1/* gtkiconview.c
2 * Copyright (C) 2002, 2004 Anders Carlsson <andersca@gnu.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include "gtkiconviewprivate.h"
21
22#include "gtkadjustmentprivate.h"
23#include "gtkcellareabox.h"
24#include "gtkcellareacontext.h"
25#include "gtkcelllayout.h"
26#include "gtkcellrenderer.h"
27#include "gtkcellrendererpixbuf.h"
28#include "gtkcellrenderertext.h"
29#include "gtkdragsourceprivate.h"
30#include "gtkentry.h"
31#include "gtkintl.h"
32#include "gtkmain.h"
33#include "gtkmarshalers.h"
34#include "gtkorientable.h"
35#include "gtkprivate.h"
36#include "gtkscrollable.h"
37#include "gtksizerequest.h"
38#include "gtksnapshot.h"
39#include "gtkstylecontextprivate.h"
40#include "gtktreednd.h"
41#include "gtktypebuiltins.h"
42#include "gtkwidgetprivate.h"
43#include "gtkwindow.h"
44#include "gtkeventcontrollerkey.h"
45#include "gtkdragsource.h"
46#include "gtkdragicon.h"
47#include "gtknative.h"
48
49#include <string.h>
50
51/**
52 * GtkIconView:
53 *
54 * `GtkIconView` is a widget which displays data in a grid of icons.
55 *
56 * `GtkIconView` provides an alternative view on a `GtkTreeModel`.
57 * It displays the model as a grid of icons with labels. Like
58 * [class@Gtk.TreeView], it allows to select one or multiple items
59 * (depending on the selection mode, see [method@Gtk.IconView.set_selection_mode]).
60 * In addition to selection with the arrow keys, `GtkIconView` supports
61 * rubberband selection, which is controlled by dragging the pointer.
62 *
63 * Note that if the tree model is backed by an actual tree store (as
64 * opposed to a flat list where the mapping to icons is obvious),
65 * `GtkIconView` will only display the first level of the tree and
66 * ignore the tree’s branches.
67 *
68 * # CSS nodes
69 *
70 * ```
71 * iconview.view
72 * ╰── [rubberband]
73 * ```
74 *
75 * `GtkIconView` has a single CSS node with name iconview and style class .view.
76 * For rubberband selection, a subnode with name rubberband is used.
77 */
78
79#define SCROLL_EDGE_SIZE 15
80
81typedef struct _GtkIconViewChild GtkIconViewChild;
82struct _GtkIconViewChild
83{
84 GtkWidget *widget;
85 GdkRectangle area;
86};
87
88/* Signals */
89enum
90{
91 ITEM_ACTIVATED,
92 SELECTION_CHANGED,
93 SELECT_ALL,
94 UNSELECT_ALL,
95 SELECT_CURSOR_ITEM,
96 TOGGLE_CURSOR_ITEM,
97 MOVE_CURSOR,
98 ACTIVATE_CURSOR_ITEM,
99 LAST_SIGNAL
100};
101
102/* Properties */
103enum
104{
105 PROP_0,
106 PROP_PIXBUF_COLUMN,
107 PROP_TEXT_COLUMN,
108 PROP_MARKUP_COLUMN,
109 PROP_SELECTION_MODE,
110 PROP_ITEM_ORIENTATION,
111 PROP_MODEL,
112 PROP_COLUMNS,
113 PROP_ITEM_WIDTH,
114 PROP_SPACING,
115 PROP_ROW_SPACING,
116 PROP_COLUMN_SPACING,
117 PROP_MARGIN,
118 PROP_REORDERABLE,
119 PROP_TOOLTIP_COLUMN,
120 PROP_ITEM_PADDING,
121 PROP_CELL_AREA,
122 PROP_HADJUSTMENT,
123 PROP_VADJUSTMENT,
124 PROP_HSCROLL_POLICY,
125 PROP_VSCROLL_POLICY,
126 PROP_ACTIVATE_ON_SINGLE_CLICK
127};
128
129/* GObject vfuncs */
130static void gtk_icon_view_cell_layout_init (GtkCellLayoutIface *iface);
131static void gtk_icon_view_dispose (GObject *object);
132static void gtk_icon_view_constructed (GObject *object);
133static void gtk_icon_view_set_property (GObject *object,
134 guint prop_id,
135 const GValue *value,
136 GParamSpec *pspec);
137static void gtk_icon_view_get_property (GObject *object,
138 guint prop_id,
139 GValue *value,
140 GParamSpec *pspec);
141/* GtkWidget vfuncs */
142static GtkSizeRequestMode gtk_icon_view_get_request_mode (GtkWidget *widget);
143static void gtk_icon_view_measure (GtkWidget *widget,
144 GtkOrientation orientation,
145 int for_size,
146 int *minimum,
147 int *natural,
148 int *minimum_baseline,
149 int *natural_baseline);
150static void gtk_icon_view_size_allocate (GtkWidget *widget,
151 int width,
152 int height,
153 int baseline);
154static void gtk_icon_view_snapshot (GtkWidget *widget,
155 GtkSnapshot *snapshot);
156static void gtk_icon_view_motion (GtkEventController *controller,
157 double x,
158 double y,
159 gpointer user_data);
160static void gtk_icon_view_leave (GtkEventController *controller,
161 gpointer user_data);
162static void gtk_icon_view_button_press (GtkGestureClick *gesture,
163 int n_press,
164 double x,
165 double y,
166 gpointer user_data);
167static void gtk_icon_view_button_release (GtkGestureClick *gesture,
168 int n_press,
169 double x,
170 double y,
171 gpointer user_data);
172static gboolean gtk_icon_view_key_pressed (GtkEventControllerKey *controller,
173 guint keyval,
174 guint keycode,
175 GdkModifierType state,
176 GtkWidget *widget);
177
178static void gtk_icon_view_remove (GtkIconView *icon_view,
179 GtkWidget *widget);
180
181/* GtkIconView vfuncs */
182static void gtk_icon_view_real_select_all (GtkIconView *icon_view);
183static void gtk_icon_view_real_unselect_all (GtkIconView *icon_view);
184static void gtk_icon_view_real_select_cursor_item (GtkIconView *icon_view);
185static void gtk_icon_view_real_toggle_cursor_item (GtkIconView *icon_view);
186static gboolean gtk_icon_view_real_activate_cursor_item (GtkIconView *icon_view);
187
188 /* Internal functions */
189static void gtk_icon_view_set_hadjustment_values (GtkIconView *icon_view);
190static void gtk_icon_view_set_vadjustment_values (GtkIconView *icon_view);
191static void gtk_icon_view_set_hadjustment (GtkIconView *icon_view,
192 GtkAdjustment *adjustment);
193static void gtk_icon_view_set_vadjustment (GtkIconView *icon_view,
194 GtkAdjustment *adjustment);
195static void gtk_icon_view_adjustment_changed (GtkAdjustment *adjustment,
196 GtkIconView *icon_view);
197static void gtk_icon_view_layout (GtkIconView *icon_view);
198static void gtk_icon_view_snapshot_item (GtkIconView *icon_view,
199 GtkSnapshot *snapshot,
200 GtkIconViewItem *item,
201 int x,
202 int y,
203 gboolean draw_focus);
204static void gtk_icon_view_snapshot_rubberband (GtkIconView *icon_view,
205 GtkSnapshot *snapshot);
206static void gtk_icon_view_queue_draw_path (GtkIconView *icon_view,
207 GtkTreePath *path);
208static void gtk_icon_view_queue_draw_item (GtkIconView *icon_view,
209 GtkIconViewItem *item);
210static void gtk_icon_view_start_rubberbanding (GtkIconView *icon_view,
211 GdkDevice *device,
212 int x,
213 int y);
214static void gtk_icon_view_stop_rubberbanding (GtkIconView *icon_view);
215static void gtk_icon_view_update_rubberband_selection (GtkIconView *icon_view);
216static gboolean gtk_icon_view_item_hit_test (GtkIconView *icon_view,
217 GtkIconViewItem *item,
218 int x,
219 int y,
220 int width,
221 int height);
222static gboolean gtk_icon_view_unselect_all_internal (GtkIconView *icon_view);
223static void gtk_icon_view_update_rubberband (GtkIconView *icon_view);
224static void gtk_icon_view_item_invalidate_size (GtkIconViewItem *item);
225static void gtk_icon_view_invalidate_sizes (GtkIconView *icon_view);
226static void gtk_icon_view_add_move_binding (GtkWidgetClass *widget_class,
227 guint keyval,
228 guint modmask,
229 GtkMovementStep step,
230 int count);
231static gboolean gtk_icon_view_real_move_cursor (GtkIconView *icon_view,
232 GtkMovementStep step,
233 int count,
234 gboolean extend,
235 gboolean modify);
236static void gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view,
237 int count);
238static void gtk_icon_view_move_cursor_page_up_down (GtkIconView *icon_view,
239 int count);
240static void gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view,
241 int count);
242static void gtk_icon_view_move_cursor_start_end (GtkIconView *icon_view,
243 int count);
244static void gtk_icon_view_scroll_to_item (GtkIconView *icon_view,
245 GtkIconViewItem *item);
246static gboolean gtk_icon_view_select_all_between (GtkIconView *icon_view,
247 GtkIconViewItem *anchor,
248 GtkIconViewItem *cursor);
249
250static void gtk_icon_view_ensure_cell_area (GtkIconView *icon_view,
251 GtkCellArea *cell_area);
252
253static GtkCellArea *gtk_icon_view_cell_layout_get_area (GtkCellLayout *layout);
254
255static void gtk_icon_view_add_editable (GtkCellArea *area,
256 GtkCellRenderer *renderer,
257 GtkCellEditable *editable,
258 GdkRectangle *cell_area,
259 const char *path,
260 GtkIconView *icon_view);
261static void gtk_icon_view_remove_editable (GtkCellArea *area,
262 GtkCellRenderer *renderer,
263 GtkCellEditable *editable,
264 GtkIconView *icon_view);
265static void update_text_cell (GtkIconView *icon_view);
266static void update_pixbuf_cell (GtkIconView *icon_view);
267
268/* Source side drag signals */
269static void gtk_icon_view_dnd_finished_cb (GdkDrag *drag,
270 GtkWidget *widget);
271static GdkContentProvider * gtk_icon_view_drag_data_get (GtkIconView *icon_view,
272 GtkTreePath *source_row);
273
274/* Target side drag signals */
275static void gtk_icon_view_drag_leave (GtkDropTargetAsync *dest,
276 GdkDrop *drop,
277 GtkIconView *icon_view);
278static GdkDragAction gtk_icon_view_drag_motion (GtkDropTargetAsync *dest,
279 GdkDrop *drop,
280 double x,
281 double y,
282 GtkIconView *icon_view);
283static gboolean gtk_icon_view_drag_drop (GtkDropTargetAsync *dest,
284 GdkDrop *drop,
285 double x,
286 double y,
287 GtkIconView *icon_view);
288static void gtk_icon_view_drag_data_received (GObject *source,
289 GAsyncResult *result,
290 gpointer data);
291static gboolean gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view,
292 double x,
293 double y,
294 GdkDevice *device);
295
296static void remove_scroll_timeout (GtkIconView *icon_view);
297
298/* GtkBuildable */
299static GtkBuildableIface *parent_buildable_iface;
300static void gtk_icon_view_buildable_init (GtkBuildableIface *iface);
301static gboolean gtk_icon_view_buildable_custom_tag_start (GtkBuildable *buildable,
302 GtkBuilder *builder,
303 GObject *child,
304 const char *tagname,
305 GtkBuildableParser *parser,
306 gpointer *data);
307static void gtk_icon_view_buildable_custom_tag_end (GtkBuildable *buildable,
308 GtkBuilder *builder,
309 GObject *child,
310 const char *tagname,
311 gpointer data);
312
313
314static guint icon_view_signals[LAST_SIGNAL] = { 0 };
315
316G_DEFINE_TYPE_WITH_CODE (GtkIconView, gtk_icon_view, GTK_TYPE_WIDGET,
317 G_ADD_PRIVATE (GtkIconView)
318 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
319 gtk_icon_view_cell_layout_init)
320 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
321 gtk_icon_view_buildable_init)
322 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
323
324static void
325gtk_icon_view_class_init (GtkIconViewClass *klass)
326{
327 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
328 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
329
330 gobject_class->constructed = gtk_icon_view_constructed;
331 gobject_class->dispose = gtk_icon_view_dispose;
332 gobject_class->set_property = gtk_icon_view_set_property;
333 gobject_class->get_property = gtk_icon_view_get_property;
334
335 widget_class->get_request_mode = gtk_icon_view_get_request_mode;
336 widget_class->measure = gtk_icon_view_measure;
337 widget_class->size_allocate = gtk_icon_view_size_allocate;
338 widget_class->snapshot = gtk_icon_view_snapshot;
339 widget_class->focus = gtk_widget_focus_self;
340 widget_class->grab_focus = gtk_widget_grab_focus_self;
341
342 klass->select_all = gtk_icon_view_real_select_all;
343 klass->unselect_all = gtk_icon_view_real_unselect_all;
344 klass->select_cursor_item = gtk_icon_view_real_select_cursor_item;
345 klass->toggle_cursor_item = gtk_icon_view_real_toggle_cursor_item;
346 klass->activate_cursor_item = gtk_icon_view_real_activate_cursor_item;
347 klass->move_cursor = gtk_icon_view_real_move_cursor;
348
349 /* Properties */
350 /**
351 * GtkIconView:selection-mode:
352 *
353 * The ::selection-mode property specifies the selection mode of
354 * icon view. If the mode is %GTK_SELECTION_MULTIPLE, rubberband selection
355 * is enabled, for the other modes, only keyboard selection is possible.
356 */
357 g_object_class_install_property (oclass: gobject_class,
358 property_id: PROP_SELECTION_MODE,
359 pspec: g_param_spec_enum (name: "selection-mode",
360 P_("Selection mode"),
361 P_("The selection mode"),
362 enum_type: GTK_TYPE_SELECTION_MODE,
363 default_value: GTK_SELECTION_SINGLE,
364 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
365
366 /**
367 * GtkIconView:pixbuf-column:
368 *
369 * The ::pixbuf-column property contains the number of the model column
370 * containing the pixbufs which are displayed. The pixbuf column must be
371 * of type `GDK_TYPE_PIXBUF`. Setting this property to -1 turns off the
372 * display of pixbufs.
373 */
374 g_object_class_install_property (oclass: gobject_class,
375 property_id: PROP_PIXBUF_COLUMN,
376 pspec: g_param_spec_int (name: "pixbuf-column",
377 P_("Pixbuf column"),
378 P_("Model column used to retrieve the icon pixbuf from"),
379 minimum: -1, G_MAXINT, default_value: -1,
380 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
381
382 /**
383 * GtkIconView:text-column:
384 *
385 * The ::text-column property contains the number of the model column
386 * containing the texts which are displayed. The text column must be
387 * of type `G_TYPE_STRING`. If this property and the :markup-column
388 * property are both set to -1, no texts are displayed.
389 */
390 g_object_class_install_property (oclass: gobject_class,
391 property_id: PROP_TEXT_COLUMN,
392 pspec: g_param_spec_int (name: "text-column",
393 P_("Text column"),
394 P_("Model column used to retrieve the text from"),
395 minimum: -1, G_MAXINT, default_value: -1,
396 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
397
398
399 /**
400 * GtkIconView:markup-column:
401 *
402 * The ::markup-column property contains the number of the model column
403 * containing markup information to be displayed. The markup column must be
404 * of type `G_TYPE_STRING`. If this property and the :text-column property
405 * are both set to column numbers, it overrides the text column.
406 * If both are set to -1, no texts are displayed.
407 */
408 g_object_class_install_property (oclass: gobject_class,
409 property_id: PROP_MARKUP_COLUMN,
410 pspec: g_param_spec_int (name: "markup-column",
411 P_("Markup column"),
412 P_("Model column used to retrieve the text if using Pango markup"),
413 minimum: -1, G_MAXINT, default_value: -1,
414 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
415
416 g_object_class_install_property (oclass: gobject_class,
417 property_id: PROP_MODEL,
418 pspec: g_param_spec_object (name: "model",
419 P_("Icon View Model"),
420 P_("The model for the icon view"),
421 GTK_TYPE_TREE_MODEL,
422 GTK_PARAM_READWRITE));
423
424 /**
425 * GtkIconView:columns:
426 *
427 * The columns property contains the number of the columns in which the
428 * items should be displayed. If it is -1, the number of columns will
429 * be chosen automatically to fill the available area.
430 */
431 g_object_class_install_property (oclass: gobject_class,
432 property_id: PROP_COLUMNS,
433 pspec: g_param_spec_int (name: "columns",
434 P_("Number of columns"),
435 P_("Number of columns to display"),
436 minimum: -1, G_MAXINT, default_value: -1,
437 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
438
439
440 /**
441 * GtkIconView:item-width:
442 *
443 * The item-width property specifies the width to use for each item.
444 * If it is set to -1, the icon view will automatically determine a
445 * suitable item size.
446 */
447 g_object_class_install_property (oclass: gobject_class,
448 property_id: PROP_ITEM_WIDTH,
449 pspec: g_param_spec_int (name: "item-width",
450 P_("Width for each item"),
451 P_("The width used for each item"),
452 minimum: -1, G_MAXINT, default_value: -1,
453 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
454
455 /**
456 * GtkIconView:spacing:
457 *
458 * The spacing property specifies the space which is inserted between
459 * the cells (i.e. the icon and the text) of an item.
460 */
461 g_object_class_install_property (oclass: gobject_class,
462 property_id: PROP_SPACING,
463 pspec: g_param_spec_int (name: "spacing",
464 P_("Spacing"),
465 P_("Space which is inserted between cells of an item"),
466 minimum: 0, G_MAXINT, default_value: 0,
467 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
468
469 /**
470 * GtkIconView:row-spacing:
471 *
472 * The row-spacing property specifies the space which is inserted between
473 * the rows of the icon view.
474 */
475 g_object_class_install_property (oclass: gobject_class,
476 property_id: PROP_ROW_SPACING,
477 pspec: g_param_spec_int (name: "row-spacing",
478 P_("Row Spacing"),
479 P_("Space which is inserted between grid rows"),
480 minimum: 0, G_MAXINT, default_value: 6,
481 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
482
483 /**
484 * GtkIconView:column-spacing:
485 *
486 * The column-spacing property specifies the space which is inserted between
487 * the columns of the icon view.
488 */
489 g_object_class_install_property (oclass: gobject_class,
490 property_id: PROP_COLUMN_SPACING,
491 pspec: g_param_spec_int (name: "column-spacing",
492 P_("Column Spacing"),
493 P_("Space which is inserted between grid columns"),
494 minimum: 0, G_MAXINT, default_value: 6,
495 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
496
497 /**
498 * GtkIconView:margin:
499 *
500 * The margin property specifies the space which is inserted
501 * at the edges of the icon view.
502 */
503 g_object_class_install_property (oclass: gobject_class,
504 property_id: PROP_MARGIN,
505 pspec: g_param_spec_int (name: "margin",
506 P_("Margin"),
507 P_("Space which is inserted at the edges of the icon view"),
508 minimum: 0, G_MAXINT, default_value: 6,
509 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
510
511 /**
512 * GtkIconView:item-orientation:
513 *
514 * The item-orientation property specifies how the cells (i.e. the icon and
515 * the text) of the item are positioned relative to each other.
516 */
517 g_object_class_install_property (oclass: gobject_class,
518 property_id: PROP_ITEM_ORIENTATION,
519 pspec: g_param_spec_enum (name: "item-orientation",
520 P_("Item Orientation"),
521 P_("How the text and icon of each item are positioned relative to each other"),
522 enum_type: GTK_TYPE_ORIENTATION,
523 default_value: GTK_ORIENTATION_VERTICAL,
524 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
525
526 /**
527 * GtkIconView:reorderable:
528 *
529 * The reorderable property specifies if the items can be reordered
530 * by DND.
531 */
532 g_object_class_install_property (oclass: gobject_class,
533 property_id: PROP_REORDERABLE,
534 pspec: g_param_spec_boolean (name: "reorderable",
535 P_("Reorderable"),
536 P_("View is reorderable"),
537 FALSE,
538 flags: G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
539
540 g_object_class_install_property (oclass: gobject_class,
541 property_id: PROP_TOOLTIP_COLUMN,
542 pspec: g_param_spec_int (name: "tooltip-column",
543 P_("Tooltip Column"),
544 P_("The column in the model containing the tooltip texts for the items"),
545 minimum: -1,
546 G_MAXINT,
547 default_value: -1,
548 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
549
550 /**
551 * GtkIconView:item-padding:
552 *
553 * The item-padding property specifies the padding around each
554 * of the icon view's item.
555 */
556 g_object_class_install_property (oclass: gobject_class,
557 property_id: PROP_ITEM_PADDING,
558 pspec: g_param_spec_int (name: "item-padding",
559 P_("Item Padding"),
560 P_("Padding around icon view items"),
561 minimum: 0, G_MAXINT, default_value: 6,
562 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
563
564 /**
565 * GtkIconView:cell-area:
566 *
567 * The `GtkCellArea` used to layout cell renderers for this view.
568 *
569 * If no area is specified when creating the icon view with gtk_icon_view_new_with_area()
570 * a `GtkCellAreaBox` will be used.
571 */
572 g_object_class_install_property (oclass: gobject_class,
573 property_id: PROP_CELL_AREA,
574 pspec: g_param_spec_object (name: "cell-area",
575 P_("Cell Area"),
576 P_("The GtkCellArea used to layout cells"),
577 GTK_TYPE_CELL_AREA,
578 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
579
580 /**
581 * GtkIconView:activate-on-single-click:
582 *
583 * The activate-on-single-click property specifies whether the "item-activated" signal
584 * will be emitted after a single click.
585 */
586 g_object_class_install_property (oclass: gobject_class,
587 property_id: PROP_ACTIVATE_ON_SINGLE_CLICK,
588 pspec: g_param_spec_boolean (name: "activate-on-single-click",
589 P_("Activate on Single Click"),
590 P_("Activate row on a single click"),
591 FALSE,
592 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
593
594 /* Scrollable interface properties */
595 g_object_class_override_property (oclass: gobject_class, property_id: PROP_HADJUSTMENT, name: "hadjustment");
596 g_object_class_override_property (oclass: gobject_class, property_id: PROP_VADJUSTMENT, name: "vadjustment");
597 g_object_class_override_property (oclass: gobject_class, property_id: PROP_HSCROLL_POLICY, name: "hscroll-policy");
598 g_object_class_override_property (oclass: gobject_class, property_id: PROP_VSCROLL_POLICY, name: "vscroll-policy");
599
600 /* Signals */
601 /**
602 * GtkIconView::item-activated:
603 * @iconview: the object on which the signal is emitted
604 * @path: the `GtkTreePath` for the activated item
605 *
606 * The ::item-activated signal is emitted when the method
607 * gtk_icon_view_item_activated() is called, when the user double
608 * clicks an item with the "activate-on-single-click" property set
609 * to %FALSE, or when the user single clicks an item when the
610 * "activate-on-single-click" property set to %TRUE. It is also
611 * emitted when a non-editable item is selected and one of the keys:
612 * Space, Return or Enter is pressed.
613 */
614 icon_view_signals[ITEM_ACTIVATED] =
615 g_signal_new (I_("item-activated"),
616 G_TYPE_FROM_CLASS (gobject_class),
617 signal_flags: G_SIGNAL_RUN_LAST,
618 G_STRUCT_OFFSET (GtkIconViewClass, item_activated),
619 NULL, NULL,
620 NULL,
621 G_TYPE_NONE, n_params: 1,
622 GTK_TYPE_TREE_PATH);
623
624 /**
625 * GtkIconView::selection-changed:
626 * @iconview: the object on which the signal is emitted
627 *
628 * The ::selection-changed signal is emitted when the selection
629 * (i.e. the set of selected items) changes.
630 */
631 icon_view_signals[SELECTION_CHANGED] =
632 g_signal_new (I_("selection-changed"),
633 G_TYPE_FROM_CLASS (gobject_class),
634 signal_flags: G_SIGNAL_RUN_FIRST,
635 G_STRUCT_OFFSET (GtkIconViewClass, selection_changed),
636 NULL, NULL,
637 NULL,
638 G_TYPE_NONE, n_params: 0);
639
640 /**
641 * GtkIconView::select-all:
642 * @iconview: the object on which the signal is emitted
643 *
644 * A [keybinding signal][class@Gtk.SignalAction]
645 * which gets emitted when the user selects all items.
646 *
647 * Applications should not connect to it, but may emit it with
648 * g_signal_emit_by_name() if they need to control selection
649 * programmatically.
650 *
651 * The default binding for this signal is Ctrl-a.
652 */
653 icon_view_signals[SELECT_ALL] =
654 g_signal_new (I_("select-all"),
655 G_TYPE_FROM_CLASS (gobject_class),
656 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
657 G_STRUCT_OFFSET (GtkIconViewClass, select_all),
658 NULL, NULL,
659 NULL,
660 G_TYPE_NONE, n_params: 0);
661
662 /**
663 * GtkIconView::unselect-all:
664 * @iconview: the object on which the signal is emitted
665 *
666 * A [keybinding signal][class@Gtk.SignalAction]
667 * which gets emitted when the user unselects all items.
668 *
669 * Applications should not connect to it, but may emit it with
670 * g_signal_emit_by_name() if they need to control selection
671 * programmatically.
672 *
673 * The default binding for this signal is Ctrl-Shift-a.
674 */
675 icon_view_signals[UNSELECT_ALL] =
676 g_signal_new (I_("unselect-all"),
677 G_TYPE_FROM_CLASS (gobject_class),
678 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
679 G_STRUCT_OFFSET (GtkIconViewClass, unselect_all),
680 NULL, NULL,
681 NULL,
682 G_TYPE_NONE, n_params: 0);
683
684 /**
685 * GtkIconView::select-cursor-item:
686 * @iconview: the object on which the signal is emitted
687 *
688 * A [keybinding signal][class@Gtk.SignalAction]
689 * which gets emitted when the user selects the item that is currently
690 * focused.
691 *
692 * Applications should not connect to it, but may emit it with
693 * g_signal_emit_by_name() if they need to control selection
694 * programmatically.
695 *
696 * There is no default binding for this signal.
697 */
698 icon_view_signals[SELECT_CURSOR_ITEM] =
699 g_signal_new (I_("select-cursor-item"),
700 G_TYPE_FROM_CLASS (gobject_class),
701 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
702 G_STRUCT_OFFSET (GtkIconViewClass, select_cursor_item),
703 NULL, NULL,
704 NULL,
705 G_TYPE_NONE, n_params: 0);
706
707 /**
708 * GtkIconView::toggle-cursor-item:
709 * @iconview: the object on which the signal is emitted
710 *
711 * A [keybinding signal][class@Gtk.SignalAction]
712 * which gets emitted when the user toggles whether the currently
713 * focused item is selected or not. The exact effect of this
714 * depend on the selection mode.
715 *
716 * Applications should not connect to it, but may emit it with
717 * g_signal_emit_by_name() if they need to control selection
718 * programmatically.
719 *
720 * There is no default binding for this signal is Ctrl-Space.
721 */
722 icon_view_signals[TOGGLE_CURSOR_ITEM] =
723 g_signal_new (I_("toggle-cursor-item"),
724 G_TYPE_FROM_CLASS (gobject_class),
725 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
726 G_STRUCT_OFFSET (GtkIconViewClass, toggle_cursor_item),
727 NULL, NULL,
728 NULL,
729 G_TYPE_NONE, n_params: 0);
730
731 /**
732 * GtkIconView::activate-cursor-item:
733 * @iconview: the object on which the signal is emitted
734 *
735 * A [keybinding signal][class@Gtk.SignalAction]
736 * which gets emitted when the user activates the currently
737 * focused item.
738 *
739 * Applications should not connect to it, but may emit it with
740 * g_signal_emit_by_name() if they need to control activation
741 * programmatically.
742 *
743 * The default bindings for this signal are Space, Return and Enter.
744 */
745 icon_view_signals[ACTIVATE_CURSOR_ITEM] =
746 g_signal_new (I_("activate-cursor-item"),
747 G_TYPE_FROM_CLASS (gobject_class),
748 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
749 G_STRUCT_OFFSET (GtkIconViewClass, activate_cursor_item),
750 NULL, NULL,
751 c_marshaller: _gtk_marshal_BOOLEAN__VOID,
752 G_TYPE_BOOLEAN, n_params: 0);
753 g_signal_set_va_marshaller (signal_id: icon_view_signals[ACTIVATE_CURSOR_ITEM],
754 G_TYPE_FROM_CLASS (klass),
755 va_marshaller: _gtk_marshal_BOOLEAN__VOIDv);
756
757 /**
758 * GtkIconView::move-cursor:
759 * @iconview: the object which received the signal
760 * @step: the granularity of the move, as a `GtkMovementStep`
761 * @count: the number of @step units to move
762 * @extend: whether to extend the selection
763 * @modify: whether to modify the selection
764 *
765 * The ::move-cursor signal is a
766 * [keybinding signal][class@Gtk.SignalAction]
767 * which gets emitted when the user initiates a cursor movement.
768 *
769 * Applications should not connect to it, but may emit it with
770 * g_signal_emit_by_name() if they need to control the cursor
771 * programmatically.
772 *
773 * The default bindings for this signal include
774 * - Arrow keys which move by individual steps
775 * - Home/End keys which move to the first/last item
776 * - PageUp/PageDown which move by "pages"
777 * All of these will extend the selection when combined with
778 * the Shift modifier.
779 */
780 icon_view_signals[MOVE_CURSOR] =
781 g_signal_new (I_("move-cursor"),
782 G_TYPE_FROM_CLASS (gobject_class),
783 signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
784 G_STRUCT_OFFSET (GtkIconViewClass, move_cursor),
785 NULL, NULL,
786 c_marshaller: _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEAN,
787 G_TYPE_BOOLEAN, n_params: 4,
788 GTK_TYPE_MOVEMENT_STEP,
789 G_TYPE_INT,
790 G_TYPE_BOOLEAN,
791 G_TYPE_BOOLEAN);
792 g_signal_set_va_marshaller (signal_id: icon_view_signals[MOVE_CURSOR],
793 G_TYPE_FROM_CLASS (klass),
794 va_marshaller: _gtk_marshal_BOOLEAN__ENUM_INT_BOOLEAN_BOOLEANv);
795
796 /* Key bindings */
797 gtk_widget_class_add_binding_signal (widget_class,
798 GDK_KEY_a, mods: GDK_CONTROL_MASK,
799 signal: "select-all",
800 NULL);
801 gtk_widget_class_add_binding_signal (widget_class,
802 GDK_KEY_a, mods: GDK_CONTROL_MASK | GDK_SHIFT_MASK,
803 signal: "unselect-all",
804 NULL);
805 gtk_widget_class_add_binding_signal (widget_class,
806 GDK_KEY_space, mods: GDK_CONTROL_MASK,
807 signal: "toggle-cursor-item",
808 NULL);
809 gtk_widget_class_add_binding_signal (widget_class,
810 GDK_KEY_KP_Space, mods: GDK_CONTROL_MASK,
811 signal: "toggle-cursor-item",
812 NULL);
813
814 gtk_widget_class_add_binding_signal (widget_class,
815 GDK_KEY_space, mods: 0,
816 signal: "activate-cursor-item",
817 NULL);
818 gtk_widget_class_add_binding_signal (widget_class,
819 GDK_KEY_KP_Space, mods: 0,
820 signal: "activate-cursor-item",
821 NULL);
822 gtk_widget_class_add_binding_signal (widget_class,
823 GDK_KEY_Return, mods: 0,
824 signal: "activate-cursor-item",
825 NULL);
826 gtk_widget_class_add_binding_signal (widget_class,
827 GDK_KEY_ISO_Enter, mods: 0,
828 signal: "activate-cursor-item",
829 NULL);
830 gtk_widget_class_add_binding_signal (widget_class,
831 GDK_KEY_KP_Enter, mods: 0,
832 signal: "activate-cursor-item",
833 NULL);
834
835 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Up, modmask: 0,
836 step: GTK_MOVEMENT_DISPLAY_LINES, count: -1);
837 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Up, modmask: 0,
838 step: GTK_MOVEMENT_DISPLAY_LINES, count: -1);
839
840 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Down, modmask: 0,
841 step: GTK_MOVEMENT_DISPLAY_LINES, count: 1);
842 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Down, modmask: 0,
843 step: GTK_MOVEMENT_DISPLAY_LINES, count: 1);
844
845 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_p, modmask: GDK_CONTROL_MASK,
846 step: GTK_MOVEMENT_DISPLAY_LINES, count: -1);
847
848 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_n, modmask: GDK_CONTROL_MASK,
849 step: GTK_MOVEMENT_DISPLAY_LINES, count: 1);
850
851 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Home, modmask: 0,
852 step: GTK_MOVEMENT_BUFFER_ENDS, count: -1);
853 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Home, modmask: 0,
854 step: GTK_MOVEMENT_BUFFER_ENDS, count: -1);
855
856 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_End, modmask: 0,
857 step: GTK_MOVEMENT_BUFFER_ENDS, count: 1);
858 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_End, modmask: 0,
859 step: GTK_MOVEMENT_BUFFER_ENDS, count: 1);
860
861 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Page_Up, modmask: 0,
862 step: GTK_MOVEMENT_PAGES, count: -1);
863 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Up, modmask: 0,
864 step: GTK_MOVEMENT_PAGES, count: -1);
865
866 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Page_Down, modmask: 0,
867 step: GTK_MOVEMENT_PAGES, count: 1);
868 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Page_Down, modmask: 0,
869 step: GTK_MOVEMENT_PAGES, count: 1);
870
871 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Right, modmask: 0,
872 step: GTK_MOVEMENT_VISUAL_POSITIONS, count: 1);
873 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_Left, modmask: 0,
874 step: GTK_MOVEMENT_VISUAL_POSITIONS, count: -1);
875
876 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Right, modmask: 0,
877 step: GTK_MOVEMENT_VISUAL_POSITIONS, count: 1);
878 gtk_icon_view_add_move_binding (widget_class, GDK_KEY_KP_Left, modmask: 0,
879 step: GTK_MOVEMENT_VISUAL_POSITIONS, count: -1);
880
881 gtk_widget_class_set_css_name (widget_class, I_("iconview"));
882}
883
884static void
885gtk_icon_view_buildable_add_child (GtkBuildable *buildable,
886 GtkBuilder *builder,
887 GObject *child,
888 const char *type)
889{
890 if (GTK_IS_CELL_RENDERER (child))
891 _gtk_cell_layout_buildable_add_child (buildable, builder, child, type);
892 else
893 parent_buildable_iface->add_child (buildable, builder, child, type);
894}
895
896static void
897gtk_icon_view_buildable_init (GtkBuildableIface *iface)
898{
899 parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface);
900 iface->add_child = gtk_icon_view_buildable_add_child;
901 iface->custom_tag_start = gtk_icon_view_buildable_custom_tag_start;
902 iface->custom_tag_end = gtk_icon_view_buildable_custom_tag_end;
903}
904
905static void
906gtk_icon_view_cell_layout_init (GtkCellLayoutIface *iface)
907{
908 iface->get_area = gtk_icon_view_cell_layout_get_area;
909}
910
911static void
912gtk_icon_view_init (GtkIconView *icon_view)
913{
914 GtkEventController *controller;
915 GtkGesture *gesture;
916
917 icon_view->priv = gtk_icon_view_get_instance_private (self: icon_view);
918
919 icon_view->priv->width = 0;
920 icon_view->priv->height = 0;
921 icon_view->priv->selection_mode = GTK_SELECTION_SINGLE;
922 icon_view->priv->pressed_button = -1;
923 icon_view->priv->press_start_x = -1;
924 icon_view->priv->press_start_y = -1;
925 icon_view->priv->text_column = -1;
926 icon_view->priv->markup_column = -1;
927 icon_view->priv->pixbuf_column = -1;
928 icon_view->priv->text_cell = NULL;
929 icon_view->priv->pixbuf_cell = NULL;
930 icon_view->priv->tooltip_column = -1;
931 icon_view->priv->mouse_x = -1;
932 icon_view->priv->mouse_y = -1;
933
934 gtk_widget_set_overflow (GTK_WIDGET (icon_view), overflow: GTK_OVERFLOW_HIDDEN);
935 gtk_widget_set_focusable (GTK_WIDGET (icon_view), TRUE);
936
937 icon_view->priv->item_orientation = GTK_ORIENTATION_VERTICAL;
938
939 icon_view->priv->columns = -1;
940 icon_view->priv->item_width = -1;
941 icon_view->priv->spacing = 0;
942 icon_view->priv->row_spacing = 6;
943 icon_view->priv->column_spacing = 6;
944 icon_view->priv->margin = 6;
945 icon_view->priv->item_padding = 6;
946 icon_view->priv->activate_on_single_click = FALSE;
947
948 icon_view->priv->draw_focus = TRUE;
949
950 icon_view->priv->row_contexts =
951 g_ptr_array_new_with_free_func (element_free_func: (GDestroyNotify)g_object_unref);
952
953 gtk_widget_add_css_class (GTK_WIDGET (icon_view), css_class: "view");
954
955 gesture = gtk_gesture_click_new ();
956 g_signal_connect (gesture, "pressed", G_CALLBACK (gtk_icon_view_button_press),
957 icon_view);
958 g_signal_connect (gesture, "released", G_CALLBACK (gtk_icon_view_button_release),
959 icon_view);
960 gtk_widget_add_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (gesture));
961
962 controller = gtk_event_controller_motion_new ();
963 g_signal_connect (controller, "leave", G_CALLBACK (gtk_icon_view_leave), icon_view);
964 g_signal_connect (controller, "motion", G_CALLBACK (gtk_icon_view_motion), icon_view);
965 gtk_widget_add_controller (GTK_WIDGET (icon_view), controller);
966
967 controller = gtk_event_controller_key_new ();
968 g_signal_connect (controller, "key-pressed", G_CALLBACK (gtk_icon_view_key_pressed),
969 icon_view);
970 gtk_widget_add_controller (GTK_WIDGET (icon_view), controller);
971}
972
973/* GObject methods */
974
975static void
976gtk_icon_view_constructed (GObject *object)
977{
978 GtkIconView *icon_view = GTK_ICON_VIEW (object);
979
980 G_OBJECT_CLASS (gtk_icon_view_parent_class)->constructed (object);
981
982 gtk_icon_view_ensure_cell_area (icon_view, NULL);
983}
984
985static void
986gtk_icon_view_dispose (GObject *object)
987{
988 GtkIconView *icon_view;
989 GtkIconViewPrivate *priv;
990
991 icon_view = GTK_ICON_VIEW (object);
992 priv = icon_view->priv;
993
994 gtk_icon_view_set_model (icon_view, NULL);
995
996 if (icon_view->priv->scroll_to_path != NULL)
997 {
998 gtk_tree_row_reference_free (reference: icon_view->priv->scroll_to_path);
999 icon_view->priv->scroll_to_path = NULL;
1000 }
1001
1002 remove_scroll_timeout (icon_view);
1003
1004 if (icon_view->priv->hadjustment != NULL)
1005 {
1006 g_object_unref (object: icon_view->priv->hadjustment);
1007 icon_view->priv->hadjustment = NULL;
1008 }
1009
1010 if (icon_view->priv->vadjustment != NULL)
1011 {
1012 g_object_unref (object: icon_view->priv->vadjustment);
1013 icon_view->priv->vadjustment = NULL;
1014 }
1015
1016 if (priv->cell_area_context)
1017 {
1018 g_object_unref (object: priv->cell_area_context);
1019 priv->cell_area_context = NULL;
1020 }
1021
1022 if (priv->row_contexts)
1023 {
1024 g_ptr_array_free (array: priv->row_contexts, TRUE);
1025 priv->row_contexts = NULL;
1026 }
1027
1028 if (priv->cell_area)
1029 {
1030 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
1031
1032 g_signal_handler_disconnect (instance: priv->cell_area, handler_id: priv->add_editable_id);
1033 g_signal_handler_disconnect (instance: priv->cell_area, handler_id: priv->remove_editable_id);
1034 priv->add_editable_id = 0;
1035 priv->remove_editable_id = 0;
1036
1037 g_object_unref (object: priv->cell_area);
1038 priv->cell_area = NULL;
1039 }
1040
1041 g_clear_object (&priv->key_controller);
1042
1043 g_clear_pointer (&priv->source_formats, gdk_content_formats_unref);
1044
1045 G_OBJECT_CLASS (gtk_icon_view_parent_class)->dispose (object);
1046}
1047
1048static void
1049gtk_icon_view_set_property (GObject *object,
1050 guint prop_id,
1051 const GValue *value,
1052 GParamSpec *pspec)
1053{
1054 GtkIconView *icon_view;
1055 GtkCellArea *area;
1056
1057 icon_view = GTK_ICON_VIEW (object);
1058
1059 switch (prop_id)
1060 {
1061 case PROP_SELECTION_MODE:
1062 gtk_icon_view_set_selection_mode (icon_view, mode: g_value_get_enum (value));
1063 break;
1064 case PROP_PIXBUF_COLUMN:
1065 gtk_icon_view_set_pixbuf_column (icon_view, column: g_value_get_int (value));
1066 break;
1067 case PROP_TEXT_COLUMN:
1068 gtk_icon_view_set_text_column (icon_view, column: g_value_get_int (value));
1069 break;
1070 case PROP_MARKUP_COLUMN:
1071 gtk_icon_view_set_markup_column (icon_view, column: g_value_get_int (value));
1072 break;
1073 case PROP_MODEL:
1074 gtk_icon_view_set_model (icon_view, model: g_value_get_object (value));
1075 break;
1076 case PROP_ITEM_ORIENTATION:
1077 gtk_icon_view_set_item_orientation (icon_view, orientation: g_value_get_enum (value));
1078 break;
1079 case PROP_COLUMNS:
1080 gtk_icon_view_set_columns (icon_view, columns: g_value_get_int (value));
1081 break;
1082 case PROP_ITEM_WIDTH:
1083 gtk_icon_view_set_item_width (icon_view, item_width: g_value_get_int (value));
1084 break;
1085 case PROP_SPACING:
1086 gtk_icon_view_set_spacing (icon_view, spacing: g_value_get_int (value));
1087 break;
1088 case PROP_ROW_SPACING:
1089 gtk_icon_view_set_row_spacing (icon_view, row_spacing: g_value_get_int (value));
1090 break;
1091 case PROP_COLUMN_SPACING:
1092 gtk_icon_view_set_column_spacing (icon_view, column_spacing: g_value_get_int (value));
1093 break;
1094 case PROP_MARGIN:
1095 gtk_icon_view_set_margin (icon_view, margin: g_value_get_int (value));
1096 break;
1097 case PROP_REORDERABLE:
1098 gtk_icon_view_set_reorderable (icon_view, reorderable: g_value_get_boolean (value));
1099 break;
1100
1101 case PROP_TOOLTIP_COLUMN:
1102 gtk_icon_view_set_tooltip_column (icon_view, column: g_value_get_int (value));
1103 break;
1104
1105 case PROP_ITEM_PADDING:
1106 gtk_icon_view_set_item_padding (icon_view, item_padding: g_value_get_int (value));
1107 break;
1108
1109 case PROP_ACTIVATE_ON_SINGLE_CLICK:
1110 gtk_icon_view_set_activate_on_single_click (icon_view, single: g_value_get_boolean (value));
1111 break;
1112
1113 case PROP_CELL_AREA:
1114 /* Construct-only, can only be assigned once */
1115 area = g_value_get_object (value);
1116 if (area)
1117 {
1118 if (icon_view->priv->cell_area != NULL)
1119 {
1120 g_warning ("cell-area has already been set, ignoring construct property");
1121 g_object_ref_sink (area);
1122 g_object_unref (object: area);
1123 }
1124 else
1125 gtk_icon_view_ensure_cell_area (icon_view, cell_area: area);
1126 }
1127 break;
1128
1129 case PROP_HADJUSTMENT:
1130 gtk_icon_view_set_hadjustment (icon_view, adjustment: g_value_get_object (value));
1131 break;
1132 case PROP_VADJUSTMENT:
1133 gtk_icon_view_set_vadjustment (icon_view, adjustment: g_value_get_object (value));
1134 break;
1135 case PROP_HSCROLL_POLICY:
1136 if (icon_view->priv->hscroll_policy != g_value_get_enum (value))
1137 {
1138 icon_view->priv->hscroll_policy = g_value_get_enum (value);
1139 gtk_widget_queue_resize (GTK_WIDGET (icon_view));
1140 g_object_notify_by_pspec (object, pspec);
1141 }
1142 break;
1143 case PROP_VSCROLL_POLICY:
1144 if (icon_view->priv->vscroll_policy != g_value_get_enum (value))
1145 {
1146 icon_view->priv->vscroll_policy = g_value_get_enum (value);
1147 gtk_widget_queue_resize (GTK_WIDGET (icon_view));
1148 g_object_notify_by_pspec (object, pspec);
1149 }
1150 break;
1151
1152 default:
1153 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1154 break;
1155 }
1156}
1157
1158static void
1159gtk_icon_view_get_property (GObject *object,
1160 guint prop_id,
1161 GValue *value,
1162 GParamSpec *pspec)
1163{
1164 GtkIconView *icon_view;
1165
1166 icon_view = GTK_ICON_VIEW (object);
1167
1168 switch (prop_id)
1169 {
1170 case PROP_SELECTION_MODE:
1171 g_value_set_enum (value, v_enum: icon_view->priv->selection_mode);
1172 break;
1173 case PROP_PIXBUF_COLUMN:
1174 g_value_set_int (value, v_int: icon_view->priv->pixbuf_column);
1175 break;
1176 case PROP_TEXT_COLUMN:
1177 g_value_set_int (value, v_int: icon_view->priv->text_column);
1178 break;
1179 case PROP_MARKUP_COLUMN:
1180 g_value_set_int (value, v_int: icon_view->priv->markup_column);
1181 break;
1182 case PROP_MODEL:
1183 g_value_set_object (value, v_object: icon_view->priv->model);
1184 break;
1185 case PROP_ITEM_ORIENTATION:
1186 g_value_set_enum (value, v_enum: icon_view->priv->item_orientation);
1187 break;
1188 case PROP_COLUMNS:
1189 g_value_set_int (value, v_int: icon_view->priv->columns);
1190 break;
1191 case PROP_ITEM_WIDTH:
1192 g_value_set_int (value, v_int: icon_view->priv->item_width);
1193 break;
1194 case PROP_SPACING:
1195 g_value_set_int (value, v_int: icon_view->priv->spacing);
1196 break;
1197 case PROP_ROW_SPACING:
1198 g_value_set_int (value, v_int: icon_view->priv->row_spacing);
1199 break;
1200 case PROP_COLUMN_SPACING:
1201 g_value_set_int (value, v_int: icon_view->priv->column_spacing);
1202 break;
1203 case PROP_MARGIN:
1204 g_value_set_int (value, v_int: icon_view->priv->margin);
1205 break;
1206 case PROP_REORDERABLE:
1207 g_value_set_boolean (value, v_boolean: icon_view->priv->reorderable);
1208 break;
1209 case PROP_TOOLTIP_COLUMN:
1210 g_value_set_int (value, v_int: icon_view->priv->tooltip_column);
1211 break;
1212
1213 case PROP_ITEM_PADDING:
1214 g_value_set_int (value, v_int: icon_view->priv->item_padding);
1215 break;
1216
1217 case PROP_ACTIVATE_ON_SINGLE_CLICK:
1218 g_value_set_boolean (value, v_boolean: icon_view->priv->activate_on_single_click);
1219 break;
1220
1221 case PROP_CELL_AREA:
1222 g_value_set_object (value, v_object: icon_view->priv->cell_area);
1223 break;
1224
1225 case PROP_HADJUSTMENT:
1226 g_value_set_object (value, v_object: icon_view->priv->hadjustment);
1227 break;
1228 case PROP_VADJUSTMENT:
1229 g_value_set_object (value, v_object: icon_view->priv->vadjustment);
1230 break;
1231 case PROP_HSCROLL_POLICY:
1232 g_value_set_enum (value, v_enum: icon_view->priv->hscroll_policy);
1233 break;
1234 case PROP_VSCROLL_POLICY:
1235 g_value_set_enum (value, v_enum: icon_view->priv->vscroll_policy);
1236 break;
1237
1238 default:
1239 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1240 break;
1241 }
1242}
1243
1244/* GtkWidget methods */
1245
1246static int
1247gtk_icon_view_get_n_items (GtkIconView *icon_view)
1248{
1249 GtkIconViewPrivate *priv = icon_view->priv;
1250
1251 if (priv->model == NULL)
1252 return 0;
1253
1254 return gtk_tree_model_iter_n_children (tree_model: priv->model, NULL);
1255}
1256
1257static void
1258adjust_wrap_width (GtkIconView *icon_view)
1259{
1260 if (icon_view->priv->text_cell)
1261 {
1262 int pixbuf_width, wrap_width;
1263
1264 if (icon_view->priv->items && icon_view->priv->pixbuf_cell)
1265 {
1266 gtk_cell_renderer_get_preferred_width (cell: icon_view->priv->pixbuf_cell,
1267 GTK_WIDGET (icon_view),
1268 minimum_size: &pixbuf_width, NULL);
1269 }
1270 else
1271 {
1272 pixbuf_width = 0;
1273 }
1274
1275 if (icon_view->priv->item_width >= 0)
1276 {
1277 if (icon_view->priv->item_orientation == GTK_ORIENTATION_VERTICAL)
1278 {
1279 wrap_width = icon_view->priv->item_width;
1280 }
1281 else
1282 {
1283 wrap_width = icon_view->priv->item_width - pixbuf_width;
1284 }
1285
1286 wrap_width -= 2 * icon_view->priv->item_padding * 2;
1287 }
1288 else
1289 {
1290 wrap_width = MAX (pixbuf_width * 2, 50);
1291 }
1292
1293 if (icon_view->priv->items && icon_view->priv->pixbuf_cell)
1294 {
1295 /* Here we go with the same old guess, try the icon size and set double
1296 * the size of the first icon found in the list, naive but works much
1297 * of the time */
1298
1299 wrap_width = MAX (wrap_width * 2, 50);
1300 }
1301
1302 g_object_set (object: icon_view->priv->text_cell, first_property_name: "wrap-width", wrap_width, NULL);
1303 g_object_set (object: icon_view->priv->text_cell, first_property_name: "width", wrap_width, NULL);
1304 }
1305}
1306
1307/* General notes about layout
1308 *
1309 * The icon view is layouted like this:
1310 *
1311 * +----------+ s +----------+
1312 * | padding | p | padding |
1313 * | +------+ | a | +------+ |
1314 * | | cell | | c | | cell | |
1315 * | +------+ | i | +------+ |
1316 * | | n | |
1317 * +----------+ g +----------+
1318 *
1319 * In size request and allocation code, there are 3 sizes that are used:
1320 * * cell size
1321 * This is the size returned by gtk_cell_area_get_preferred_foo(). In places
1322 * where code is interacting with the cell area and renderers this is useful.
1323 * * padded size
1324 * This is the cell size plus the item padding on each side.
1325 * * spaced size
1326 * This is the padded size plus the spacing. This is what’s used for most
1327 * calculations because it can (ab)use the following formula:
1328 * iconview_size = 2 * margin + n_items * spaced_size - spacing
1329 * So when reading this code and fixing my bugs where I confuse these two, be
1330 * aware of this distinction.
1331 */
1332static void
1333cell_area_get_preferred_size (GtkIconView *icon_view,
1334 GtkCellAreaContext *context,
1335 GtkOrientation orientation,
1336 int for_size,
1337 int *minimum,
1338 int *natural)
1339{
1340 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1341 {
1342 if (for_size > 0)
1343 gtk_cell_area_get_preferred_width_for_height (area: icon_view->priv->cell_area,
1344 context,
1345 GTK_WIDGET (icon_view),
1346 height: for_size,
1347 minimum_width: minimum, natural_width: natural);
1348 else
1349 gtk_cell_area_get_preferred_width (area: icon_view->priv->cell_area,
1350 context,
1351 GTK_WIDGET (icon_view),
1352 minimum_width: minimum, natural_width: natural);
1353 }
1354 else
1355 {
1356 if (for_size > 0)
1357 gtk_cell_area_get_preferred_height_for_width (area: icon_view->priv->cell_area,
1358 context,
1359 GTK_WIDGET (icon_view),
1360 width: for_size,
1361 minimum_height: minimum, natural_height: natural);
1362 else
1363 gtk_cell_area_get_preferred_height (area: icon_view->priv->cell_area,
1364 context,
1365 GTK_WIDGET (icon_view),
1366 minimum_height: minimum, natural_height: natural);
1367 }
1368}
1369
1370static gboolean
1371gtk_icon_view_is_empty (GtkIconView *icon_view)
1372{
1373 return icon_view->priv->items == NULL;
1374}
1375
1376static void
1377gtk_icon_view_get_preferred_item_size (GtkIconView *icon_view,
1378 GtkOrientation orientation,
1379 int for_size,
1380 int *minimum,
1381 int *natural)
1382{
1383 GtkIconViewPrivate *priv = icon_view->priv;
1384 GtkCellAreaContext *context;
1385 GList *items;
1386
1387 g_assert (!gtk_icon_view_is_empty (icon_view));
1388
1389 context = gtk_cell_area_create_context (area: priv->cell_area);
1390
1391 for_size -= 2 * priv->item_padding;
1392
1393 if (for_size > 0)
1394 {
1395 /* This is necessary for the context to work properly */
1396 for (items = priv->items; items; items = items->next)
1397 {
1398 GtkIconViewItem *item = items->data;
1399
1400 _gtk_icon_view_set_cell_data (icon_view, item);
1401 cell_area_get_preferred_size (icon_view, context, orientation: 1 - orientation, for_size: -1, NULL, NULL);
1402 }
1403 }
1404
1405 for (items = priv->items; items; items = items->next)
1406 {
1407 GtkIconViewItem *item = items->data;
1408
1409 _gtk_icon_view_set_cell_data (icon_view, item);
1410 if (items == priv->items)
1411 adjust_wrap_width (icon_view);
1412 cell_area_get_preferred_size (icon_view, context, orientation, for_size, NULL, NULL);
1413 }
1414
1415 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1416 {
1417 if (for_size > 0)
1418 gtk_cell_area_context_get_preferred_width_for_height (context,
1419 height: for_size,
1420 minimum_width: minimum, natural_width: natural);
1421 else
1422 gtk_cell_area_context_get_preferred_width (context,
1423 minimum_width: minimum, natural_width: natural);
1424 }
1425 else
1426 {
1427 if (for_size > 0)
1428 gtk_cell_area_context_get_preferred_height_for_width (context,
1429 width: for_size,
1430 minimum_height: minimum, natural_height: natural);
1431 else
1432 gtk_cell_area_context_get_preferred_height (context,
1433 minimum_height: minimum, natural_height: natural);
1434 }
1435
1436 if (orientation == GTK_ORIENTATION_HORIZONTAL && priv->item_width >= 0)
1437 {
1438 if (minimum)
1439 *minimum = MAX (*minimum, priv->item_width);
1440 if (natural)
1441 *natural = *minimum;
1442 }
1443
1444 if (minimum)
1445 *minimum = MAX (1, *minimum + 2 * priv->item_padding);
1446 if (natural)
1447 *natural = MAX (1, *natural + 2 * priv->item_padding);
1448
1449 g_object_unref (object: context);
1450}
1451
1452static void
1453gtk_icon_view_compute_n_items_for_size (GtkIconView *icon_view,
1454 GtkOrientation orientation,
1455 int size,
1456 int *min_items,
1457 int *min_item_size,
1458 int *max_items,
1459 int *max_item_size)
1460{
1461 GtkIconViewPrivate *priv = icon_view->priv;
1462 int minimum, natural, spacing;
1463
1464 g_return_if_fail (min_item_size == NULL || min_items != NULL);
1465 g_return_if_fail (max_item_size == NULL || max_items != NULL);
1466 g_return_if_fail (!gtk_icon_view_is_empty (icon_view));
1467
1468 gtk_icon_view_get_preferred_item_size (icon_view, orientation, for_size: -1, minimum: &minimum, natural: &natural);
1469
1470 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1471 spacing = priv->column_spacing;
1472 else
1473 spacing = priv->row_spacing;
1474
1475 size -= 2 * priv->margin;
1476 size += spacing;
1477 minimum += spacing;
1478 natural += spacing;
1479
1480 if (priv->columns > 0)
1481 {
1482 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1483 {
1484 if (min_items)
1485 *min_items = priv->columns;
1486 if (max_items)
1487 *max_items = priv->columns;
1488 }
1489 else
1490 {
1491 int n_items = gtk_icon_view_get_n_items (icon_view);
1492
1493 if (min_items)
1494 *min_items = (n_items + priv->columns - 1) / priv->columns;
1495 if (max_items)
1496 *max_items = (n_items + priv->columns - 1) / priv->columns;
1497 }
1498 }
1499 else
1500 {
1501 if (max_items)
1502 {
1503 if (size <= minimum)
1504 *max_items = 1;
1505 else
1506 *max_items = size / minimum;
1507 }
1508
1509 if (min_items)
1510 {
1511 if (size <= natural)
1512 *min_items = 1;
1513 else
1514 *min_items = size / natural;
1515 }
1516 }
1517
1518 if (min_item_size)
1519 {
1520 *min_item_size = size / *min_items;
1521 *min_item_size = CLAMP (*min_item_size, minimum, natural);
1522 *min_item_size -= spacing;
1523 *min_item_size -= 2 * priv->item_padding;
1524 }
1525
1526 if (max_item_size)
1527 {
1528 *max_item_size = size / *max_items;
1529 *max_item_size = CLAMP (*max_item_size, minimum, natural);
1530 *max_item_size -= spacing;
1531 *max_item_size -= 2 * priv->item_padding;
1532 }
1533}
1534
1535static GtkSizeRequestMode
1536gtk_icon_view_get_request_mode (GtkWidget *widget)
1537{
1538 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
1539}
1540
1541static void
1542gtk_icon_view_measure (GtkWidget *widget,
1543 GtkOrientation orientation,
1544 int for_size,
1545 int *minimum,
1546 int *natural,
1547 int *minimum_baseline,
1548 int *natural_baseline)
1549{
1550 GtkIconView *icon_view = GTK_ICON_VIEW (widget);
1551 GtkIconViewPrivate *priv = icon_view->priv;
1552 GtkOrientation other_orientation = orientation == GTK_ORIENTATION_HORIZONTAL ?
1553 GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL;
1554 int item_min, item_nat, items = 0, item_size = 0, n_items;
1555
1556 if (gtk_icon_view_is_empty (icon_view))
1557 {
1558 *minimum = *natural = 2 * priv->margin;
1559 return;
1560 }
1561
1562 n_items = gtk_icon_view_get_n_items (icon_view);
1563
1564 if (for_size < 0)
1565 {
1566 gtk_icon_view_get_preferred_item_size (icon_view, orientation, for_size: -1, minimum: &item_min, natural: &item_nat);
1567
1568 if (priv->columns > 0)
1569 {
1570 int n_rows = (n_items + priv->columns - 1) / priv->columns;
1571
1572 *minimum = item_min * n_rows + priv->row_spacing * (n_rows - 1);
1573 *natural = item_nat * n_rows + priv->row_spacing * (n_rows - 1);
1574 }
1575 else
1576 {
1577 *minimum = item_min;
1578 *natural = item_nat * n_items + priv->row_spacing * (n_items - 1);
1579 }
1580 }
1581 else
1582 {
1583 gtk_icon_view_compute_n_items_for_size (icon_view, orientation, size: for_size, NULL, NULL, max_items: &items, max_item_size: &item_size);
1584 gtk_icon_view_get_preferred_item_size (icon_view, orientation: other_orientation, for_size: item_size, minimum: &item_min, natural: &item_nat);
1585 *minimum = (item_min + priv->row_spacing) * ((n_items + items - 1) / items) - priv->row_spacing;
1586 *natural = (item_nat + priv->row_spacing) * ((n_items + items - 1) / items) - priv->row_spacing;
1587 }
1588
1589 *minimum += 2 * priv->margin;
1590 *natural += 2 * priv->margin;
1591}
1592
1593
1594static void
1595gtk_icon_view_allocate_children (GtkIconView *icon_view)
1596{
1597 GList *list;
1598
1599 for (list = icon_view->priv->children; list; list = list->next)
1600 {
1601 GtkIconViewChild *child = list->data;
1602
1603 /* totally ignore our child's requisition */
1604 gtk_widget_size_allocate (widget: child->widget, allocation: &child->area, baseline: -1);
1605 }
1606}
1607
1608static void
1609gtk_icon_view_size_allocate (GtkWidget *widget,
1610 int width,
1611 int height,
1612 int baseline)
1613{
1614 GtkIconView *icon_view = GTK_ICON_VIEW (widget);
1615
1616 gtk_icon_view_layout (icon_view);
1617
1618 gtk_icon_view_allocate_children (icon_view);
1619
1620 /* Delay signal emission */
1621 g_object_freeze_notify (G_OBJECT (icon_view->priv->hadjustment));
1622 g_object_freeze_notify (G_OBJECT (icon_view->priv->vadjustment));
1623
1624 gtk_icon_view_set_hadjustment_values (icon_view);
1625 gtk_icon_view_set_vadjustment_values (icon_view);
1626
1627 if (gtk_widget_get_realized (widget) &&
1628 icon_view->priv->scroll_to_path)
1629 {
1630 GtkTreePath *path;
1631 path = gtk_tree_row_reference_get_path (reference: icon_view->priv->scroll_to_path);
1632 gtk_tree_row_reference_free (reference: icon_view->priv->scroll_to_path);
1633 icon_view->priv->scroll_to_path = NULL;
1634
1635 gtk_icon_view_scroll_to_path (icon_view, path,
1636 use_align: icon_view->priv->scroll_to_use_align,
1637 row_align: icon_view->priv->scroll_to_row_align,
1638 col_align: icon_view->priv->scroll_to_col_align);
1639 gtk_tree_path_free (path);
1640 }
1641
1642 /* Emit any pending signals now */
1643 g_object_thaw_notify (G_OBJECT (icon_view->priv->hadjustment));
1644 g_object_thaw_notify (G_OBJECT (icon_view->priv->vadjustment));
1645}
1646
1647static void
1648gtk_icon_view_snapshot (GtkWidget *widget,
1649 GtkSnapshot *snapshot)
1650{
1651 GtkIconView *icon_view;
1652 GList *icons;
1653 GtkTreePath *path;
1654 int dest_index;
1655 GtkIconViewDropPosition dest_pos;
1656 GtkIconViewItem *dest_item = NULL;
1657 GtkStyleContext *context;
1658 int width, height;
1659 double offset_x, offset_y;
1660
1661 icon_view = GTK_ICON_VIEW (widget);
1662
1663 context = gtk_widget_get_style_context (widget);
1664 width = gtk_widget_get_width (widget);
1665 height = gtk_widget_get_height (widget);
1666
1667 offset_x = gtk_adjustment_get_value (adjustment: icon_view->priv->hadjustment);
1668 offset_y = gtk_adjustment_get_value (adjustment: icon_view->priv->vadjustment);
1669
1670 gtk_snapshot_save (snapshot);
1671 gtk_snapshot_translate (snapshot, point: &GRAPHENE_POINT_INIT (- offset_x, - offset_y));
1672
1673 gtk_icon_view_get_drag_dest_item (icon_view, path: &path, pos: &dest_pos);
1674
1675 if (path)
1676 {
1677 dest_index = gtk_tree_path_get_indices (path)[0];
1678 gtk_tree_path_free (path);
1679 }
1680 else
1681 dest_index = -1;
1682
1683 for (icons = icon_view->priv->items; icons; icons = icons->next)
1684 {
1685 GtkIconViewItem *item = icons->data;
1686 graphene_rect_t area;
1687
1688 graphene_rect_init (r: &area,
1689 x: item->cell_area.x - icon_view->priv->item_padding,
1690 y: item->cell_area.y - icon_view->priv->item_padding,
1691 width: item->cell_area.width + icon_view->priv->item_padding * 2,
1692 height: item->cell_area.height + icon_view->priv->item_padding * 2);
1693
1694 if (gdk_rectangle_intersect (src1: &item->cell_area,
1695 src2: &(GdkRectangle) { offset_x, offset_y, width, height }, NULL))
1696 {
1697 gtk_icon_view_snapshot_item (icon_view, snapshot, item,
1698 x: item->cell_area.x, y: item->cell_area.y,
1699 draw_focus: icon_view->priv->draw_focus);
1700
1701 if (dest_index == item->index)
1702 dest_item = item;
1703 }
1704 }
1705
1706 if (dest_item &&
1707 dest_pos != GTK_ICON_VIEW_NO_DROP)
1708 {
1709 GdkRectangle rect = { 0 };
1710
1711 switch (dest_pos)
1712 {
1713 case GTK_ICON_VIEW_DROP_INTO:
1714 rect = dest_item->cell_area;
1715 break;
1716 case GTK_ICON_VIEW_DROP_ABOVE:
1717 rect.x = dest_item->cell_area.x;
1718 rect.y = dest_item->cell_area.y - 1;
1719 rect.width = dest_item->cell_area.width;
1720 rect.height = 2;
1721 break;
1722 case GTK_ICON_VIEW_DROP_LEFT:
1723 rect.x = dest_item->cell_area.x - 1;
1724 rect.y = dest_item->cell_area.y;
1725 rect.width = 2;
1726 rect.height = dest_item->cell_area.height;
1727 break;
1728 case GTK_ICON_VIEW_DROP_BELOW:
1729 rect.x = dest_item->cell_area.x;
1730 rect.y = dest_item->cell_area.y + dest_item->cell_area.height - 1;
1731 rect.width = dest_item->cell_area.width;
1732 rect.height = 2;
1733 break;
1734 case GTK_ICON_VIEW_DROP_RIGHT:
1735 rect.x = dest_item->cell_area.x + dest_item->cell_area.width - 1;
1736 rect.y = dest_item->cell_area.y;
1737 rect.width = 2;
1738 rect.height = dest_item->cell_area.height;
1739 break;
1740 case GTK_ICON_VIEW_NO_DROP:
1741 default:
1742 break;
1743 }
1744
1745 gtk_style_context_save_to_node (context, node: icon_view->priv->dndnode);
1746 gtk_style_context_set_state (context, flags: gtk_style_context_get_state (context) | GTK_STATE_FLAG_DROP_ACTIVE);
1747
1748 gtk_snapshot_render_frame (snapshot, context,
1749 x: rect.x, y: rect.y,
1750 width: rect.width, height: rect.height);
1751
1752 gtk_style_context_restore (context);
1753 }
1754
1755 if (icon_view->priv->doing_rubberband)
1756 gtk_icon_view_snapshot_rubberband (icon_view, snapshot);
1757
1758 gtk_snapshot_restore (snapshot);
1759
1760 GTK_WIDGET_CLASS (gtk_icon_view_parent_class)->snapshot (widget, snapshot);
1761}
1762
1763static gboolean
1764rubberband_scroll_timeout (gpointer data)
1765{
1766 GtkIconView *icon_view = data;
1767
1768 gtk_adjustment_set_value (adjustment: icon_view->priv->vadjustment,
1769 value: gtk_adjustment_get_value (adjustment: icon_view->priv->vadjustment) +
1770 icon_view->priv->scroll_value_diff);
1771
1772 gtk_icon_view_update_rubberband (icon_view);
1773
1774 return TRUE;
1775}
1776
1777static GtkIconViewItem *
1778_gtk_icon_view_get_item_at_widget_coords (GtkIconView *icon_view,
1779 int x,
1780 int y,
1781 gboolean only_in_cell,
1782 GtkCellRenderer **cell_at_pos)
1783{
1784 x += gtk_adjustment_get_value (adjustment: icon_view->priv->hadjustment);
1785 y += gtk_adjustment_get_value (adjustment: icon_view->priv->vadjustment);
1786
1787 return _gtk_icon_view_get_item_at_coords (icon_view, x, y,
1788 only_in_cell, cell_at_pos);
1789}
1790
1791static void
1792gtk_icon_view_motion (GtkEventController *controller,
1793 double x,
1794 double y,
1795 gpointer user_data)
1796{
1797 GtkIconView *icon_view;
1798 int abs_y;
1799 GdkDevice *device;
1800
1801 icon_view = GTK_ICON_VIEW (user_data);
1802
1803 icon_view->priv->mouse_x = x;
1804 icon_view->priv->mouse_y = y;
1805
1806 device = gtk_event_controller_get_current_event_device (controller);
1807 gtk_icon_view_maybe_begin_drag (icon_view, x, y, device);
1808
1809 if (icon_view->priv->doing_rubberband)
1810 {
1811 int height;
1812 gtk_icon_view_update_rubberband (icon_view);
1813
1814 abs_y = icon_view->priv->mouse_y - icon_view->priv->height *
1815 (gtk_adjustment_get_value (adjustment: icon_view->priv->vadjustment) /
1816 (gtk_adjustment_get_upper (adjustment: icon_view->priv->vadjustment) -
1817 gtk_adjustment_get_lower (adjustment: icon_view->priv->vadjustment)));
1818
1819 height = gtk_widget_get_height (GTK_WIDGET (icon_view));
1820
1821
1822 if (abs_y < 0 || abs_y > height)
1823 {
1824 if (abs_y < 0)
1825 icon_view->priv->scroll_value_diff = abs_y;
1826 else
1827 icon_view->priv->scroll_value_diff = abs_y - height;
1828
1829 icon_view->priv->event_last_x = icon_view->priv->mouse_x;
1830 icon_view->priv->event_last_y = icon_view->priv->mouse_x;
1831
1832 if (icon_view->priv->scroll_timeout_id == 0) {
1833 icon_view->priv->scroll_timeout_id = g_timeout_add (interval: 30, function: rubberband_scroll_timeout, data: icon_view);
1834 gdk_source_set_static_name_by_id (tag: icon_view->priv->scroll_timeout_id, name: "[gtk] rubberband_scroll_timeout");
1835 }
1836 }
1837 else
1838 remove_scroll_timeout (icon_view);
1839 }
1840 else
1841 {
1842 GtkIconViewItem *item, *last_prelight_item;
1843 GtkCellRenderer *cell = NULL;
1844
1845 last_prelight_item = icon_view->priv->last_prelight;
1846 item = _gtk_icon_view_get_item_at_widget_coords (icon_view,
1847 x: icon_view->priv->mouse_x,
1848 y: icon_view->priv->mouse_y,
1849 FALSE,
1850 cell_at_pos: &cell);
1851
1852 if (item != last_prelight_item)
1853 {
1854 if (item != NULL)
1855 {
1856 gtk_icon_view_queue_draw_item (icon_view, item);
1857 }
1858
1859 if (last_prelight_item != NULL)
1860 {
1861 gtk_icon_view_queue_draw_item (icon_view,
1862 item: icon_view->priv->last_prelight);
1863 }
1864
1865 icon_view->priv->last_prelight = item;
1866 }
1867 }
1868}
1869
1870static void
1871gtk_icon_view_leave (GtkEventController *controller,
1872 gpointer user_data)
1873{
1874 GtkIconView *icon_view;
1875 GtkIconViewPrivate *priv;
1876
1877 icon_view = GTK_ICON_VIEW (user_data);
1878 priv = icon_view->priv;
1879
1880 if (priv->last_prelight)
1881 {
1882 gtk_icon_view_queue_draw_item (icon_view, item: priv->last_prelight);
1883 priv->last_prelight = NULL;
1884 }
1885}
1886
1887static void
1888gtk_icon_view_remove (GtkIconView *icon_view,
1889 GtkWidget *widget)
1890{
1891 GtkIconViewChild *child = NULL;
1892 GList *tmp_list;
1893
1894 tmp_list = icon_view->priv->children;
1895 while (tmp_list)
1896 {
1897 child = tmp_list->data;
1898 if (child->widget == widget)
1899 {
1900 gtk_widget_unparent (widget);
1901
1902 icon_view->priv->children = g_list_remove_link (list: icon_view->priv->children, llink: tmp_list);
1903 g_list_free_1 (list: tmp_list);
1904 g_free (mem: child);
1905 return;
1906 }
1907
1908 tmp_list = tmp_list->next;
1909 }
1910}
1911
1912static void
1913gtk_icon_view_add_editable (GtkCellArea *area,
1914 GtkCellRenderer *renderer,
1915 GtkCellEditable *editable,
1916 GdkRectangle *cell_area,
1917 const char *path,
1918 GtkIconView *icon_view)
1919{
1920 GtkIconViewChild *child;
1921 GtkWidget *widget = GTK_WIDGET (editable);
1922
1923 child = g_new (GtkIconViewChild, 1);
1924
1925 child->widget = widget;
1926 child->area.x = cell_area->x;
1927 child->area.y = cell_area->y;
1928 child->area.width = cell_area->width;
1929 child->area.height = cell_area->height;
1930
1931 icon_view->priv->children = g_list_append (list: icon_view->priv->children, data: child);
1932
1933 gtk_widget_set_parent (widget, GTK_WIDGET (icon_view));
1934}
1935
1936static void
1937gtk_icon_view_remove_editable (GtkCellArea *area,
1938 GtkCellRenderer *renderer,
1939 GtkCellEditable *editable,
1940 GtkIconView *icon_view)
1941{
1942 GtkTreePath *path;
1943
1944 if (gtk_widget_has_focus (GTK_WIDGET (editable)))
1945 gtk_widget_grab_focus (GTK_WIDGET (icon_view));
1946
1947 gtk_icon_view_remove (icon_view, GTK_WIDGET (editable));
1948
1949 path = gtk_tree_path_new_from_string (path: gtk_cell_area_get_current_path_string (area));
1950 gtk_icon_view_queue_draw_path (icon_view, path);
1951 gtk_tree_path_free (path);
1952}
1953
1954/**
1955 * gtk_icon_view_set_cursor:
1956 * @icon_view: A `GtkIconView`
1957 * @path: A `GtkTreePath`
1958 * @cell: (nullable): One of the cell renderers of @icon_view
1959 * @start_editing: %TRUE if the specified cell should start being edited.
1960 *
1961 * Sets the current keyboard focus to be at @path, and selects it. This is
1962 * useful when you want to focus the user’s attention on a particular item.
1963 * If @cell is not %NULL, then focus is given to the cell specified by
1964 * it. Additionally, if @start_editing is %TRUE, then editing should be
1965 * started in the specified cell.
1966 *
1967 * This function is often followed by `gtk_widget_grab_focus
1968 * (icon_view)` in order to give keyboard focus to the widget.
1969 * Please note that editing can only happen when the widget is realized.
1970 **/
1971void
1972gtk_icon_view_set_cursor (GtkIconView *icon_view,
1973 GtkTreePath *path,
1974 GtkCellRenderer *cell,
1975 gboolean start_editing)
1976{
1977 GtkIconViewItem *item = NULL;
1978
1979 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
1980 g_return_if_fail (path != NULL);
1981 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
1982
1983 if (icon_view->priv->cell_area)
1984 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
1985
1986 if (gtk_tree_path_get_depth (path) == 1)
1987 item = g_list_nth_data (list: icon_view->priv->items,
1988 n: gtk_tree_path_get_indices(path)[0]);
1989
1990 if (!item)
1991 return;
1992
1993 _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell: cell);
1994 gtk_icon_view_scroll_to_path (icon_view, path, FALSE, row_align: 0.0, col_align: 0.0);
1995
1996 if (start_editing &&
1997 icon_view->priv->cell_area)
1998 {
1999 GtkCellAreaContext *context;
2000
2001 context = g_ptr_array_index (icon_view->priv->row_contexts, item->row);
2002 _gtk_icon_view_set_cell_data (icon_view, item);
2003 gtk_cell_area_activate (area: icon_view->priv->cell_area, context,
2004 GTK_WIDGET (icon_view), cell_area: &item->cell_area,
2005 flags: 0, TRUE);
2006 }
2007}
2008
2009/**
2010 * gtk_icon_view_get_cursor:
2011 * @icon_view: A `GtkIconView`
2012 * @path: (out) (optional) (transfer full): Return location for the current
2013 * cursor path
2014 * @cell: (out) (optional) (transfer none): Return location the current
2015 * focus cell
2016 *
2017 * Fills in @path and @cell with the current cursor path and cell.
2018 * If the cursor isn’t currently set, then *@path will be %NULL.
2019 * If no cell currently has focus, then *@cell will be %NULL.
2020 *
2021 * The returned `GtkTreePath` must be freed with gtk_tree_path_free().
2022 *
2023 * Returns: %TRUE if the cursor is set.
2024 **/
2025gboolean
2026gtk_icon_view_get_cursor (GtkIconView *icon_view,
2027 GtkTreePath **path,
2028 GtkCellRenderer **cell)
2029{
2030 GtkIconViewItem *item;
2031
2032 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE);
2033
2034 item = icon_view->priv->cursor_item;
2035
2036 if (path != NULL)
2037 {
2038 if (item != NULL)
2039 *path = gtk_tree_path_new_from_indices (first_index: item->index, -1);
2040 else
2041 *path = NULL;
2042 }
2043
2044 if (cell != NULL && item != NULL && icon_view->priv->cell_area != NULL)
2045 *cell = gtk_cell_area_get_focus_cell (area: icon_view->priv->cell_area);
2046
2047 return (item != NULL);
2048}
2049
2050static void
2051gtk_icon_view_button_press (GtkGestureClick *gesture,
2052 int n_press,
2053 double x,
2054 double y,
2055 gpointer user_data)
2056{
2057 GtkIconView *icon_view = user_data;
2058 GtkWidget *widget = GTK_WIDGET (icon_view);
2059 GtkIconViewItem *item;
2060 gboolean dirty = FALSE;
2061 GtkCellRenderer *cell = NULL, *cursor_cell = NULL;
2062 int button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
2063 GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
2064 GdkEvent *event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
2065 GdkModifierType state;
2066
2067 if (!gtk_widget_has_focus (widget))
2068 gtk_widget_grab_focus (widget);
2069
2070 if (button == GDK_BUTTON_PRIMARY)
2071 {
2072 GdkModifierType extend_mod_mask = GDK_SHIFT_MASK;
2073 GdkModifierType modify_mod_mask = GDK_CONTROL_MASK;
2074
2075 state = gdk_event_get_modifier_state (event);
2076
2077 item = _gtk_icon_view_get_item_at_widget_coords (icon_view,
2078 x, y,
2079 FALSE,
2080 cell_at_pos: &cell);
2081
2082 /*
2083 * We consider only the cells' area as the item area if the
2084 * item is not selected, but if it *is* selected, the complete
2085 * selection rectangle is considered to be part of the item.
2086 */
2087 if (item != NULL && (cell != NULL || item->selected))
2088 {
2089 if (cell != NULL)
2090 {
2091 if (gtk_cell_renderer_is_activatable (cell))
2092 cursor_cell = cell;
2093 }
2094
2095 gtk_icon_view_scroll_to_item (icon_view, item);
2096
2097 if (icon_view->priv->selection_mode == GTK_SELECTION_NONE)
2098 {
2099 _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2100 }
2101 else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE &&
2102 (state & extend_mod_mask))
2103 {
2104 gtk_icon_view_unselect_all_internal (icon_view);
2105
2106 _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2107 if (!icon_view->priv->anchor_item)
2108 icon_view->priv->anchor_item = item;
2109 else
2110 gtk_icon_view_select_all_between (icon_view,
2111 anchor: icon_view->priv->anchor_item,
2112 cursor: item);
2113 dirty = TRUE;
2114 }
2115 else
2116 {
2117 if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE ||
2118 ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) &&
2119 (state & modify_mod_mask))
2120 {
2121 item->selected = !item->selected;
2122 gtk_icon_view_queue_draw_item (icon_view, item);
2123 dirty = TRUE;
2124 }
2125 else
2126 {
2127 gtk_icon_view_unselect_all_internal (icon_view);
2128
2129 item->selected = TRUE;
2130 gtk_icon_view_queue_draw_item (icon_view, item);
2131 dirty = TRUE;
2132 }
2133 _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell);
2134 icon_view->priv->anchor_item = item;
2135 }
2136
2137 /* Save press to possibly begin a drag */
2138 if (icon_view->priv->pressed_button < 0)
2139 {
2140 icon_view->priv->pressed_button = button;
2141 icon_view->priv->press_start_x = x;
2142 icon_view->priv->press_start_y = y;
2143 }
2144
2145 icon_view->priv->last_single_clicked = item;
2146
2147 /* cancel the current editing, if it exists */
2148 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
2149
2150 if (cell != NULL && gtk_cell_renderer_is_activatable (cell))
2151 {
2152 GtkCellAreaContext *context;
2153
2154 context = g_ptr_array_index (icon_view->priv->row_contexts, item->row);
2155
2156 _gtk_icon_view_set_cell_data (icon_view, item);
2157 gtk_cell_area_activate (area: icon_view->priv->cell_area, context,
2158 GTK_WIDGET (icon_view),
2159 cell_area: &item->cell_area, flags: 0, FALSE);
2160 }
2161 }
2162 else
2163 {
2164 if (icon_view->priv->selection_mode != GTK_SELECTION_BROWSE &&
2165 !(state & modify_mod_mask))
2166 {
2167 dirty = gtk_icon_view_unselect_all_internal (icon_view);
2168 }
2169
2170 if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
2171 gtk_icon_view_start_rubberbanding (icon_view,
2172 device: gtk_gesture_get_device (GTK_GESTURE (gesture)),
2173 x, y);
2174 }
2175
2176 /* don't draw keyboard focus around a clicked-on item */
2177 icon_view->priv->draw_focus = FALSE;
2178 }
2179
2180 if (!icon_view->priv->activate_on_single_click
2181 && button == GDK_BUTTON_PRIMARY
2182 && n_press == 2)
2183 {
2184 item = _gtk_icon_view_get_item_at_widget_coords (icon_view,
2185 x, y,
2186 FALSE,
2187 NULL);
2188
2189 if (item && item == icon_view->priv->last_single_clicked)
2190 {
2191 GtkTreePath *path;
2192
2193 path = gtk_tree_path_new_from_indices (first_index: item->index, -1);
2194 gtk_icon_view_item_activated (icon_view, path);
2195 gtk_tree_path_free (path);
2196 }
2197
2198 icon_view->priv->last_single_clicked = NULL;
2199 icon_view->priv->pressed_button = -1;
2200 }
2201
2202 if (dirty)
2203 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
2204}
2205
2206static gboolean
2207button_event_modifies_selection (GdkEvent *event)
2208{
2209 guint state = gdk_event_get_modifier_state (event);
2210
2211 return (state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
2212}
2213
2214static void
2215gtk_icon_view_button_release (GtkGestureClick *gesture,
2216 int n_press,
2217 double x,
2218 double y,
2219 gpointer user_data)
2220{
2221 GtkIconView *icon_view = user_data;
2222 int button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
2223 GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
2224 GdkEvent *event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
2225
2226 if (icon_view->priv->pressed_button == button)
2227 icon_view->priv->pressed_button = -1;
2228
2229 gtk_icon_view_stop_rubberbanding (icon_view);
2230
2231 remove_scroll_timeout (icon_view);
2232
2233 if (button == GDK_BUTTON_PRIMARY
2234 && icon_view->priv->activate_on_single_click
2235 && !button_event_modifies_selection (event)
2236 && icon_view->priv->last_single_clicked != NULL)
2237 {
2238 GtkIconViewItem *item;
2239
2240 item = _gtk_icon_view_get_item_at_widget_coords (icon_view,
2241 x, y,
2242 FALSE,
2243 NULL);
2244 if (item == icon_view->priv->last_single_clicked)
2245 {
2246 GtkTreePath *path;
2247 path = gtk_tree_path_new_from_indices (first_index: item->index, -1);
2248 gtk_icon_view_item_activated (icon_view, path);
2249 gtk_tree_path_free (path);
2250 }
2251
2252 icon_view->priv->last_single_clicked = NULL;
2253 }
2254}
2255
2256static gboolean
2257gtk_icon_view_key_pressed (GtkEventControllerKey *controller,
2258 guint keyval,
2259 guint keycode,
2260 GdkModifierType state,
2261 GtkWidget *widget)
2262{
2263 GtkIconView *icon_view = GTK_ICON_VIEW (widget);
2264
2265 if (icon_view->priv->doing_rubberband)
2266 {
2267 if (keyval == GDK_KEY_Escape)
2268 gtk_icon_view_stop_rubberbanding (icon_view);
2269
2270 return TRUE;
2271 }
2272
2273 return FALSE;
2274}
2275
2276static void
2277gtk_icon_view_update_rubberband (GtkIconView *icon_view)
2278{
2279 int x, y;
2280
2281 x = MAX (icon_view->priv->mouse_x, 0);
2282 y = MAX (icon_view->priv->mouse_y, 0);
2283
2284 icon_view->priv->rubberband_x2 = x + gtk_adjustment_get_value (adjustment: icon_view->priv->hadjustment);
2285 icon_view->priv->rubberband_y2 = y + gtk_adjustment_get_value (adjustment: icon_view->priv->vadjustment);
2286
2287 gtk_icon_view_update_rubberband_selection (icon_view);
2288 gtk_widget_queue_draw (GTK_WIDGET (icon_view));
2289}
2290
2291static void
2292gtk_icon_view_start_rubberbanding (GtkIconView *icon_view,
2293 GdkDevice *device,
2294 int x,
2295 int y)
2296{
2297 GtkIconViewPrivate *priv = icon_view->priv;
2298 GList *items;
2299 GtkCssNode *widget_node;
2300
2301 if (priv->rubberband_device)
2302 return;
2303
2304 for (items = priv->items; items; items = items->next)
2305 {
2306 GtkIconViewItem *item = items->data;
2307
2308 item->selected_before_rubberbanding = item->selected;
2309 }
2310
2311 priv->rubberband_x1 = x + gtk_adjustment_get_value (adjustment: priv->hadjustment);
2312 priv->rubberband_y1 = y + gtk_adjustment_get_value (adjustment: priv->vadjustment);
2313 priv->rubberband_x2 = priv->rubberband_x1;
2314 priv->rubberband_y2 = priv->rubberband_y1;
2315
2316 priv->doing_rubberband = TRUE;
2317 priv->rubberband_device = device;
2318
2319 widget_node = gtk_widget_get_css_node (GTK_WIDGET (icon_view));
2320 priv->rubberband_node = gtk_css_node_new ();
2321 gtk_css_node_set_name (cssnode: priv->rubberband_node, name: g_quark_from_static_string (string: "rubberband"));
2322 gtk_css_node_set_parent (cssnode: priv->rubberband_node, parent: widget_node);
2323 gtk_css_node_set_state (cssnode: priv->rubberband_node, state_flags: gtk_css_node_get_state (cssnode: widget_node));
2324 g_object_unref (object: priv->rubberband_node);
2325}
2326
2327static void
2328gtk_icon_view_stop_rubberbanding (GtkIconView *icon_view)
2329{
2330 GtkIconViewPrivate *priv = icon_view->priv;
2331
2332 if (!priv->doing_rubberband)
2333 return;
2334
2335 priv->doing_rubberband = FALSE;
2336 priv->rubberband_device = NULL;
2337 gtk_css_node_set_parent (cssnode: priv->rubberband_node, NULL);
2338 priv->rubberband_node = NULL;
2339
2340 gtk_widget_queue_draw (GTK_WIDGET (icon_view));
2341}
2342
2343static void
2344gtk_icon_view_update_rubberband_selection (GtkIconView *icon_view)
2345{
2346 GList *items;
2347 int x, y, width, height;
2348 gboolean dirty = FALSE;
2349
2350 x = MIN (icon_view->priv->rubberband_x1,
2351 icon_view->priv->rubberband_x2);
2352 y = MIN (icon_view->priv->rubberband_y1,
2353 icon_view->priv->rubberband_y2);
2354 width = ABS (icon_view->priv->rubberband_x1 -
2355 icon_view->priv->rubberband_x2);
2356 height = ABS (icon_view->priv->rubberband_y1 -
2357 icon_view->priv->rubberband_y2);
2358
2359 for (items = icon_view->priv->items; items; items = items->next)
2360 {
2361 GtkIconViewItem *item = items->data;
2362 gboolean is_in;
2363 gboolean selected;
2364
2365 is_in = gtk_icon_view_item_hit_test (icon_view, item,
2366 x, y, width, height);
2367
2368 selected = is_in ^ item->selected_before_rubberbanding;
2369
2370 if (item->selected != selected)
2371 {
2372 item->selected = selected;
2373 dirty = TRUE;
2374 gtk_icon_view_queue_draw_item (icon_view, item);
2375 }
2376 }
2377
2378 if (dirty)
2379 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
2380}
2381
2382
2383typedef struct {
2384 GdkRectangle hit_rect;
2385 gboolean hit;
2386} HitTestData;
2387
2388static gboolean
2389hit_test (GtkCellRenderer *renderer,
2390 const GdkRectangle *cell_area,
2391 const GdkRectangle *cell_background,
2392 HitTestData *data)
2393{
2394 if (MIN (data->hit_rect.x + data->hit_rect.width, cell_area->x + cell_area->width) -
2395 MAX (data->hit_rect.x, cell_area->x) > 0 &&
2396 MIN (data->hit_rect.y + data->hit_rect.height, cell_area->y + cell_area->height) -
2397 MAX (data->hit_rect.y, cell_area->y) > 0)
2398 data->hit = TRUE;
2399
2400 return (data->hit != FALSE);
2401}
2402
2403static gboolean
2404gtk_icon_view_item_hit_test (GtkIconView *icon_view,
2405 GtkIconViewItem *item,
2406 int x,
2407 int y,
2408 int width,
2409 int height)
2410{
2411 HitTestData data = { { x, y, width, height }, FALSE };
2412 GtkCellAreaContext *context;
2413 GdkRectangle *item_area = &item->cell_area;
2414
2415 if (MIN (x + width, item_area->x + item_area->width) - MAX (x, item_area->x) <= 0 ||
2416 MIN (y + height, item_area->y + item_area->height) - MAX (y, item_area->y) <= 0)
2417 return FALSE;
2418
2419 context = g_ptr_array_index (icon_view->priv->row_contexts, item->row);
2420
2421 _gtk_icon_view_set_cell_data (icon_view, item);
2422 gtk_cell_area_foreach_alloc (area: icon_view->priv->cell_area, context,
2423 GTK_WIDGET (icon_view),
2424 cell_area: item_area, background_area: item_area,
2425 callback: (GtkCellAllocCallback)hit_test, callback_data: &data);
2426
2427 return data.hit;
2428}
2429
2430static gboolean
2431gtk_icon_view_unselect_all_internal (GtkIconView *icon_view)
2432{
2433 gboolean dirty = FALSE;
2434 GList *items;
2435
2436 if (icon_view->priv->selection_mode == GTK_SELECTION_NONE)
2437 return FALSE;
2438
2439 for (items = icon_view->priv->items; items; items = items->next)
2440 {
2441 GtkIconViewItem *item = items->data;
2442
2443 if (item->selected)
2444 {
2445 item->selected = FALSE;
2446 dirty = TRUE;
2447 gtk_icon_view_queue_draw_item (icon_view, item);
2448 }
2449 }
2450
2451 return dirty;
2452}
2453
2454
2455/* GtkIconView signals */
2456static void
2457gtk_icon_view_real_select_all (GtkIconView *icon_view)
2458{
2459 gtk_icon_view_select_all (icon_view);
2460}
2461
2462static void
2463gtk_icon_view_real_unselect_all (GtkIconView *icon_view)
2464{
2465 gtk_icon_view_unselect_all (icon_view);
2466}
2467
2468static void
2469gtk_icon_view_real_select_cursor_item (GtkIconView *icon_view)
2470{
2471 gtk_icon_view_unselect_all (icon_view);
2472
2473 if (icon_view->priv->cursor_item != NULL)
2474 _gtk_icon_view_select_item (icon_view, item: icon_view->priv->cursor_item);
2475}
2476
2477static gboolean
2478gtk_icon_view_real_activate_cursor_item (GtkIconView *icon_view)
2479{
2480 GtkTreePath *path;
2481 GtkCellAreaContext *context;
2482
2483 if (!icon_view->priv->cursor_item)
2484 return FALSE;
2485
2486 context = g_ptr_array_index (icon_view->priv->row_contexts, icon_view->priv->cursor_item->row);
2487
2488 _gtk_icon_view_set_cell_data (icon_view, item: icon_view->priv->cursor_item);
2489 gtk_cell_area_activate (area: icon_view->priv->cell_area, context,
2490 GTK_WIDGET (icon_view),
2491 cell_area: &icon_view->priv->cursor_item->cell_area,
2492 flags: 0,
2493 FALSE);
2494
2495 path = gtk_tree_path_new_from_indices (first_index: icon_view->priv->cursor_item->index, -1);
2496 gtk_icon_view_item_activated (icon_view, path);
2497 gtk_tree_path_free (path);
2498
2499 return TRUE;
2500}
2501
2502static void
2503gtk_icon_view_real_toggle_cursor_item (GtkIconView *icon_view)
2504{
2505 if (!icon_view->priv->cursor_item)
2506 return;
2507
2508 switch (icon_view->priv->selection_mode)
2509 {
2510 case GTK_SELECTION_NONE:
2511 default:
2512 break;
2513 case GTK_SELECTION_BROWSE:
2514 _gtk_icon_view_select_item (icon_view, item: icon_view->priv->cursor_item);
2515 break;
2516 case GTK_SELECTION_SINGLE:
2517 if (icon_view->priv->cursor_item->selected)
2518 _gtk_icon_view_unselect_item (icon_view, item: icon_view->priv->cursor_item);
2519 else
2520 _gtk_icon_view_select_item (icon_view, item: icon_view->priv->cursor_item);
2521 break;
2522 case GTK_SELECTION_MULTIPLE:
2523 icon_view->priv->cursor_item->selected = !icon_view->priv->cursor_item->selected;
2524 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
2525
2526 gtk_icon_view_queue_draw_item (icon_view, item: icon_view->priv->cursor_item);
2527 break;
2528 }
2529}
2530
2531static void
2532gtk_icon_view_set_hadjustment_values (GtkIconView *icon_view)
2533{
2534 int width;
2535 GtkAdjustment *adj = icon_view->priv->hadjustment;
2536 double old_page_size;
2537 double old_upper;
2538 double old_value;
2539 double new_value;
2540 double new_upper;
2541
2542 width = gtk_widget_get_width (GTK_WIDGET (icon_view));
2543
2544 old_value = gtk_adjustment_get_value (adjustment: adj);
2545 old_upper = gtk_adjustment_get_upper (adjustment: adj);
2546 old_page_size = gtk_adjustment_get_page_size (adjustment: adj);
2547 new_upper = MAX (width, icon_view->priv->width);
2548
2549 if (gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL)
2550 {
2551 /* Make sure no scrolling occurs for RTL locales also (if possible) */
2552 /* Quick explanation:
2553 * In LTR locales, leftmost portion of visible rectangle should stay
2554 * fixed, which means left edge of scrollbar thumb should remain fixed
2555 * and thus adjustment's value should stay the same.
2556 *
2557 * In RTL locales, we want to keep rightmost portion of visible
2558 * rectangle fixed. This means right edge of thumb should remain fixed.
2559 * In this case, upper - value - page_size should remain constant.
2560 */
2561 new_value = (new_upper - width) -
2562 (old_upper - old_value - old_page_size);
2563 new_value = CLAMP (new_value, 0, new_upper - width);
2564 }
2565 else
2566 new_value = CLAMP (old_value, 0, new_upper - width);
2567
2568 gtk_adjustment_configure (adjustment: adj,
2569 value: new_value,
2570 lower: 0.0,
2571 upper: new_upper,
2572 step_increment: width * 0.1,
2573 page_increment: width * 0.9,
2574 page_size: width);
2575}
2576
2577static void
2578gtk_icon_view_set_vadjustment_values (GtkIconView *icon_view)
2579{
2580 int height;
2581 GtkAdjustment *adj = icon_view->priv->vadjustment;
2582
2583 height = gtk_widget_get_height (GTK_WIDGET (icon_view));
2584
2585 gtk_adjustment_configure (adjustment: adj,
2586 value: gtk_adjustment_get_value (adjustment: adj),
2587 lower: 0.0,
2588 MAX (height, icon_view->priv->height),
2589 step_increment: height * 0.1,
2590 page_increment: height * 0.9,
2591 page_size: height);
2592}
2593
2594static void
2595gtk_icon_view_set_hadjustment (GtkIconView *icon_view,
2596 GtkAdjustment *adjustment)
2597{
2598 GtkIconViewPrivate *priv = icon_view->priv;
2599
2600 if (adjustment && priv->hadjustment == adjustment)
2601 return;
2602
2603 if (priv->hadjustment != NULL)
2604 {
2605 g_signal_handlers_disconnect_matched (instance: priv->hadjustment,
2606 mask: G_SIGNAL_MATCH_DATA,
2607 signal_id: 0, detail: 0, NULL, NULL, data: icon_view);
2608 g_object_unref (object: priv->hadjustment);
2609 }
2610
2611 if (!adjustment)
2612 adjustment = gtk_adjustment_new (value: 0.0, lower: 0.0, upper: 0.0,
2613 step_increment: 0.0, page_increment: 0.0, page_size: 0.0);
2614
2615 g_signal_connect (adjustment, "value-changed",
2616 G_CALLBACK (gtk_icon_view_adjustment_changed), icon_view);
2617 priv->hadjustment = g_object_ref_sink (adjustment);
2618 gtk_icon_view_set_hadjustment_values (icon_view);
2619
2620 g_object_notify (G_OBJECT (icon_view), property_name: "hadjustment");
2621}
2622
2623static void
2624gtk_icon_view_set_vadjustment (GtkIconView *icon_view,
2625 GtkAdjustment *adjustment)
2626{
2627 GtkIconViewPrivate *priv = icon_view->priv;
2628
2629 if (adjustment && priv->vadjustment == adjustment)
2630 return;
2631
2632 if (priv->vadjustment != NULL)
2633 {
2634 g_signal_handlers_disconnect_matched (instance: priv->vadjustment,
2635 mask: G_SIGNAL_MATCH_DATA,
2636 signal_id: 0, detail: 0, NULL, NULL, data: icon_view);
2637 g_object_unref (object: priv->vadjustment);
2638 }
2639
2640 if (!adjustment)
2641 adjustment = gtk_adjustment_new (value: 0.0, lower: 0.0, upper: 0.0,
2642 step_increment: 0.0, page_increment: 0.0, page_size: 0.0);
2643
2644 g_signal_connect (adjustment, "value-changed",
2645 G_CALLBACK (gtk_icon_view_adjustment_changed), icon_view);
2646 priv->vadjustment = g_object_ref_sink (adjustment);
2647 gtk_icon_view_set_vadjustment_values (icon_view);
2648
2649 g_object_notify (G_OBJECT (icon_view), property_name: "vadjustment");
2650}
2651
2652static void
2653gtk_icon_view_adjustment_changed (GtkAdjustment *adjustment,
2654 GtkIconView *icon_view)
2655{
2656 GtkWidget *widget = GTK_WIDGET (icon_view);
2657
2658 if (gtk_widget_get_realized (widget))
2659 {
2660 if (icon_view->priv->doing_rubberband)
2661 gtk_icon_view_update_rubberband (icon_view);
2662 }
2663
2664 gtk_widget_queue_draw (GTK_WIDGET (icon_view));
2665}
2666
2667static int
2668compare_sizes (gconstpointer p1,
2669 gconstpointer p2,
2670 gpointer unused)
2671{
2672 return GPOINTER_TO_INT (((const GtkRequestedSize *) p1)->data)
2673 - GPOINTER_TO_INT (((const GtkRequestedSize *) p2)->data);
2674}
2675
2676static void
2677gtk_icon_view_layout (GtkIconView *icon_view)
2678{
2679 GtkIconViewPrivate *priv = icon_view->priv;
2680 GtkWidget *widget = GTK_WIDGET (icon_view);
2681 GList *items;
2682 int item_width = 0; /* this doesn't include item_padding */
2683 int n_columns, n_rows, n_items;
2684 int col, row;
2685 GtkRequestedSize *sizes;
2686 gboolean rtl;
2687 int width, height;
2688
2689 if (gtk_icon_view_is_empty (icon_view))
2690 return;
2691
2692 rtl = gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL;
2693 n_items = gtk_icon_view_get_n_items (icon_view);
2694
2695 width = gtk_widget_get_width (widget);
2696 height = gtk_widget_get_height (widget);
2697
2698 gtk_icon_view_compute_n_items_for_size (icon_view,
2699 orientation: GTK_ORIENTATION_HORIZONTAL,
2700 size: width,
2701 NULL, NULL,
2702 max_items: &n_columns, max_item_size: &item_width);
2703 n_rows = (n_items + n_columns - 1) / n_columns;
2704
2705 priv->width = n_columns * (item_width + 2 * priv->item_padding + priv->column_spacing) - priv->column_spacing;
2706 priv->width += 2 * priv->margin;
2707 priv->width = MAX (priv->width, width);
2708
2709 /* Clear the per row contexts */
2710 g_ptr_array_set_size (array: icon_view->priv->row_contexts, length: 0);
2711
2712 gtk_cell_area_context_reset (context: priv->cell_area_context);
2713 /* because layouting is complicated. We designed an API
2714 * that is O(N²) and nonsensical.
2715 * And we're proud of it. */
2716 for (items = priv->items; items; items = items->next)
2717 {
2718 _gtk_icon_view_set_cell_data (icon_view, item: items->data);
2719 gtk_cell_area_get_preferred_width (area: priv->cell_area,
2720 context: priv->cell_area_context,
2721 widget,
2722 NULL, NULL);
2723 }
2724
2725 sizes = g_newa (GtkRequestedSize, n_rows);
2726 items = priv->items;
2727 priv->height = priv->margin;
2728
2729 /* Collect the heights for all rows */
2730 for (row = 0; row < n_rows; row++)
2731 {
2732 GtkCellAreaContext *context = gtk_cell_area_copy_context (area: priv->cell_area, context: priv->cell_area_context);
2733 g_ptr_array_add (array: priv->row_contexts, data: context);
2734
2735 for (col = 0; col < n_columns && items; col++, items = items->next)
2736 {
2737 GtkIconViewItem *item = items->data;
2738
2739 _gtk_icon_view_set_cell_data (icon_view, item);
2740 gtk_cell_area_get_preferred_height_for_width (area: priv->cell_area,
2741 context,
2742 widget,
2743 width: item_width,
2744 NULL, NULL);
2745 }
2746
2747 sizes[row].data = GINT_TO_POINTER (row);
2748 gtk_cell_area_context_get_preferred_height_for_width (context,
2749 width: item_width,
2750 minimum_height: &sizes[row].minimum_size,
2751 natural_height: &sizes[row].natural_size);
2752 priv->height += sizes[row].minimum_size + 2 * priv->item_padding + priv->row_spacing;
2753 }
2754
2755 priv->height -= priv->row_spacing;
2756 priv->height += priv->margin;
2757 priv->height = MIN (priv->height, height);
2758
2759 gtk_distribute_natural_allocation (extra_space: height - priv->height,
2760 n_requested_sizes: n_rows,
2761 sizes);
2762
2763 /* Actually allocate the rows */
2764 g_qsort_with_data (pbase: sizes, total_elems: n_rows, size: sizeof (GtkRequestedSize), compare_func: compare_sizes, NULL);
2765
2766 items = priv->items;
2767 priv->height = priv->margin;
2768
2769 for (row = 0; row < n_rows; row++)
2770 {
2771 GtkCellAreaContext *context = g_ptr_array_index (priv->row_contexts, row);
2772 gtk_cell_area_context_allocate (context, width: item_width, height: sizes[row].minimum_size);
2773
2774 priv->height += priv->item_padding;
2775
2776 for (col = 0; col < n_columns && items; col++, items = items->next)
2777 {
2778 GtkIconViewItem *item = items->data;
2779
2780 item->cell_area.x = priv->margin + (col * 2 + 1) * priv->item_padding + col * (priv->column_spacing + item_width);
2781 item->cell_area.width = item_width;
2782 item->cell_area.y = priv->height;
2783 item->cell_area.height = sizes[row].minimum_size;
2784 item->row = row;
2785 item->col = col;
2786 if (rtl)
2787 {
2788 item->cell_area.x = priv->width - item_width - item->cell_area.x;
2789 item->col = n_columns - 1 - col;
2790 }
2791 }
2792
2793 priv->height += sizes[row].minimum_size + priv->item_padding + priv->row_spacing;
2794 }
2795
2796 priv->height -= priv->row_spacing;
2797 priv->height += priv->margin;
2798 priv->height = MAX (priv->height, height);
2799}
2800
2801static void
2802gtk_icon_view_invalidate_sizes (GtkIconView *icon_view)
2803{
2804 /* Clear all item sizes */
2805 g_list_foreach (list: icon_view->priv->items,
2806 func: (GFunc)gtk_icon_view_item_invalidate_size, NULL);
2807
2808 /* Re-layout the items */
2809 gtk_widget_queue_resize (GTK_WIDGET (icon_view));
2810}
2811
2812static void
2813gtk_icon_view_item_invalidate_size (GtkIconViewItem *item)
2814{
2815 item->cell_area.width = -1;
2816 item->cell_area.height = -1;
2817}
2818
2819static void
2820gtk_icon_view_snapshot_item (GtkIconView *icon_view,
2821 GtkSnapshot *snapshot,
2822 GtkIconViewItem *item,
2823 int x,
2824 int y,
2825 gboolean draw_focus)
2826{
2827 GdkRectangle cell_area;
2828 GtkStateFlags state = 0;
2829 GtkCellRendererState flags = 0;
2830 GtkStyleContext *style_context;
2831 GtkWidget *widget = GTK_WIDGET (icon_view);
2832 GtkIconViewPrivate *priv = icon_view->priv;
2833 GtkCellAreaContext *context;
2834
2835 if (priv->model == NULL || item->cell_area.width <= 0 || item->cell_area.height <= 0)
2836 return;
2837
2838 _gtk_icon_view_set_cell_data (icon_view, item);
2839
2840 style_context = gtk_widget_get_style_context (widget);
2841 state = gtk_widget_get_state_flags (widget);
2842
2843 gtk_style_context_save (context: style_context);
2844 gtk_style_context_add_class (context: style_context, class_name: "cell");
2845
2846 state &= ~(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_PRELIGHT);
2847
2848 if ((state & GTK_STATE_FLAG_FOCUSED) &&
2849 item == icon_view->priv->cursor_item)
2850 flags |= GTK_CELL_RENDERER_FOCUSED;
2851
2852 if (item->selected)
2853 {
2854 state |= GTK_STATE_FLAG_SELECTED;
2855 flags |= GTK_CELL_RENDERER_SELECTED;
2856 }
2857
2858 if (item == priv->last_prelight)
2859 {
2860 state |= GTK_STATE_FLAG_PRELIGHT;
2861 flags |= GTK_CELL_RENDERER_PRELIT;
2862 }
2863
2864 gtk_style_context_set_state (context: style_context, flags: state);
2865
2866 gtk_snapshot_render_background (snapshot, context: style_context,
2867 x: x - priv->item_padding,
2868 y: y - priv->item_padding,
2869 width: item->cell_area.width + priv->item_padding * 2,
2870 height: item->cell_area.height + priv->item_padding * 2);
2871 gtk_snapshot_render_frame (snapshot, context: style_context,
2872 x: x - priv->item_padding,
2873 y: y - priv->item_padding,
2874 width: item->cell_area.width + priv->item_padding * 2,
2875 height: item->cell_area.height + priv->item_padding * 2);
2876
2877 cell_area.x = x;
2878 cell_area.y = y;
2879 cell_area.width = item->cell_area.width;
2880 cell_area.height = item->cell_area.height;
2881
2882 context = g_ptr_array_index (priv->row_contexts, item->row);
2883 gtk_cell_area_snapshot (area: priv->cell_area, context,
2884 widget, snapshot, background_area: &cell_area, cell_area: &cell_area, flags,
2885 paint_focus: draw_focus);
2886
2887 gtk_style_context_restore (context: style_context);
2888}
2889
2890static void
2891gtk_icon_view_snapshot_rubberband (GtkIconView *icon_view,
2892 GtkSnapshot *snapshot)
2893{
2894 GtkIconViewPrivate *priv = icon_view->priv;
2895 GtkStyleContext *context;
2896 GdkRectangle rect;
2897
2898 rect.x = MIN (priv->rubberband_x1, priv->rubberband_x2);
2899 rect.y = MIN (priv->rubberband_y1, priv->rubberband_y2);
2900 rect.width = ABS (priv->rubberband_x1 - priv->rubberband_x2) + 1;
2901 rect.height = ABS (priv->rubberband_y1 - priv->rubberband_y2) + 1;
2902
2903 context = gtk_widget_get_style_context (GTK_WIDGET (icon_view));
2904
2905 gtk_style_context_save_to_node (context, node: priv->rubberband_node);
2906
2907 gtk_snapshot_render_background (snapshot, context,
2908 x: rect.x, y: rect.y,
2909 width: rect.width, height: rect.height);
2910 gtk_snapshot_render_frame (snapshot, context,
2911 x: rect.x, y: rect.y,
2912 width: rect.width, height: rect.height);
2913
2914 gtk_style_context_restore (context);
2915}
2916
2917static void
2918gtk_icon_view_queue_draw_path (GtkIconView *icon_view,
2919 GtkTreePath *path)
2920{
2921 GList *l;
2922 int index;
2923
2924 index = gtk_tree_path_get_indices (path)[0];
2925
2926 for (l = icon_view->priv->items; l; l = l->next)
2927 {
2928 GtkIconViewItem *item = l->data;
2929
2930 if (item->index == index)
2931 {
2932 gtk_icon_view_queue_draw_item (icon_view, item);
2933 break;
2934 }
2935 }
2936}
2937
2938static void
2939gtk_icon_view_queue_draw_item (GtkIconView *icon_view,
2940 GtkIconViewItem *item)
2941{
2942 gtk_widget_queue_draw (GTK_WIDGET (icon_view));
2943}
2944
2945void
2946_gtk_icon_view_set_cursor_item (GtkIconView *icon_view,
2947 GtkIconViewItem *item,
2948 GtkCellRenderer *cursor_cell)
2949{
2950 /* When hitting this path from keynav, the focus cell is already set,
2951 * but we still need to queue the draw here (in the case that the focus
2952 * cell changes but not the cursor item).
2953 */
2954 gtk_icon_view_queue_draw_item (icon_view, item);
2955
2956 if (icon_view->priv->cursor_item == item &&
2957 (cursor_cell == NULL || cursor_cell == gtk_cell_area_get_focus_cell (area: icon_view->priv->cell_area)))
2958 return;
2959
2960 if (icon_view->priv->cursor_item != NULL)
2961 gtk_icon_view_queue_draw_item (icon_view, item: icon_view->priv->cursor_item);
2962
2963 icon_view->priv->cursor_item = item;
2964
2965 if (cursor_cell)
2966 gtk_cell_area_set_focus_cell (area: icon_view->priv->cell_area, renderer: cursor_cell);
2967 else
2968 {
2969 /* Make sure there is a cell in focus initially */
2970 if (!gtk_cell_area_get_focus_cell (area: icon_view->priv->cell_area))
2971 gtk_cell_area_focus (area: icon_view->priv->cell_area, direction: GTK_DIR_TAB_FORWARD);
2972 }
2973}
2974
2975
2976static GtkIconViewItem *
2977gtk_icon_view_item_new (void)
2978{
2979 GtkIconViewItem *item;
2980
2981 item = g_slice_new0 (GtkIconViewItem);
2982
2983 item->cell_area.width = -1;
2984 item->cell_area.height = -1;
2985
2986 return item;
2987}
2988
2989static void
2990gtk_icon_view_item_free (GtkIconViewItem *item)
2991{
2992 g_return_if_fail (item != NULL);
2993
2994 g_slice_free (GtkIconViewItem, item);
2995}
2996
2997GtkIconViewItem *
2998_gtk_icon_view_get_item_at_coords (GtkIconView *icon_view,
2999 int x,
3000 int y,
3001 gboolean only_in_cell,
3002 GtkCellRenderer **cell_at_pos)
3003{
3004 GList *items;
3005
3006 if (cell_at_pos)
3007 *cell_at_pos = NULL;
3008
3009 for (items = icon_view->priv->items; items; items = items->next)
3010 {
3011 GtkIconViewItem *item = items->data;
3012 GdkRectangle *item_area = &item->cell_area;
3013
3014 if (x >= item_area->x - icon_view->priv->column_spacing/2 &&
3015 x <= item_area->x + item_area->width + icon_view->priv->column_spacing/2 &&
3016 y >= item_area->y - icon_view->priv->row_spacing/2 &&
3017 y <= item_area->y + item_area->height + icon_view->priv->row_spacing/2)
3018 {
3019 if (only_in_cell || cell_at_pos)
3020 {
3021 GtkCellRenderer *cell = NULL;
3022 GtkCellAreaContext *context;
3023
3024 context = g_ptr_array_index (icon_view->priv->row_contexts, item->row);
3025 _gtk_icon_view_set_cell_data (icon_view, item);
3026
3027 if (x >= item_area->x && x <= item_area->x + item_area->width &&
3028 y >= item_area->y && y <= item_area->y + item_area->height)
3029 cell = gtk_cell_area_get_cell_at_position (area: icon_view->priv->cell_area, context,
3030 GTK_WIDGET (icon_view),
3031 cell_area: item_area,
3032 x, y, NULL);
3033
3034 if (cell_at_pos)
3035 *cell_at_pos = cell;
3036
3037 if (only_in_cell)
3038 return cell != NULL ? item : NULL;
3039 else
3040 return item;
3041 }
3042 return item;
3043 }
3044 }
3045 return NULL;
3046}
3047
3048void
3049_gtk_icon_view_select_item (GtkIconView *icon_view,
3050 GtkIconViewItem *item)
3051{
3052 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
3053 g_return_if_fail (item != NULL);
3054
3055 if (item->selected)
3056 return;
3057
3058 if (icon_view->priv->selection_mode == GTK_SELECTION_NONE)
3059 return;
3060 else if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
3061 gtk_icon_view_unselect_all_internal (icon_view);
3062
3063 item->selected = TRUE;
3064
3065 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
3066
3067 gtk_icon_view_queue_draw_item (icon_view, item);
3068}
3069
3070
3071void
3072_gtk_icon_view_unselect_item (GtkIconView *icon_view,
3073 GtkIconViewItem *item)
3074{
3075 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
3076 g_return_if_fail (item != NULL);
3077
3078 if (!item->selected)
3079 return;
3080
3081 if (icon_view->priv->selection_mode == GTK_SELECTION_NONE ||
3082 icon_view->priv->selection_mode == GTK_SELECTION_BROWSE)
3083 return;
3084
3085 item->selected = FALSE;
3086
3087 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
3088
3089 gtk_icon_view_queue_draw_item (icon_view, item);
3090}
3091
3092static void
3093verify_items (GtkIconView *icon_view)
3094{
3095 GList *items;
3096 int i = 0;
3097
3098 for (items = icon_view->priv->items; items; items = items->next)
3099 {
3100 GtkIconViewItem *item = items->data;
3101
3102 if (item->index != i)
3103 g_error ("List item does not match its index: "
3104 "item index %d and list index %d\n", item->index, i);
3105
3106 i++;
3107 }
3108}
3109
3110static void
3111gtk_icon_view_row_changed (GtkTreeModel *model,
3112 GtkTreePath *path,
3113 GtkTreeIter *iter,
3114 gpointer data)
3115{
3116 GtkIconView *icon_view = GTK_ICON_VIEW (data);
3117
3118 /* ignore changes in branches */
3119 if (gtk_tree_path_get_depth (path) > 1)
3120 return;
3121
3122 /* An icon view subclass might add it's own model and populate
3123 * things at init() time instead of waiting for the constructor()
3124 * to be called
3125 */
3126 if (icon_view->priv->cell_area)
3127 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
3128
3129 /* Here we can use a "grow-only" strategy for optimization
3130 * and only invalidate a single item and queue a relayout
3131 * instead of invalidating the whole thing.
3132 *
3133 * For now GtkIconView still can't deal with huge models
3134 * so just invalidate the whole thing when the model
3135 * changes.
3136 */
3137 gtk_icon_view_invalidate_sizes (icon_view);
3138
3139 verify_items (icon_view);
3140}
3141
3142static void
3143gtk_icon_view_row_inserted (GtkTreeModel *model,
3144 GtkTreePath *path,
3145 GtkTreeIter *iter,
3146 gpointer data)
3147{
3148 GtkIconView *icon_view = GTK_ICON_VIEW (data);
3149 int index;
3150 GtkIconViewItem *item;
3151 GList *list;
3152
3153 /* ignore changes in branches */
3154 if (gtk_tree_path_get_depth (path) > 1)
3155 return;
3156
3157 gtk_tree_model_ref_node (tree_model: model, iter);
3158
3159 index = gtk_tree_path_get_indices(path)[0];
3160
3161 item = gtk_icon_view_item_new ();
3162
3163 item->index = index;
3164
3165 /* FIXME: We can be more efficient here,
3166 we can store a tail pointer and use that when
3167 appending (which is a rather common operation)
3168 */
3169 icon_view->priv->items = g_list_insert (list: icon_view->priv->items,
3170 data: item, position: index);
3171
3172 list = g_list_nth (list: icon_view->priv->items, n: index + 1);
3173 for (; list; list = list->next)
3174 {
3175 item = list->data;
3176
3177 item->index++;
3178 }
3179
3180 verify_items (icon_view);
3181
3182 gtk_widget_queue_resize (GTK_WIDGET (icon_view));
3183}
3184
3185static void
3186gtk_icon_view_row_deleted (GtkTreeModel *model,
3187 GtkTreePath *path,
3188 gpointer data)
3189{
3190 GtkIconView *icon_view = GTK_ICON_VIEW (data);
3191 int index;
3192 GtkIconViewItem *item;
3193 GList *list, *next;
3194 gboolean emit = FALSE;
3195 GtkTreeIter iter;
3196
3197 /* ignore changes in branches */
3198 if (gtk_tree_path_get_depth (path) > 1)
3199 return;
3200
3201 if (gtk_tree_model_get_iter (tree_model: model, iter: &iter, path))
3202 gtk_tree_model_unref_node (tree_model: model, iter: &iter);
3203
3204 index = gtk_tree_path_get_indices(path)[0];
3205
3206 list = g_list_nth (list: icon_view->priv->items, n: index);
3207 item = list->data;
3208
3209 if (icon_view->priv->cell_area)
3210 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
3211
3212 if (item == icon_view->priv->anchor_item)
3213 icon_view->priv->anchor_item = NULL;
3214
3215 if (item == icon_view->priv->cursor_item)
3216 icon_view->priv->cursor_item = NULL;
3217
3218 if (item == icon_view->priv->last_prelight)
3219 icon_view->priv->last_prelight = NULL;
3220
3221 if (item->selected)
3222 emit = TRUE;
3223
3224 gtk_icon_view_item_free (item);
3225
3226 for (next = list->next; next; next = next->next)
3227 {
3228 item = next->data;
3229
3230 item->index--;
3231 }
3232
3233 icon_view->priv->items = g_list_delete_link (list: icon_view->priv->items, link_: list);
3234
3235 verify_items (icon_view);
3236
3237 gtk_widget_queue_resize (GTK_WIDGET (icon_view));
3238
3239 if (emit)
3240 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
3241}
3242
3243static void
3244gtk_icon_view_rows_reordered (GtkTreeModel *model,
3245 GtkTreePath *parent,
3246 GtkTreeIter *iter,
3247 int *new_order,
3248 gpointer data)
3249{
3250 GtkIconView *icon_view = GTK_ICON_VIEW (data);
3251 int i;
3252 int length;
3253 GList *items = NULL, *list;
3254 GtkIconViewItem **item_array;
3255 int *order;
3256
3257 /* ignore changes in branches */
3258 if (iter != NULL)
3259 return;
3260
3261 if (icon_view->priv->cell_area)
3262 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
3263
3264 length = gtk_tree_model_iter_n_children (tree_model: model, NULL);
3265
3266 order = g_new (int, length);
3267 for (i = 0; i < length; i++)
3268 order [new_order[i]] = i;
3269
3270 item_array = g_new (GtkIconViewItem *, length);
3271 for (i = 0, list = icon_view->priv->items; list != NULL; list = list->next, i++)
3272 item_array[order[i]] = list->data;
3273 g_free (mem: order);
3274
3275 for (i = length - 1; i >= 0; i--)
3276 {
3277 item_array[i]->index = i;
3278 items = g_list_prepend (list: items, data: item_array[i]);
3279 }
3280
3281 g_free (mem: item_array);
3282 g_list_free (list: icon_view->priv->items);
3283 icon_view->priv->items = items;
3284
3285 gtk_widget_queue_resize (GTK_WIDGET (icon_view));
3286
3287 verify_items (icon_view);
3288}
3289
3290static void
3291gtk_icon_view_build_items (GtkIconView *icon_view)
3292{
3293 GtkTreeIter iter;
3294 int i;
3295 GList *items = NULL;
3296
3297 if (!gtk_tree_model_get_iter_first (tree_model: icon_view->priv->model,
3298 iter: &iter))
3299 return;
3300
3301 i = 0;
3302
3303 do
3304 {
3305 GtkIconViewItem *item = gtk_icon_view_item_new ();
3306
3307 item->index = i;
3308
3309 i++;
3310
3311 items = g_list_prepend (list: items, data: item);
3312
3313 } while (gtk_tree_model_iter_next (tree_model: icon_view->priv->model, iter: &iter));
3314
3315 icon_view->priv->items = g_list_reverse (list: items);
3316}
3317
3318static void
3319gtk_icon_view_add_move_binding (GtkWidgetClass *widget_class,
3320 guint keyval,
3321 guint modmask,
3322 GtkMovementStep step,
3323 int count)
3324{
3325
3326 gtk_widget_class_add_binding_signal (widget_class,
3327 keyval, mods: modmask,
3328 I_("move-cursor"),
3329 format_string: "(iibb)", step, count, FALSE, FALSE);
3330
3331 gtk_widget_class_add_binding_signal (widget_class,
3332 keyval, mods: GDK_SHIFT_MASK,
3333 signal: "move-cursor",
3334 format_string: "(iibb)", step, count, TRUE, FALSE);
3335
3336 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
3337 return;
3338
3339 gtk_widget_class_add_binding_signal (widget_class,
3340 keyval, mods: GDK_CONTROL_MASK | GDK_SHIFT_MASK,
3341 signal: "move-cursor",
3342 format_string: "(iibb)", step, count, TRUE, TRUE);
3343
3344 gtk_widget_class_add_binding_signal (widget_class,
3345 keyval, mods: GDK_CONTROL_MASK,
3346 signal: "move-cursor",
3347 format_string: "(iibb)", step, count, FALSE, TRUE);
3348}
3349
3350static gboolean
3351gtk_icon_view_real_move_cursor (GtkIconView *icon_view,
3352 GtkMovementStep step,
3353 int count,
3354 gboolean extend,
3355 gboolean modify)
3356{
3357 g_return_val_if_fail (GTK_ICON_VIEW (icon_view), FALSE);
3358 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
3359 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
3360 step == GTK_MOVEMENT_DISPLAY_LINES ||
3361 step == GTK_MOVEMENT_PAGES ||
3362 step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
3363
3364 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
3365 return FALSE;
3366
3367 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, FALSE);
3368 gtk_widget_grab_focus (GTK_WIDGET (icon_view));
3369
3370 icon_view->priv->extend_selection_pressed = extend;
3371 icon_view->priv->modify_selection_pressed = modify;
3372
3373 switch (step)
3374 {
3375 case GTK_MOVEMENT_LOGICAL_POSITIONS:
3376 case GTK_MOVEMENT_VISUAL_POSITIONS:
3377 gtk_icon_view_move_cursor_left_right (icon_view, count);
3378 break;
3379 case GTK_MOVEMENT_DISPLAY_LINES:
3380 gtk_icon_view_move_cursor_up_down (icon_view, count);
3381 break;
3382 case GTK_MOVEMENT_PAGES:
3383 gtk_icon_view_move_cursor_page_up_down (icon_view, count);
3384 break;
3385 case GTK_MOVEMENT_BUFFER_ENDS:
3386 gtk_icon_view_move_cursor_start_end (icon_view, count);
3387 break;
3388 case GTK_MOVEMENT_WORDS:
3389 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
3390 case GTK_MOVEMENT_PARAGRAPHS:
3391 case GTK_MOVEMENT_PARAGRAPH_ENDS:
3392 case GTK_MOVEMENT_HORIZONTAL_PAGES:
3393 default:
3394 g_assert_not_reached ();
3395 break;
3396 }
3397
3398 icon_view->priv->modify_selection_pressed = FALSE;
3399 icon_view->priv->extend_selection_pressed = FALSE;
3400
3401 icon_view->priv->draw_focus = TRUE;
3402
3403 return TRUE;
3404}
3405
3406static GtkIconViewItem *
3407find_item (GtkIconView *icon_view,
3408 GtkIconViewItem *current,
3409 int row_ofs,
3410 int col_ofs)
3411{
3412 int row, col;
3413 GList *items;
3414 GtkIconViewItem *item;
3415
3416 /* FIXME: this could be more efficient
3417 */
3418 row = current->row + row_ofs;
3419 col = current->col + col_ofs;
3420
3421 for (items = icon_view->priv->items; items; items = items->next)
3422 {
3423 item = items->data;
3424 if (item->row == row && item->col == col)
3425 return item;
3426 }
3427
3428 return NULL;
3429}
3430
3431static GtkIconViewItem *
3432find_item_page_up_down (GtkIconView *icon_view,
3433 GtkIconViewItem *current,
3434 int count)
3435{
3436 GList *item, *next;
3437 int y, col;
3438
3439 col = current->col;
3440 y = current->cell_area.y + count * gtk_adjustment_get_page_size (adjustment: icon_view->priv->vadjustment);
3441
3442 item = g_list_find (list: icon_view->priv->items, data: current);
3443 if (count > 0)
3444 {
3445 while (item)
3446 {
3447 for (next = item->next; next; next = next->next)
3448 {
3449 if (((GtkIconViewItem *)next->data)->col == col)
3450 break;
3451 }
3452 if (!next || ((GtkIconViewItem *)next->data)->cell_area.y > y)
3453 break;
3454
3455 item = next;
3456 }
3457 }
3458 else
3459 {
3460 while (item)
3461 {
3462 for (next = item->prev; next; next = next->prev)
3463 {
3464 if (((GtkIconViewItem *)next->data)->col == col)
3465 break;
3466 }
3467 if (!next || ((GtkIconViewItem *)next->data)->cell_area.y < y)
3468 break;
3469
3470 item = next;
3471 }
3472 }
3473
3474 if (item)
3475 return item->data;
3476
3477 return NULL;
3478}
3479
3480static gboolean
3481gtk_icon_view_select_all_between (GtkIconView *icon_view,
3482 GtkIconViewItem *anchor,
3483 GtkIconViewItem *cursor)
3484{
3485 GList *items;
3486 GtkIconViewItem *item;
3487 int row1, row2, col1, col2;
3488 gboolean dirty = FALSE;
3489
3490 if (anchor->row < cursor->row)
3491 {
3492 row1 = anchor->row;
3493 row2 = cursor->row;
3494 }
3495 else
3496 {
3497 row1 = cursor->row;
3498 row2 = anchor->row;
3499 }
3500
3501 if (anchor->col < cursor->col)
3502 {
3503 col1 = anchor->col;
3504 col2 = cursor->col;
3505 }
3506 else
3507 {
3508 col1 = cursor->col;
3509 col2 = anchor->col;
3510 }
3511
3512 for (items = icon_view->priv->items; items; items = items->next)
3513 {
3514 item = items->data;
3515
3516 if (row1 <= item->row && item->row <= row2 &&
3517 col1 <= item->col && item->col <= col2)
3518 {
3519 if (!item->selected)
3520 {
3521 dirty = TRUE;
3522 item->selected = TRUE;
3523 }
3524 gtk_icon_view_queue_draw_item (icon_view, item);
3525 }
3526 }
3527
3528 return dirty;
3529}
3530
3531static void
3532gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view,
3533 int count)
3534{
3535 GtkIconViewItem *item;
3536 GtkCellRenderer *cell = NULL;
3537 gboolean dirty = FALSE;
3538 int step;
3539 GtkDirectionType direction;
3540
3541 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
3542 return;
3543
3544 direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN;
3545
3546 if (!icon_view->priv->cursor_item)
3547 {
3548 GList *list;
3549
3550 if (count > 0)
3551 list = icon_view->priv->items;
3552 else
3553 list = g_list_last (list: icon_view->priv->items);
3554
3555 if (list)
3556 {
3557 item = list->data;
3558
3559 /* Give focus to the first cell initially */
3560 _gtk_icon_view_set_cell_data (icon_view, item);
3561 gtk_cell_area_focus (area: icon_view->priv->cell_area, direction);
3562 }
3563 else
3564 {
3565 item = NULL;
3566 }
3567 }
3568 else
3569 {
3570 item = icon_view->priv->cursor_item;
3571 step = count > 0 ? 1 : -1;
3572
3573 /* Save the current focus cell in case we hit the edge */
3574 cell = gtk_cell_area_get_focus_cell (area: icon_view->priv->cell_area);
3575
3576 while (item)
3577 {
3578 _gtk_icon_view_set_cell_data (icon_view, item);
3579
3580 if (gtk_cell_area_focus (area: icon_view->priv->cell_area, direction))
3581 break;
3582
3583 item = find_item (icon_view, current: item, row_ofs: step, col_ofs: 0);
3584 }
3585 }
3586
3587 if (!item)
3588 {
3589 if (!gtk_widget_keynav_failed (GTK_WIDGET (icon_view), direction))
3590 {
3591 GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (icon_view)));
3592 if (toplevel)
3593 gtk_widget_child_focus (widget: toplevel,
3594 direction: direction == GTK_DIR_UP ?
3595 GTK_DIR_TAB_BACKWARD :
3596 GTK_DIR_TAB_FORWARD);
3597
3598 }
3599
3600 gtk_cell_area_set_focus_cell (area: icon_view->priv->cell_area, renderer: cell);
3601 return;
3602 }
3603
3604 if (icon_view->priv->modify_selection_pressed ||
3605 !icon_view->priv->extend_selection_pressed ||
3606 !icon_view->priv->anchor_item ||
3607 icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
3608 icon_view->priv->anchor_item = item;
3609
3610 cell = gtk_cell_area_get_focus_cell (area: icon_view->priv->cell_area);
3611 _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell: cell);
3612
3613 if (!icon_view->priv->modify_selection_pressed &&
3614 icon_view->priv->selection_mode != GTK_SELECTION_NONE)
3615 {
3616 dirty = gtk_icon_view_unselect_all_internal (icon_view);
3617 dirty = gtk_icon_view_select_all_between (icon_view,
3618 anchor: icon_view->priv->anchor_item,
3619 cursor: item) || dirty;
3620 }
3621
3622 gtk_icon_view_scroll_to_item (icon_view, item);
3623
3624 if (dirty)
3625 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
3626}
3627
3628static void
3629gtk_icon_view_move_cursor_page_up_down (GtkIconView *icon_view,
3630 int count)
3631{
3632 GtkIconViewItem *item;
3633 gboolean dirty = FALSE;
3634
3635 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
3636 return;
3637
3638 if (!icon_view->priv->cursor_item)
3639 {
3640 GList *list;
3641
3642 if (count > 0)
3643 list = icon_view->priv->items;
3644 else
3645 list = g_list_last (list: icon_view->priv->items);
3646
3647 item = list ? list->data : NULL;
3648 }
3649 else
3650 item = find_item_page_up_down (icon_view,
3651 current: icon_view->priv->cursor_item,
3652 count);
3653
3654 if (item == icon_view->priv->cursor_item)
3655 gtk_widget_error_bell (GTK_WIDGET (icon_view));
3656
3657 if (!item)
3658 return;
3659
3660 if (icon_view->priv->modify_selection_pressed ||
3661 !icon_view->priv->extend_selection_pressed ||
3662 !icon_view->priv->anchor_item ||
3663 icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
3664 icon_view->priv->anchor_item = item;
3665
3666 _gtk_icon_view_set_cursor_item (icon_view, item, NULL);
3667
3668 if (!icon_view->priv->modify_selection_pressed &&
3669 icon_view->priv->selection_mode != GTK_SELECTION_NONE)
3670 {
3671 dirty = gtk_icon_view_unselect_all_internal (icon_view);
3672 dirty = gtk_icon_view_select_all_between (icon_view,
3673 anchor: icon_view->priv->anchor_item,
3674 cursor: item) || dirty;
3675 }
3676
3677 gtk_icon_view_scroll_to_item (icon_view, item);
3678
3679 if (dirty)
3680 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
3681}
3682
3683static void
3684gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view,
3685 int count)
3686{
3687 GtkIconViewItem *item;
3688 GtkCellRenderer *cell = NULL;
3689 gboolean dirty = FALSE;
3690 int step;
3691 GtkDirectionType direction;
3692
3693 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
3694 return;
3695
3696 direction = count < 0 ? GTK_DIR_LEFT : GTK_DIR_RIGHT;
3697
3698 if (!icon_view->priv->cursor_item)
3699 {
3700 GList *list;
3701
3702 if (count > 0)
3703 list = icon_view->priv->items;
3704 else
3705 list = g_list_last (list: icon_view->priv->items);
3706
3707 if (list)
3708 {
3709 item = list->data;
3710
3711 /* Give focus to the first cell initially */
3712 _gtk_icon_view_set_cell_data (icon_view, item);
3713 gtk_cell_area_focus (area: icon_view->priv->cell_area, direction);
3714 }
3715 else
3716 {
3717 item = NULL;
3718 }
3719 }
3720 else
3721 {
3722 item = icon_view->priv->cursor_item;
3723 step = count > 0 ? 1 : -1;
3724
3725 /* Save the current focus cell in case we hit the edge */
3726 cell = gtk_cell_area_get_focus_cell (area: icon_view->priv->cell_area);
3727
3728 while (item)
3729 {
3730 _gtk_icon_view_set_cell_data (icon_view, item);
3731
3732 if (gtk_cell_area_focus (area: icon_view->priv->cell_area, direction))
3733 break;
3734
3735 item = find_item (icon_view, current: item, row_ofs: 0, col_ofs: step);
3736 }
3737 }
3738
3739 if (!item)
3740 {
3741 if (!gtk_widget_keynav_failed (GTK_WIDGET (icon_view), direction))
3742 {
3743 GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (icon_view)));
3744 if (toplevel)
3745 gtk_widget_child_focus (widget: toplevel,
3746 direction: direction == GTK_DIR_LEFT ?
3747 GTK_DIR_TAB_BACKWARD :
3748 GTK_DIR_TAB_FORWARD);
3749
3750 }
3751
3752 gtk_cell_area_set_focus_cell (area: icon_view->priv->cell_area, renderer: cell);
3753 return;
3754 }
3755
3756 if (icon_view->priv->modify_selection_pressed ||
3757 !icon_view->priv->extend_selection_pressed ||
3758 !icon_view->priv->anchor_item ||
3759 icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
3760 icon_view->priv->anchor_item = item;
3761
3762 cell = gtk_cell_area_get_focus_cell (area: icon_view->priv->cell_area);
3763 _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell: cell);
3764
3765 if (!icon_view->priv->modify_selection_pressed &&
3766 icon_view->priv->selection_mode != GTK_SELECTION_NONE)
3767 {
3768 dirty = gtk_icon_view_unselect_all_internal (icon_view);
3769 dirty = gtk_icon_view_select_all_between (icon_view,
3770 anchor: icon_view->priv->anchor_item,
3771 cursor: item) || dirty;
3772 }
3773
3774 gtk_icon_view_scroll_to_item (icon_view, item);
3775
3776 if (dirty)
3777 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
3778}
3779
3780static void
3781gtk_icon_view_move_cursor_start_end (GtkIconView *icon_view,
3782 int count)
3783{
3784 GtkIconViewItem *item;
3785 GList *list;
3786 gboolean dirty = FALSE;
3787
3788 if (!gtk_widget_has_focus (GTK_WIDGET (icon_view)))
3789 return;
3790
3791 if (count < 0)
3792 list = icon_view->priv->items;
3793 else
3794 list = g_list_last (list: icon_view->priv->items);
3795
3796 item = list ? list->data : NULL;
3797
3798 if (item == icon_view->priv->cursor_item)
3799 gtk_widget_error_bell (GTK_WIDGET (icon_view));
3800
3801 if (!item)
3802 return;
3803
3804 if (icon_view->priv->modify_selection_pressed ||
3805 !icon_view->priv->extend_selection_pressed ||
3806 !icon_view->priv->anchor_item ||
3807 icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
3808 icon_view->priv->anchor_item = item;
3809
3810 _gtk_icon_view_set_cursor_item (icon_view, item, NULL);
3811
3812 if (!icon_view->priv->modify_selection_pressed &&
3813 icon_view->priv->selection_mode != GTK_SELECTION_NONE)
3814 {
3815 dirty = gtk_icon_view_unselect_all_internal (icon_view);
3816 dirty = gtk_icon_view_select_all_between (icon_view,
3817 anchor: icon_view->priv->anchor_item,
3818 cursor: item) || dirty;
3819 }
3820
3821 gtk_icon_view_scroll_to_item (icon_view, item);
3822
3823 if (dirty)
3824 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
3825}
3826
3827/**
3828 * gtk_icon_view_scroll_to_path:
3829 * @icon_view: A `GtkIconView`
3830 * @path: The path of the item to move to.
3831 * @use_align: whether to use alignment arguments, or %FALSE.
3832 * @row_align: The vertical alignment of the item specified by @path.
3833 * @col_align: The horizontal alignment of the item specified by @path.
3834 *
3835 * Moves the alignments of @icon_view to the position specified by @path.
3836 * @row_align determines where the row is placed, and @col_align determines
3837 * where @column is placed. Both are expected to be between 0.0 and 1.0.
3838 * 0.0 means left/top alignment, 1.0 means right/bottom alignment, 0.5 means
3839 * center.
3840 *
3841 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
3842 * tree does the minimum amount of work to scroll the item onto the screen.
3843 * This means that the item will be scrolled to the edge closest to its current
3844 * position. If the item is currently visible on the screen, nothing is done.
3845 *
3846 * This function only works if the model is set, and @path is a valid row on
3847 * the model. If the model changes before the @icon_view is realized, the
3848 * centered path will be modified to reflect this change.
3849 **/
3850void
3851gtk_icon_view_scroll_to_path (GtkIconView *icon_view,
3852 GtkTreePath *path,
3853 gboolean use_align,
3854 float row_align,
3855 float col_align)
3856{
3857 GtkIconViewItem *item = NULL;
3858 GtkWidget *widget;
3859
3860 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
3861 g_return_if_fail (path != NULL);
3862 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
3863 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
3864
3865 widget = GTK_WIDGET (icon_view);
3866
3867 if (gtk_tree_path_get_depth (path) > 0)
3868 item = g_list_nth_data (list: icon_view->priv->items,
3869 n: gtk_tree_path_get_indices(path)[0]);
3870
3871 if (!item || item->cell_area.width < 0 ||
3872 !gtk_widget_get_realized (widget))
3873 {
3874 if (icon_view->priv->scroll_to_path)
3875 gtk_tree_row_reference_free (reference: icon_view->priv->scroll_to_path);
3876
3877 icon_view->priv->scroll_to_path = NULL;
3878
3879 if (path)
3880 icon_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view), model: icon_view->priv->model, path);
3881
3882 icon_view->priv->scroll_to_use_align = use_align;
3883 icon_view->priv->scroll_to_row_align = row_align;
3884 icon_view->priv->scroll_to_col_align = col_align;
3885
3886 return;
3887 }
3888
3889 if (use_align)
3890 {
3891 int width, height;
3892 int x, y;
3893 float offset;
3894 GdkRectangle item_area =
3895 {
3896 item->cell_area.x - icon_view->priv->item_padding,
3897 item->cell_area.y - icon_view->priv->item_padding,
3898 item->cell_area.width + icon_view->priv->item_padding * 2,
3899 item->cell_area.height + icon_view->priv->item_padding * 2
3900 };
3901
3902 x =0;
3903 y =0;
3904
3905 width = gtk_widget_get_width (widget);
3906 height = gtk_widget_get_height (widget);
3907
3908 offset = y + item_area.y - row_align * (height - item_area.height);
3909
3910 gtk_adjustment_set_value (adjustment: icon_view->priv->vadjustment,
3911 value: gtk_adjustment_get_value (adjustment: icon_view->priv->vadjustment) + offset);
3912
3913 offset = x + item_area.x - col_align * (width - item_area.width);
3914
3915 gtk_adjustment_set_value (adjustment: icon_view->priv->hadjustment,
3916 value: gtk_adjustment_get_value (adjustment: icon_view->priv->hadjustment) + offset);
3917 }
3918 else
3919 gtk_icon_view_scroll_to_item (icon_view, item);
3920}
3921
3922
3923static void
3924gtk_icon_view_scroll_to_item (GtkIconView *icon_view,
3925 GtkIconViewItem *item)
3926{
3927 GtkIconViewPrivate *priv = icon_view->priv;
3928 GtkWidget *widget = GTK_WIDGET (icon_view);
3929 GtkAdjustment *hadj, *vadj;
3930 int widget_width, widget_height;
3931 int x, y;
3932 GdkRectangle item_area;
3933
3934 item_area.x = item->cell_area.x - priv->item_padding;
3935 item_area.y = item->cell_area.y - priv->item_padding;
3936 item_area.width = item->cell_area.width + priv->item_padding * 2;
3937 item_area.height = item->cell_area.height + priv->item_padding * 2;
3938
3939 widget_width = gtk_widget_get_width (widget);
3940 widget_height = gtk_widget_get_height (widget);
3941
3942 hadj = icon_view->priv->hadjustment;
3943 vadj = icon_view->priv->vadjustment;
3944
3945 x = - gtk_adjustment_get_value (adjustment: hadj);
3946 y = - gtk_adjustment_get_value (adjustment: vadj);
3947
3948 if (y + item_area.y < 0)
3949 gtk_adjustment_animate_to_value (adjustment: vadj,
3950 value: gtk_adjustment_get_value (adjustment: vadj)
3951 + y + item_area.y);
3952 else if (y + item_area.y + item_area.height > widget_height)
3953 gtk_adjustment_animate_to_value (adjustment: vadj,
3954 value: gtk_adjustment_get_value (adjustment: vadj)
3955 + y + item_area.y + item_area.height - widget_height);
3956
3957 if (x + item_area.x < 0)
3958 gtk_adjustment_animate_to_value (adjustment: hadj,
3959 value: gtk_adjustment_get_value (adjustment: hadj)
3960 + x + item_area.x);
3961 else if (x + item_area.x + item_area.width > widget_width)
3962 gtk_adjustment_animate_to_value (adjustment: hadj,
3963 value: gtk_adjustment_get_value (adjustment: hadj)
3964 + x + item_area.x + item_area.width - widget_width);
3965}
3966
3967/* GtkCellLayout implementation */
3968
3969static void
3970gtk_icon_view_ensure_cell_area (GtkIconView *icon_view,
3971 GtkCellArea *cell_area)
3972{
3973 GtkIconViewPrivate *priv = icon_view->priv;
3974
3975 if (priv->cell_area)
3976 return;
3977
3978 if (cell_area)
3979 priv->cell_area = cell_area;
3980 else
3981 priv->cell_area = gtk_cell_area_box_new ();
3982
3983 g_object_ref_sink (priv->cell_area);
3984
3985 if (GTK_IS_ORIENTABLE (priv->cell_area))
3986 gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->cell_area), orientation: priv->item_orientation);
3987
3988 priv->cell_area_context = gtk_cell_area_create_context (area: priv->cell_area);
3989
3990 priv->add_editable_id =
3991 g_signal_connect (priv->cell_area, "add-editable",
3992 G_CALLBACK (gtk_icon_view_add_editable), icon_view);
3993 priv->remove_editable_id =
3994 g_signal_connect (priv->cell_area, "remove-editable",
3995 G_CALLBACK (gtk_icon_view_remove_editable), icon_view);
3996
3997 update_text_cell (icon_view);
3998 update_pixbuf_cell (icon_view);
3999}
4000
4001static GtkCellArea *
4002gtk_icon_view_cell_layout_get_area (GtkCellLayout *cell_layout)
4003{
4004 GtkIconView *icon_view = GTK_ICON_VIEW (cell_layout);
4005 GtkIconViewPrivate *priv = icon_view->priv;
4006
4007 if (G_UNLIKELY (!priv->cell_area))
4008 gtk_icon_view_ensure_cell_area (icon_view, NULL);
4009
4010 return icon_view->priv->cell_area;
4011}
4012
4013void
4014_gtk_icon_view_set_cell_data (GtkIconView *icon_view,
4015 GtkIconViewItem *item)
4016{
4017 GtkTreeIter iter;
4018 GtkTreePath *path;
4019
4020 path = gtk_tree_path_new_from_indices (first_index: item->index, -1);
4021 if (!gtk_tree_model_get_iter (tree_model: icon_view->priv->model, iter: &iter, path))
4022 return;
4023 gtk_tree_path_free (path);
4024
4025 gtk_cell_area_apply_attributes (area: icon_view->priv->cell_area,
4026 tree_model: icon_view->priv->model,
4027 iter: &iter, FALSE, FALSE);
4028}
4029
4030
4031
4032/* Public API */
4033
4034
4035/**
4036 * gtk_icon_view_new:
4037 *
4038 * Creates a new `GtkIconView` widget
4039 *
4040 * Returns: A newly created `GtkIconView` widget
4041 **/
4042GtkWidget *
4043gtk_icon_view_new (void)
4044{
4045 return g_object_new (GTK_TYPE_ICON_VIEW, NULL);
4046}
4047
4048/**
4049 * gtk_icon_view_new_with_area:
4050 * @area: the `GtkCellArea` to use to layout cells
4051 *
4052 * Creates a new `GtkIconView` widget using the
4053 * specified @area to layout cells inside the icons.
4054 *
4055 * Returns: A newly created `GtkIconView` widget
4056 **/
4057GtkWidget *
4058gtk_icon_view_new_with_area (GtkCellArea *area)
4059{
4060 return g_object_new (GTK_TYPE_ICON_VIEW, first_property_name: "cell-area", area, NULL);
4061}
4062
4063/**
4064 * gtk_icon_view_new_with_model:
4065 * @model: The model.
4066 *
4067 * Creates a new `GtkIconView` widget with the model @model.
4068 *
4069 * Returns: A newly created `GtkIconView` widget.
4070 **/
4071GtkWidget *
4072gtk_icon_view_new_with_model (GtkTreeModel *model)
4073{
4074 return g_object_new (GTK_TYPE_ICON_VIEW, first_property_name: "model", model, NULL);
4075}
4076
4077/**
4078 * gtk_icon_view_get_path_at_pos:
4079 * @icon_view: A `GtkIconView`.
4080 * @x: The x position to be identified
4081 * @y: The y position to be identified
4082 *
4083 * Gets the path for the icon at the given position.
4084 *
4085 * Returns: (nullable) (transfer full): The `GtkTreePath` corresponding
4086 * to the icon or %NULL if no icon exists at that position.
4087 **/
4088GtkTreePath *
4089gtk_icon_view_get_path_at_pos (GtkIconView *icon_view,
4090 int x,
4091 int y)
4092{
4093 GtkIconViewItem *item;
4094 GtkTreePath *path;
4095
4096 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL);
4097
4098 item = _gtk_icon_view_get_item_at_coords (icon_view, x, y, TRUE, NULL);
4099
4100 if (!item)
4101 return NULL;
4102
4103 path = gtk_tree_path_new_from_indices (first_index: item->index, -1);
4104
4105 return path;
4106}
4107
4108/**
4109 * gtk_icon_view_get_item_at_pos:
4110 * @icon_view: A `GtkIconView`.
4111 * @x: The x position to be identified
4112 * @y: The y position to be identified
4113 * @path: (out) (optional): Return location for the path
4114 * @cell: (out) (optional) (transfer none): Return location for the renderer
4115 * responsible for the cell at (@x, @y)
4116 *
4117 * Gets the path and cell for the icon at the given position.
4118 *
4119 * Returns: %TRUE if an item exists at the specified position
4120 **/
4121gboolean
4122gtk_icon_view_get_item_at_pos (GtkIconView *icon_view,
4123 int x,
4124 int y,
4125 GtkTreePath **path,
4126 GtkCellRenderer **cell)
4127{
4128 GtkIconViewItem *item;
4129 GtkCellRenderer *renderer = NULL;
4130
4131 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE);
4132
4133 item = _gtk_icon_view_get_item_at_coords (icon_view, x, y, TRUE, cell_at_pos: &renderer);
4134
4135 if (path != NULL)
4136 {
4137 if (item != NULL)
4138 *path = gtk_tree_path_new_from_indices (first_index: item->index, -1);
4139 else
4140 *path = NULL;
4141 }
4142
4143 if (cell != NULL)
4144 *cell = renderer;
4145
4146 return (item != NULL);
4147}
4148
4149/**
4150 * gtk_icon_view_get_cell_rect:
4151 * @icon_view: a `GtkIconView`
4152 * @path: a `GtkTreePath`
4153 * @cell: (nullable): a `GtkCellRenderer`
4154 * @rect: (out): rectangle to fill with cell rect
4155 *
4156 * Fills the bounding rectangle in widget coordinates for the cell specified by
4157 * @path and @cell. If @cell is %NULL the main cell area is used.
4158 *
4159 * This function is only valid if @icon_view is realized.
4160 *
4161 * Returns: %FALSE if there is no such item, %TRUE otherwise
4162 */
4163gboolean
4164gtk_icon_view_get_cell_rect (GtkIconView *icon_view,
4165 GtkTreePath *path,
4166 GtkCellRenderer *cell,
4167 GdkRectangle *rect)
4168{
4169 GtkIconViewItem *item = NULL;
4170
4171 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE);
4172 g_return_val_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell), FALSE);
4173
4174 if (gtk_tree_path_get_depth (path) > 0)
4175 item = g_list_nth_data (list: icon_view->priv->items,
4176 n: gtk_tree_path_get_indices(path)[0]);
4177
4178 if (!item)
4179 return FALSE;
4180
4181 if (cell)
4182 {
4183 GtkCellAreaContext *context;
4184
4185 context = g_ptr_array_index (icon_view->priv->row_contexts, item->row);
4186 _gtk_icon_view_set_cell_data (icon_view, item);
4187 gtk_cell_area_get_cell_allocation (area: icon_view->priv->cell_area, context,
4188 GTK_WIDGET (icon_view),
4189 renderer: cell, cell_area: &item->cell_area, allocation: rect);
4190 }
4191 else
4192 {
4193 rect->x = item->cell_area.x - icon_view->priv->item_padding;
4194 rect->y = item->cell_area.y - icon_view->priv->item_padding;
4195 rect->width = item->cell_area.width + icon_view->priv->item_padding * 2;
4196 rect->height = item->cell_area.height + icon_view->priv->item_padding * 2;
4197 }
4198
4199 return TRUE;
4200}
4201
4202/**
4203 * gtk_icon_view_set_tooltip_item:
4204 * @icon_view: a `GtkIconView`
4205 * @tooltip: a `GtkTooltip`
4206 * @path: a `GtkTreePath`
4207 *
4208 * Sets the tip area of @tooltip to be the area covered by the item at @path.
4209 * See also gtk_icon_view_set_tooltip_column() for a simpler alternative.
4210 * See also gtk_tooltip_set_tip_area().
4211 */
4212void
4213gtk_icon_view_set_tooltip_item (GtkIconView *icon_view,
4214 GtkTooltip *tooltip,
4215 GtkTreePath *path)
4216{
4217 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
4218 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
4219
4220 gtk_icon_view_set_tooltip_cell (icon_view, tooltip, path, NULL);
4221}
4222
4223/**
4224 * gtk_icon_view_set_tooltip_cell:
4225 * @icon_view: a `GtkIconView`
4226 * @tooltip: a `GtkTooltip`
4227 * @path: a `GtkTreePath`
4228 * @cell: (nullable): a `GtkCellRenderer`
4229 *
4230 * Sets the tip area of @tooltip to the area which @cell occupies in
4231 * the item pointed to by @path. See also gtk_tooltip_set_tip_area().
4232 *
4233 * See also gtk_icon_view_set_tooltip_column() for a simpler alternative.
4234 */
4235void
4236gtk_icon_view_set_tooltip_cell (GtkIconView *icon_view,
4237 GtkTooltip *tooltip,
4238 GtkTreePath *path,
4239 GtkCellRenderer *cell)
4240{
4241 GdkRectangle rect;
4242
4243 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
4244 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
4245 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
4246
4247 if (!gtk_icon_view_get_cell_rect (icon_view, path, cell, rect: &rect))
4248 return;
4249
4250 gtk_tooltip_set_tip_area (tooltip, rect: &rect);
4251}
4252
4253
4254/**
4255 * gtk_icon_view_get_tooltip_context:
4256 * @icon_view: an `GtkIconView`
4257 * @x: the x coordinate (relative to widget coordinates)
4258 * @y: the y coordinate (relative to widget coordinates)
4259 * @keyboard_tip: whether this is a keyboard tooltip or not
4260 * @model: (out) (optional) (transfer none): a pointer to receive a `GtkTreeModel`
4261 * @path: (out) (optional): a pointer to receive a `GtkTreePath`
4262 * @iter: (out) (optional): a pointer to receive a `GtkTreeIter`
4263 *
4264 * This function is supposed to be used in a `GtkWidget::query-tooltip`
4265 * signal handler for `GtkIconView`. The @x, @y and @keyboard_tip values
4266 * which are received in the signal handler, should be passed to this
4267 * function without modification.
4268 *
4269 * The return value indicates whether there is an icon view item at the given
4270 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
4271 * tooltips the item returned will be the cursor item. When %TRUE, then any of
4272 * @model, @path and @iter which have been provided will be set to point to
4273 * that row and the corresponding model.
4274 *
4275 * Returns: whether or not the given tooltip context points to an item
4276 */
4277gboolean
4278gtk_icon_view_get_tooltip_context (GtkIconView *icon_view,
4279 int x,
4280 int y,
4281 gboolean keyboard_tip,
4282 GtkTreeModel **model,
4283 GtkTreePath **path,
4284 GtkTreeIter *iter)
4285{
4286 GtkTreePath *tmppath = NULL;
4287
4288 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE);
4289
4290 if (keyboard_tip)
4291 {
4292 gtk_icon_view_get_cursor (icon_view, path: &tmppath, NULL);
4293
4294 if (!tmppath)
4295 return FALSE;
4296 }
4297 else
4298 {
4299 if (!gtk_icon_view_get_item_at_pos (icon_view, x, y, path: &tmppath, NULL))
4300 return FALSE;
4301 }
4302
4303 if (model)
4304 *model = gtk_icon_view_get_model (icon_view);
4305
4306 if (iter)
4307 gtk_tree_model_get_iter (tree_model: gtk_icon_view_get_model (icon_view),
4308 iter, path: tmppath);
4309
4310 if (path)
4311 *path = tmppath;
4312 else
4313 gtk_tree_path_free (path: tmppath);
4314
4315 return TRUE;
4316}
4317
4318static gboolean
4319gtk_icon_view_set_tooltip_query_cb (GtkWidget *widget,
4320 int x,
4321 int y,
4322 gboolean keyboard_tip,
4323 GtkTooltip *tooltip,
4324 gpointer data)
4325{
4326 char *str;
4327 GtkTreeIter iter;
4328 GtkTreePath *path;
4329 GtkTreeModel *model;
4330 GtkIconView *icon_view = GTK_ICON_VIEW (widget);
4331
4332 if (!gtk_icon_view_get_tooltip_context (GTK_ICON_VIEW (widget),
4333 x, y,
4334 keyboard_tip,
4335 model: &model, path: &path, iter: &iter))
4336 return FALSE;
4337
4338 gtk_tree_model_get (tree_model: model, iter: &iter, icon_view->priv->tooltip_column, &str, -1);
4339
4340 if (!str)
4341 {
4342 gtk_tree_path_free (path);
4343 return FALSE;
4344 }
4345
4346 gtk_tooltip_set_markup (tooltip, markup: str);
4347 gtk_icon_view_set_tooltip_item (icon_view, tooltip, path);
4348
4349 gtk_tree_path_free (path);
4350 g_free (mem: str);
4351
4352 return TRUE;
4353}
4354
4355
4356/**
4357 * gtk_icon_view_set_tooltip_column:
4358 * @icon_view: a `GtkIconView`
4359 * @column: an integer, which is a valid column number for @icon_view’s model
4360 *
4361 * If you only plan to have simple (text-only) tooltips on full items, you
4362 * can use this function to have `GtkIconView` handle these automatically
4363 * for you. @column should be set to the column in @icon_view’s model
4364 * containing the tooltip texts, or -1 to disable this feature.
4365 *
4366 * When enabled, `GtkWidget:has-tooltip` will be set to %TRUE and
4367 * @icon_view will connect a `GtkWidget::query-tooltip` signal handler.
4368 *
4369 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
4370 * so &, <, etc have to be escaped in the text.
4371 */
4372void
4373gtk_icon_view_set_tooltip_column (GtkIconView *icon_view,
4374 int column)
4375{
4376 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
4377
4378 if (column == icon_view->priv->tooltip_column)
4379 return;
4380
4381 if (column == -1)
4382 {
4383 g_signal_handlers_disconnect_by_func (icon_view,
4384 gtk_icon_view_set_tooltip_query_cb,
4385 NULL);
4386 gtk_widget_set_has_tooltip (GTK_WIDGET (icon_view), FALSE);
4387 }
4388 else
4389 {
4390 if (icon_view->priv->tooltip_column == -1)
4391 {
4392 g_signal_connect (icon_view, "query-tooltip",
4393 G_CALLBACK (gtk_icon_view_set_tooltip_query_cb), NULL);
4394 gtk_widget_set_has_tooltip (GTK_WIDGET (icon_view), TRUE);
4395 }
4396 }
4397
4398 icon_view->priv->tooltip_column = column;
4399 g_object_notify (G_OBJECT (icon_view), property_name: "tooltip-column");
4400}
4401
4402/**
4403 * gtk_icon_view_get_tooltip_column:
4404 * @icon_view: a `GtkIconView`
4405 *
4406 * Returns the column of @icon_view’s model which is being used for
4407 * displaying tooltips on @icon_view’s rows.
4408 *
4409 * Returns: the index of the tooltip column that is currently being
4410 * used, or -1 if this is disabled.
4411 */
4412int
4413gtk_icon_view_get_tooltip_column (GtkIconView *icon_view)
4414{
4415 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), 0);
4416
4417 return icon_view->priv->tooltip_column;
4418}
4419
4420/**
4421 * gtk_icon_view_get_visible_range:
4422 * @icon_view: A `GtkIconView`
4423 * @start_path: (out) (optional): Return location for start of region
4424 * @end_path: (out) (optional): Return location for end of region
4425 *
4426 * Sets @start_path and @end_path to be the first and last visible path.
4427 * Note that there may be invisible paths in between.
4428 *
4429 * Both paths should be freed with gtk_tree_path_free() after use.
4430 *
4431 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path
4432 */
4433gboolean
4434gtk_icon_view_get_visible_range (GtkIconView *icon_view,
4435 GtkTreePath **start_path,
4436 GtkTreePath **end_path)
4437{
4438 int start_index = -1;
4439 int end_index = -1;
4440 GList *icons;
4441
4442 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE);
4443
4444 if (icon_view->priv->hadjustment == NULL ||
4445 icon_view->priv->vadjustment == NULL)
4446 return FALSE;
4447
4448 if (start_path == NULL && end_path == NULL)
4449 return FALSE;
4450
4451 for (icons = icon_view->priv->items; icons; icons = icons->next)
4452 {
4453 GtkIconViewItem *item = icons->data;
4454 GdkRectangle *item_area = &item->cell_area;
4455
4456 if ((item_area->x + item_area->width >= (int)gtk_adjustment_get_value (adjustment: icon_view->priv->hadjustment)) &&
4457 (item_area->y + item_area->height >= (int)gtk_adjustment_get_value (adjustment: icon_view->priv->vadjustment)) &&
4458 (item_area->x <=
4459 (int) (gtk_adjustment_get_value (adjustment: icon_view->priv->hadjustment) +
4460 gtk_adjustment_get_page_size (adjustment: icon_view->priv->hadjustment))) &&
4461 (item_area->y <=
4462 (int) (gtk_adjustment_get_value (adjustment: icon_view->priv->vadjustment) +
4463 gtk_adjustment_get_page_size (adjustment: icon_view->priv->vadjustment))))
4464 {
4465 if (start_index == -1)
4466 start_index = item->index;
4467 end_index = item->index;
4468 }
4469 }
4470
4471 if (start_path && start_index != -1)
4472 *start_path = gtk_tree_path_new_from_indices (first_index: start_index, -1);
4473 if (end_path && end_index != -1)
4474 *end_path = gtk_tree_path_new_from_indices (first_index: end_index, -1);
4475
4476 return start_index != -1;
4477}
4478
4479/**
4480 * gtk_icon_view_selected_foreach:
4481 * @icon_view: A `GtkIconView`.
4482 * @func: (scope call): The function to call for each selected icon.
4483 * @data: User data to pass to the function.
4484 *
4485 * Calls a function for each selected icon. Note that the model or
4486 * selection cannot be modified from within this function.
4487 **/
4488void
4489gtk_icon_view_selected_foreach (GtkIconView *icon_view,
4490 GtkIconViewForeachFunc func,
4491 gpointer data)
4492{
4493 GList *list;
4494
4495 for (list = icon_view->priv->items; list; list = list->next)
4496 {
4497 GtkIconViewItem *item = list->data;
4498 GtkTreePath *path = gtk_tree_path_new_from_indices (first_index: item->index, -1);
4499
4500 if (item->selected)
4501 (* func) (icon_view, path, data);
4502
4503 gtk_tree_path_free (path);
4504 }
4505}
4506
4507/**
4508 * gtk_icon_view_set_selection_mode:
4509 * @icon_view: A `GtkIconView`.
4510 * @mode: The selection mode
4511 *
4512 * Sets the selection mode of the @icon_view.
4513 **/
4514void
4515gtk_icon_view_set_selection_mode (GtkIconView *icon_view,
4516 GtkSelectionMode mode)
4517{
4518 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
4519
4520 if (mode == icon_view->priv->selection_mode)
4521 return;
4522
4523 if (mode == GTK_SELECTION_NONE ||
4524 icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE)
4525 gtk_icon_view_unselect_all (icon_view);
4526
4527 icon_view->priv->selection_mode = mode;
4528
4529 g_object_notify (G_OBJECT (icon_view), property_name: "selection-mode");
4530}
4531
4532/**
4533 * gtk_icon_view_get_selection_mode:
4534 * @icon_view: A `GtkIconView`.
4535 *
4536 * Gets the selection mode of the @icon_view.
4537 *
4538 * Returns: the current selection mode
4539 **/
4540GtkSelectionMode
4541gtk_icon_view_get_selection_mode (GtkIconView *icon_view)
4542{
4543 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), GTK_SELECTION_SINGLE);
4544
4545 return icon_view->priv->selection_mode;
4546}
4547
4548/**
4549 * gtk_icon_view_set_model:
4550 * @icon_view: A `GtkIconView`.
4551 * @model: (nullable): The model.
4552 *
4553 * Sets the model for a `GtkIconView`.
4554 * If the @icon_view already has a model set, it will remove
4555 * it before setting the new model. If @model is %NULL, then
4556 * it will unset the old model.
4557 **/
4558void
4559gtk_icon_view_set_model (GtkIconView *icon_view,
4560 GtkTreeModel *model)
4561{
4562 gboolean dirty;
4563
4564 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
4565 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
4566
4567 if (icon_view->priv->model == model)
4568 return;
4569
4570 if (icon_view->priv->scroll_to_path)
4571 {
4572 gtk_tree_row_reference_free (reference: icon_view->priv->scroll_to_path);
4573 icon_view->priv->scroll_to_path = NULL;
4574 }
4575
4576 /* The area can be NULL while disposing */
4577 if (icon_view->priv->cell_area)
4578 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
4579
4580 dirty = gtk_icon_view_unselect_all_internal (icon_view);
4581
4582 if (model)
4583 {
4584 if (icon_view->priv->pixbuf_column != -1)
4585 {
4586 g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->pixbuf_column) == GDK_TYPE_PIXBUF);
4587 }
4588
4589 if (icon_view->priv->text_column != -1)
4590 {
4591 g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->text_column) == G_TYPE_STRING);
4592 }
4593
4594 if (icon_view->priv->markup_column != -1)
4595 {
4596 g_return_if_fail (gtk_tree_model_get_column_type (model, icon_view->priv->markup_column) == G_TYPE_STRING);
4597 }
4598
4599 }
4600
4601 if (icon_view->priv->model)
4602 {
4603 g_signal_handlers_disconnect_by_func (icon_view->priv->model,
4604 gtk_icon_view_row_changed,
4605 icon_view);
4606 g_signal_handlers_disconnect_by_func (icon_view->priv->model,
4607 gtk_icon_view_row_inserted,
4608 icon_view);
4609 g_signal_handlers_disconnect_by_func (icon_view->priv->model,
4610 gtk_icon_view_row_deleted,
4611 icon_view);
4612 g_signal_handlers_disconnect_by_func (icon_view->priv->model,
4613 gtk_icon_view_rows_reordered,
4614 icon_view);
4615
4616 g_object_unref (object: icon_view->priv->model);
4617
4618 g_list_free_full (list: icon_view->priv->items, free_func: (GDestroyNotify) gtk_icon_view_item_free);
4619 icon_view->priv->items = NULL;
4620 icon_view->priv->anchor_item = NULL;
4621 icon_view->priv->cursor_item = NULL;
4622 icon_view->priv->last_single_clicked = NULL;
4623 icon_view->priv->last_prelight = NULL;
4624 icon_view->priv->width = 0;
4625 icon_view->priv->height = 0;
4626 }
4627
4628 icon_view->priv->model = model;
4629
4630 if (icon_view->priv->model)
4631 {
4632 g_object_ref (icon_view->priv->model);
4633 g_signal_connect (icon_view->priv->model,
4634 "row-changed",
4635 G_CALLBACK (gtk_icon_view_row_changed),
4636 icon_view);
4637 g_signal_connect (icon_view->priv->model,
4638 "row-inserted",
4639 G_CALLBACK (gtk_icon_view_row_inserted),
4640 icon_view);
4641 g_signal_connect (icon_view->priv->model,
4642 "row-deleted",
4643 G_CALLBACK (gtk_icon_view_row_deleted),
4644 icon_view);
4645 g_signal_connect (icon_view->priv->model,
4646 "rows-reordered",
4647 G_CALLBACK (gtk_icon_view_rows_reordered),
4648 icon_view);
4649
4650 gtk_icon_view_build_items (icon_view);
4651 }
4652
4653 g_object_notify (G_OBJECT (icon_view), property_name: "model");
4654
4655 if (dirty)
4656 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
4657
4658 gtk_widget_queue_resize (GTK_WIDGET (icon_view));
4659}
4660
4661/**
4662 * gtk_icon_view_get_model:
4663 * @icon_view: a `GtkIconView`
4664 *
4665 * Returns the model the `GtkIconView` is based on. Returns %NULL if the
4666 * model is unset.
4667 *
4668 * Returns: (nullable) (transfer none): The currently used `GtkTreeModel`
4669 */
4670GtkTreeModel *
4671gtk_icon_view_get_model (GtkIconView *icon_view)
4672{
4673 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL);
4674
4675 return icon_view->priv->model;
4676}
4677
4678static void
4679update_text_cell (GtkIconView *icon_view)
4680{
4681 if (!icon_view->priv->cell_area)
4682 return;
4683
4684 if (icon_view->priv->text_column == -1 &&
4685 icon_view->priv->markup_column == -1)
4686 {
4687 if (icon_view->priv->text_cell != NULL)
4688 {
4689 gtk_cell_area_remove (area: icon_view->priv->cell_area,
4690 renderer: icon_view->priv->text_cell);
4691 icon_view->priv->text_cell = NULL;
4692 }
4693 }
4694 else
4695 {
4696 if (icon_view->priv->text_cell == NULL)
4697 {
4698 icon_view->priv->text_cell = gtk_cell_renderer_text_new ();
4699
4700 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (icon_view), cell: icon_view->priv->text_cell, FALSE);
4701 }
4702
4703 if (icon_view->priv->markup_column != -1)
4704 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
4705 cell: icon_view->priv->text_cell,
4706 "markup", icon_view->priv->markup_column,
4707 NULL);
4708 else
4709 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
4710 cell: icon_view->priv->text_cell,
4711 "text", icon_view->priv->text_column,
4712 NULL);
4713
4714 if (icon_view->priv->item_orientation == GTK_ORIENTATION_VERTICAL)
4715 g_object_set (object: icon_view->priv->text_cell,
4716 first_property_name: "alignment", PANGO_ALIGN_CENTER,
4717 "wrap-mode", PANGO_WRAP_WORD_CHAR,
4718 "xalign", 0.5,
4719 "yalign", 0.0,
4720 NULL);
4721 else
4722 g_object_set (object: icon_view->priv->text_cell,
4723 first_property_name: "alignment", PANGO_ALIGN_LEFT,
4724 "wrap-mode", PANGO_WRAP_WORD_CHAR,
4725 "xalign", 0.0,
4726 "yalign", 0.5,
4727 NULL);
4728 }
4729}
4730
4731static void
4732update_pixbuf_cell (GtkIconView *icon_view)
4733{
4734 if (!icon_view->priv->cell_area)
4735 return;
4736
4737 if (icon_view->priv->pixbuf_column == -1)
4738 {
4739 if (icon_view->priv->pixbuf_cell != NULL)
4740 {
4741 gtk_cell_area_remove (area: icon_view->priv->cell_area,
4742 renderer: icon_view->priv->pixbuf_cell);
4743
4744 icon_view->priv->pixbuf_cell = NULL;
4745 }
4746 }
4747 else
4748 {
4749 if (icon_view->priv->pixbuf_cell == NULL)
4750 {
4751 icon_view->priv->pixbuf_cell = gtk_cell_renderer_pixbuf_new ();
4752
4753 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_view), cell: icon_view->priv->pixbuf_cell, FALSE);
4754 }
4755
4756 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view),
4757 cell: icon_view->priv->pixbuf_cell,
4758 "pixbuf", icon_view->priv->pixbuf_column,
4759 NULL);
4760
4761 if (icon_view->priv->item_orientation == GTK_ORIENTATION_VERTICAL)
4762 g_object_set (object: icon_view->priv->pixbuf_cell,
4763 first_property_name: "xalign", 0.5,
4764 "yalign", 1.0,
4765 NULL);
4766 else
4767 g_object_set (object: icon_view->priv->pixbuf_cell,
4768 first_property_name: "xalign", 0.0,
4769 "yalign", 0.0,
4770 NULL);
4771 }
4772}
4773
4774/**
4775 * gtk_icon_view_set_text_column:
4776 * @icon_view: A `GtkIconView`.
4777 * @column: A column in the currently used model, or -1 to display no text
4778 *
4779 * Sets the column with text for @icon_view to be @column. The text
4780 * column must be of type `G_TYPE_STRING`.
4781 **/
4782void
4783gtk_icon_view_set_text_column (GtkIconView *icon_view,
4784 int column)
4785{
4786 if (column == icon_view->priv->text_column)
4787 return;
4788
4789 if (column == -1)
4790 icon_view->priv->text_column = -1;
4791 else
4792 {
4793 if (icon_view->priv->model != NULL)
4794 {
4795 g_return_if_fail (gtk_tree_model_get_column_type (icon_view->priv->model, column) == G_TYPE_STRING);
4796 }
4797
4798 icon_view->priv->text_column = column;
4799 }
4800
4801 if (icon_view->priv->cell_area)
4802 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
4803
4804 update_text_cell (icon_view);
4805
4806 gtk_icon_view_invalidate_sizes (icon_view);
4807
4808 g_object_notify (G_OBJECT (icon_view), property_name: "text-column");
4809}
4810
4811/**
4812 * gtk_icon_view_get_text_column:
4813 * @icon_view: A `GtkIconView`.
4814 *
4815 * Returns the column with text for @icon_view.
4816 *
4817 * Returns: the text column, or -1 if it’s unset.
4818 */
4819int
4820gtk_icon_view_get_text_column (GtkIconView *icon_view)
4821{
4822 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
4823
4824 return icon_view->priv->text_column;
4825}
4826
4827/**
4828 * gtk_icon_view_set_markup_column:
4829 * @icon_view: A `GtkIconView`.
4830 * @column: A column in the currently used model, or -1 to display no text
4831 *
4832 * Sets the column with markup information for @icon_view to be
4833 * @column. The markup column must be of type `G_TYPE_STRING`.
4834 * If the markup column is set to something, it overrides
4835 * the text column set by gtk_icon_view_set_text_column().
4836 **/
4837void
4838gtk_icon_view_set_markup_column (GtkIconView *icon_view,
4839 int column)
4840{
4841 if (column == icon_view->priv->markup_column)
4842 return;
4843
4844 if (column == -1)
4845 icon_view->priv->markup_column = -1;
4846 else
4847 {
4848 if (icon_view->priv->model != NULL)
4849 {
4850 g_return_if_fail (gtk_tree_model_get_column_type (icon_view->priv->model, column) == G_TYPE_STRING);
4851 }
4852
4853 icon_view->priv->markup_column = column;
4854 }
4855
4856 if (icon_view->priv->cell_area)
4857 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
4858
4859 update_text_cell (icon_view);
4860
4861 gtk_icon_view_invalidate_sizes (icon_view);
4862
4863 g_object_notify (G_OBJECT (icon_view), property_name: "markup-column");
4864}
4865
4866/**
4867 * gtk_icon_view_get_markup_column:
4868 * @icon_view: A `GtkIconView`.
4869 *
4870 * Returns the column with markup text for @icon_view.
4871 *
4872 * Returns: the markup column, or -1 if it’s unset.
4873 */
4874int
4875gtk_icon_view_get_markup_column (GtkIconView *icon_view)
4876{
4877 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
4878
4879 return icon_view->priv->markup_column;
4880}
4881
4882/**
4883 * gtk_icon_view_set_pixbuf_column:
4884 * @icon_view: A `GtkIconView`.
4885 * @column: A column in the currently used model, or -1 to disable
4886 *
4887 * Sets the column with pixbufs for @icon_view to be @column. The pixbuf
4888 * column must be of type `GDK_TYPE_PIXBUF`
4889 **/
4890void
4891gtk_icon_view_set_pixbuf_column (GtkIconView *icon_view,
4892 int column)
4893{
4894 if (column == icon_view->priv->pixbuf_column)
4895 return;
4896
4897 if (column == -1)
4898 icon_view->priv->pixbuf_column = -1;
4899 else
4900 {
4901 if (icon_view->priv->model != NULL)
4902 {
4903 g_return_if_fail (gtk_tree_model_get_column_type (icon_view->priv->model, column) == GDK_TYPE_PIXBUF);
4904 }
4905
4906 icon_view->priv->pixbuf_column = column;
4907 }
4908
4909 if (icon_view->priv->cell_area)
4910 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
4911
4912 update_pixbuf_cell (icon_view);
4913
4914 gtk_icon_view_invalidate_sizes (icon_view);
4915
4916 g_object_notify (G_OBJECT (icon_view), property_name: "pixbuf-column");
4917
4918}
4919
4920/**
4921 * gtk_icon_view_get_pixbuf_column:
4922 * @icon_view: A `GtkIconView`.
4923 *
4924 * Returns the column with pixbufs for @icon_view.
4925 *
4926 * Returns: the pixbuf column, or -1 if it’s unset.
4927 */
4928int
4929gtk_icon_view_get_pixbuf_column (GtkIconView *icon_view)
4930{
4931 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
4932
4933 return icon_view->priv->pixbuf_column;
4934}
4935
4936/**
4937 * gtk_icon_view_select_path:
4938 * @icon_view: A `GtkIconView`.
4939 * @path: The `GtkTreePath` to be selected.
4940 *
4941 * Selects the row at @path.
4942 **/
4943void
4944gtk_icon_view_select_path (GtkIconView *icon_view,
4945 GtkTreePath *path)
4946{
4947 GtkIconViewItem *item = NULL;
4948
4949 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
4950 g_return_if_fail (icon_view->priv->model != NULL);
4951 g_return_if_fail (path != NULL);
4952
4953 if (gtk_tree_path_get_depth (path) > 0)
4954 item = g_list_nth_data (list: icon_view->priv->items,
4955 n: gtk_tree_path_get_indices(path)[0]);
4956
4957 if (item)
4958 _gtk_icon_view_select_item (icon_view, item);
4959}
4960
4961/**
4962 * gtk_icon_view_unselect_path:
4963 * @icon_view: A `GtkIconView`.
4964 * @path: The `GtkTreePath` to be unselected.
4965 *
4966 * Unselects the row at @path.
4967 **/
4968void
4969gtk_icon_view_unselect_path (GtkIconView *icon_view,
4970 GtkTreePath *path)
4971{
4972 GtkIconViewItem *item;
4973
4974 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
4975 g_return_if_fail (icon_view->priv->model != NULL);
4976 g_return_if_fail (path != NULL);
4977
4978 item = g_list_nth_data (list: icon_view->priv->items,
4979 n: gtk_tree_path_get_indices(path)[0]);
4980
4981 if (!item)
4982 return;
4983
4984 _gtk_icon_view_unselect_item (icon_view, item);
4985}
4986
4987/**
4988 * gtk_icon_view_get_selected_items:
4989 * @icon_view: A `GtkIconView`.
4990 *
4991 * Creates a list of paths of all selected items. Additionally, if you are
4992 * planning on modifying the model after calling this function, you may
4993 * want to convert the returned list into a list of `GtkTreeRowReferences`.
4994 * To do this, you can use gtk_tree_row_reference_new().
4995 *
4996 * To free the return value, use `g_list_free_full`:
4997 * |[<!-- language="C" -->
4998 * GtkWidget *icon_view = gtk_icon_view_new ();
4999 * // Use icon_view
5000 *
5001 * GList *list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view));
5002 *
5003 * // use list
5004 *
5005 * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
5006 * ]|
5007 *
5008 * Returns: (element-type GtkTreePath) (transfer full): A `GList` containing a `GtkTreePath` for each selected row.
5009 **/
5010GList *
5011gtk_icon_view_get_selected_items (GtkIconView *icon_view)
5012{
5013 GList *list;
5014 GList *selected = NULL;
5015
5016 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL);
5017
5018 for (list = icon_view->priv->items; list != NULL; list = list->next)
5019 {
5020 GtkIconViewItem *item = list->data;
5021
5022 if (item->selected)
5023 {
5024 GtkTreePath *path = gtk_tree_path_new_from_indices (first_index: item->index, -1);
5025
5026 selected = g_list_prepend (list: selected, data: path);
5027 }
5028 }
5029
5030 return selected;
5031}
5032
5033/**
5034 * gtk_icon_view_select_all:
5035 * @icon_view: A `GtkIconView`.
5036 *
5037 * Selects all the icons. @icon_view must has its selection mode set
5038 * to %GTK_SELECTION_MULTIPLE.
5039 **/
5040void
5041gtk_icon_view_select_all (GtkIconView *icon_view)
5042{
5043 GList *items;
5044 gboolean dirty = FALSE;
5045
5046 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
5047
5048 if (icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE)
5049 return;
5050
5051 for (items = icon_view->priv->items; items; items = items->next)
5052 {
5053 GtkIconViewItem *item = items->data;
5054
5055 if (!item->selected)
5056 {
5057 dirty = TRUE;
5058 item->selected = TRUE;
5059 gtk_icon_view_queue_draw_item (icon_view, item);
5060 }
5061 }
5062
5063 if (dirty)
5064 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
5065}
5066
5067/**
5068 * gtk_icon_view_unselect_all:
5069 * @icon_view: A `GtkIconView`.
5070 *
5071 * Unselects all the icons.
5072 **/
5073void
5074gtk_icon_view_unselect_all (GtkIconView *icon_view)
5075{
5076 gboolean dirty = FALSE;
5077
5078 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
5079
5080 if (icon_view->priv->selection_mode == GTK_SELECTION_BROWSE)
5081 return;
5082
5083 dirty = gtk_icon_view_unselect_all_internal (icon_view);
5084
5085 if (dirty)
5086 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[SELECTION_CHANGED], detail: 0);
5087}
5088
5089/**
5090 * gtk_icon_view_path_is_selected:
5091 * @icon_view: A `GtkIconView`.
5092 * @path: A `GtkTreePath` to check selection on.
5093 *
5094 * Returns %TRUE if the icon pointed to by @path is currently
5095 * selected. If @path does not point to a valid location, %FALSE is returned.
5096 *
5097 * Returns: %TRUE if @path is selected.
5098 **/
5099gboolean
5100gtk_icon_view_path_is_selected (GtkIconView *icon_view,
5101 GtkTreePath *path)
5102{
5103 GtkIconViewItem *item;
5104
5105 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE);
5106 g_return_val_if_fail (icon_view->priv->model != NULL, FALSE);
5107 g_return_val_if_fail (path != NULL, FALSE);
5108
5109 item = g_list_nth_data (list: icon_view->priv->items,
5110 n: gtk_tree_path_get_indices(path)[0]);
5111
5112 if (!item)
5113 return FALSE;
5114
5115 return item->selected;
5116}
5117
5118/**
5119 * gtk_icon_view_get_item_row:
5120 * @icon_view: a `GtkIconView`
5121 * @path: the `GtkTreePath` of the item
5122 *
5123 * Gets the row in which the item @path is currently
5124 * displayed. Row numbers start at 0.
5125 *
5126 * Returns: The row in which the item is displayed
5127 */
5128int
5129gtk_icon_view_get_item_row (GtkIconView *icon_view,
5130 GtkTreePath *path)
5131{
5132 GtkIconViewItem *item;
5133
5134 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
5135 g_return_val_if_fail (icon_view->priv->model != NULL, -1);
5136 g_return_val_if_fail (path != NULL, -1);
5137
5138 item = g_list_nth_data (list: icon_view->priv->items,
5139 n: gtk_tree_path_get_indices(path)[0]);
5140
5141 if (!item)
5142 return -1;
5143
5144 return item->row;
5145}
5146
5147/**
5148 * gtk_icon_view_get_item_column:
5149 * @icon_view: a `GtkIconView`
5150 * @path: the `GtkTreePath` of the item
5151 *
5152 * Gets the column in which the item @path is currently
5153 * displayed. Column numbers start at 0.
5154 *
5155 * Returns: The column in which the item is displayed
5156 */
5157int
5158gtk_icon_view_get_item_column (GtkIconView *icon_view,
5159 GtkTreePath *path)
5160{
5161 GtkIconViewItem *item;
5162
5163 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
5164 g_return_val_if_fail (icon_view->priv->model != NULL, -1);
5165 g_return_val_if_fail (path != NULL, -1);
5166
5167 item = g_list_nth_data (list: icon_view->priv->items,
5168 n: gtk_tree_path_get_indices(path)[0]);
5169
5170 if (!item)
5171 return -1;
5172
5173 return item->col;
5174}
5175
5176/**
5177 * gtk_icon_view_item_activated:
5178 * @icon_view: A `GtkIconView`
5179 * @path: The `GtkTreePath` to be activated
5180 *
5181 * Activates the item determined by @path.
5182 **/
5183void
5184gtk_icon_view_item_activated (GtkIconView *icon_view,
5185 GtkTreePath *path)
5186{
5187 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
5188 g_return_if_fail (path != NULL);
5189
5190 g_signal_emit (instance: icon_view, signal_id: icon_view_signals[ITEM_ACTIVATED], detail: 0, path);
5191}
5192
5193/**
5194 * gtk_icon_view_set_item_orientation:
5195 * @icon_view: a `GtkIconView`
5196 * @orientation: the relative position of texts and icons
5197 *
5198 * Sets the ::item-orientation property which determines whether the labels
5199 * are drawn beside the icons instead of below.
5200 **/
5201void
5202gtk_icon_view_set_item_orientation (GtkIconView *icon_view,
5203 GtkOrientation orientation)
5204{
5205 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
5206
5207 if (icon_view->priv->item_orientation != orientation)
5208 {
5209 icon_view->priv->item_orientation = orientation;
5210
5211 if (icon_view->priv->cell_area)
5212 {
5213 if (GTK_IS_ORIENTABLE (icon_view->priv->cell_area))
5214 gtk_orientable_set_orientation (GTK_ORIENTABLE (icon_view->priv->cell_area),
5215 orientation: icon_view->priv->item_orientation);
5216
5217 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
5218 }
5219
5220 gtk_icon_view_invalidate_sizes (icon_view);
5221
5222 update_text_cell (icon_view);
5223 update_pixbuf_cell (icon_view);
5224
5225 g_object_notify (G_OBJECT (icon_view), property_name: "item-orientation");
5226 }
5227}
5228
5229/**
5230 * gtk_icon_view_get_item_orientation:
5231 * @icon_view: a `GtkIconView`
5232 *
5233 * Returns the value of the ::item-orientation property which determines
5234 * whether the labels are drawn beside the icons instead of below.
5235 *
5236 * Returns: the relative position of texts and icons
5237 **/
5238GtkOrientation
5239gtk_icon_view_get_item_orientation (GtkIconView *icon_view)
5240{
5241 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view),
5242 GTK_ORIENTATION_VERTICAL);
5243
5244 return icon_view->priv->item_orientation;
5245}
5246
5247/**
5248 * gtk_icon_view_set_columns:
5249 * @icon_view: a `GtkIconView`
5250 * @columns: the number of columns
5251 *
5252 * Sets the ::columns property which determines in how
5253 * many columns the icons are arranged. If @columns is
5254 * -1, the number of columns will be chosen automatically
5255 * to fill the available area.
5256 */
5257void
5258gtk_icon_view_set_columns (GtkIconView *icon_view,
5259 int columns)
5260{
5261 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
5262
5263 if (icon_view->priv->columns != columns)
5264 {
5265 icon_view->priv->columns = columns;
5266
5267 if (icon_view->priv->cell_area)
5268 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
5269
5270 gtk_widget_queue_resize (GTK_WIDGET (icon_view));
5271
5272 g_object_notify (G_OBJECT (icon_view), property_name: "columns");
5273 }
5274}
5275
5276/**
5277 * gtk_icon_view_get_columns:
5278 * @icon_view: a `GtkIconView`
5279 *
5280 * Returns the value of the ::columns property.
5281 *
5282 * Returns: the number of columns, or -1
5283 */
5284int
5285gtk_icon_view_get_columns (GtkIconView *icon_view)
5286{
5287 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
5288
5289 return icon_view->priv->columns;
5290}
5291
5292/**
5293 * gtk_icon_view_set_item_width:
5294 * @icon_view: a `GtkIconView`
5295 * @item_width: the width for each item
5296 *
5297 * Sets the ::item-width property which specifies the width
5298 * to use for each item. If it is set to -1, the icon view will
5299 * automatically determine a suitable item size.
5300 */
5301void
5302gtk_icon_view_set_item_width (GtkIconView *icon_view,
5303 int item_width)
5304{
5305 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
5306
5307 if (icon_view->priv->item_width != item_width)
5308 {
5309 icon_view->priv->item_width = item_width;
5310
5311 if (icon_view->priv->cell_area)
5312 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
5313
5314 gtk_icon_view_invalidate_sizes (icon_view);
5315
5316 update_text_cell (icon_view);
5317
5318 g_object_notify (G_OBJECT (icon_view), property_name: "item-width");
5319 }
5320}
5321
5322/**
5323 * gtk_icon_view_get_item_width:
5324 * @icon_view: a `GtkIconView`
5325 *
5326 * Returns the value of the ::item-width property.
5327 *
5328 * Returns: the width of a single item, or -1
5329 */
5330int
5331gtk_icon_view_get_item_width (GtkIconView *icon_view)
5332{
5333 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
5334
5335 return icon_view->priv->item_width;
5336}
5337
5338
5339/**
5340 * gtk_icon_view_set_spacing:
5341 * @icon_view: a `GtkIconView`
5342 * @spacing: the spacing
5343 *
5344 * Sets the ::spacing property which specifies the space
5345 * which is inserted between the cells (i.e. the icon and
5346 * the text) of an item.
5347 */
5348void
5349gtk_icon_view_set_spacing (GtkIconView *icon_view,
5350 int spacing)
5351{
5352 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
5353
5354 if (icon_view->priv->spacing != spacing)
5355 {
5356 icon_view->priv->spacing = spacing;
5357
5358 if (icon_view->priv->cell_area)
5359 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
5360
5361 gtk_icon_view_invalidate_sizes (icon_view);
5362
5363 g_object_notify (G_OBJECT (icon_view), property_name: "spacing");
5364 }
5365}
5366
5367/**
5368 * gtk_icon_view_get_spacing:
5369 * @icon_view: a `GtkIconView`
5370 *
5371 * Returns the value of the ::spacing property.
5372 *
5373 * Returns: the space between cells
5374 */
5375int
5376gtk_icon_view_get_spacing (GtkIconView *icon_view)
5377{
5378 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
5379
5380 return icon_view->priv->spacing;
5381}
5382
5383/**
5384 * gtk_icon_view_set_row_spacing:
5385 * @icon_view: a `GtkIconView`
5386 * @row_spacing: the row spacing
5387 *
5388 * Sets the ::row-spacing property which specifies the space
5389 * which is inserted between the rows of the icon view.
5390 */
5391void
5392gtk_icon_view_set_row_spacing (GtkIconView *icon_view,
5393 int row_spacing)
5394{
5395 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
5396
5397 if (icon_view->priv->row_spacing != row_spacing)
5398 {
5399 icon_view->priv->row_spacing = row_spacing;
5400
5401 if (icon_view->priv->cell_area)
5402 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
5403
5404 gtk_icon_view_invalidate_sizes (icon_view);
5405
5406 g_object_notify (G_OBJECT (icon_view), property_name: "row-spacing");
5407 }
5408}
5409
5410/**
5411 * gtk_icon_view_get_row_spacing:
5412 * @icon_view: a `GtkIconView`
5413 *
5414 * Returns the value of the ::row-spacing property.
5415 *
5416 * Returns: the space between rows
5417 */
5418int
5419gtk_icon_view_get_row_spacing (GtkIconView *icon_view)
5420{
5421 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
5422
5423 return icon_view->priv->row_spacing;
5424}
5425
5426/**
5427 * gtk_icon_view_set_column_spacing:
5428 * @icon_view: a `GtkIconView`
5429 * @column_spacing: the column spacing
5430 *
5431 * Sets the ::column-spacing property which specifies the space
5432 * which is inserted between the columns of the icon view.
5433 */
5434void
5435gtk_icon_view_set_column_spacing (GtkIconView *icon_view,
5436 int column_spacing)
5437{
5438 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
5439
5440 if (icon_view->priv->column_spacing != column_spacing)
5441 {
5442 icon_view->priv->column_spacing = column_spacing;
5443
5444 if (icon_view->priv->cell_area)
5445 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
5446
5447 gtk_icon_view_invalidate_sizes (icon_view);
5448
5449 g_object_notify (G_OBJECT (icon_view), property_name: "column-spacing");
5450 }
5451}
5452
5453/**
5454 * gtk_icon_view_get_column_spacing:
5455 * @icon_view: a `GtkIconView`
5456 *
5457 * Returns the value of the ::column-spacing property.
5458 *
5459 * Returns: the space between columns
5460 */
5461int
5462gtk_icon_view_get_column_spacing (GtkIconView *icon_view)
5463{
5464 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
5465
5466 return icon_view->priv->column_spacing;
5467}
5468
5469/**
5470 * gtk_icon_view_set_margin:
5471 * @icon_view: a `GtkIconView`
5472 * @margin: the margin
5473 *
5474 * Sets the ::margin property which specifies the space
5475 * which is inserted at the top, bottom, left and right
5476 * of the icon view.
5477 */
5478void
5479gtk_icon_view_set_margin (GtkIconView *icon_view,
5480 int margin)
5481{
5482 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
5483
5484 if (icon_view->priv->margin != margin)
5485 {
5486 icon_view->priv->margin = margin;
5487
5488 if (icon_view->priv->cell_area)
5489 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
5490
5491 gtk_icon_view_invalidate_sizes (icon_view);
5492
5493 g_object_notify (G_OBJECT (icon_view), property_name: "margin");
5494 }
5495}
5496
5497/**
5498 * gtk_icon_view_get_margin:
5499 * @icon_view: a `GtkIconView`
5500 *
5501 * Returns the value of the ::margin property.
5502 *
5503 * Returns: the space at the borders
5504 */
5505int
5506gtk_icon_view_get_margin (GtkIconView *icon_view)
5507{
5508 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
5509
5510 return icon_view->priv->margin;
5511}
5512
5513/**
5514 * gtk_icon_view_set_item_padding:
5515 * @icon_view: a `GtkIconView`
5516 * @item_padding: the item padding
5517 *
5518 * Sets the `GtkIconView`:item-padding property which specifies the padding
5519 * around each of the icon view’s items.
5520 */
5521void
5522gtk_icon_view_set_item_padding (GtkIconView *icon_view,
5523 int item_padding)
5524{
5525 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
5526
5527 if (icon_view->priv->item_padding != item_padding)
5528 {
5529 icon_view->priv->item_padding = item_padding;
5530
5531 if (icon_view->priv->cell_area)
5532 gtk_cell_area_stop_editing (area: icon_view->priv->cell_area, TRUE);
5533
5534 gtk_icon_view_invalidate_sizes (icon_view);
5535
5536 g_object_notify (G_OBJECT (icon_view), property_name: "item-padding");
5537 }
5538}
5539
5540/**
5541 * gtk_icon_view_get_item_padding:
5542 * @icon_view: a `GtkIconView`
5543 *
5544 * Returns the value of the ::item-padding property.
5545 *
5546 * Returns: the padding around items
5547 */
5548int
5549gtk_icon_view_get_item_padding (GtkIconView *icon_view)
5550{
5551 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1);
5552
5553 return icon_view->priv->item_padding;
5554}
5555
5556/* Get/set whether drag_motion requested the drag data and
5557 * drag_data_received should thus not actually insert the data,
5558 * since the data doesn’t result from a drop.
5559 */
5560static void
5561set_status_pending (GdkDrop *drop,
5562 GdkDragAction suggested_action)
5563{
5564 g_object_set_data (G_OBJECT (drop),
5565 I_("gtk-icon-view-status-pending"),
5566 GINT_TO_POINTER (suggested_action));
5567}
5568
5569static GdkDragAction
5570get_status_pending (GdkDrop *drop)
5571{
5572 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drop),
5573 "gtk-icon-view-status-pending"));
5574}
5575
5576static void
5577unset_reorderable (GtkIconView *icon_view)
5578{
5579 if (icon_view->priv->reorderable)
5580 {
5581 icon_view->priv->reorderable = FALSE;
5582 g_object_notify (G_OBJECT (icon_view), property_name: "reorderable");
5583 }
5584}
5585
5586typedef struct
5587{
5588 GtkTreeRowReference *dest_row;
5589 gboolean empty_view_drop;
5590 gboolean drop_append_mode;
5591} DestRow;
5592
5593static void
5594dest_row_free (gpointer data)
5595{
5596 DestRow *dr = (DestRow *)data;
5597
5598 gtk_tree_row_reference_free (reference: dr->dest_row);
5599 g_free (mem: dr);
5600}
5601
5602static void
5603set_dest_row (GdkDrop *drop,
5604 GtkTreeModel *model,
5605 GtkTreePath *dest_row,
5606 gboolean empty_view_drop,
5607 gboolean drop_append_mode)
5608{
5609 DestRow *dr;
5610
5611 if (!dest_row)
5612 {
5613 g_object_set_data_full (G_OBJECT (drop),
5614 I_("gtk-icon-view-dest-row"),
5615 NULL, NULL);
5616 return;
5617 }
5618
5619 dr = g_new0 (DestRow, 1);
5620
5621 dr->dest_row = gtk_tree_row_reference_new (model, path: dest_row);
5622 dr->empty_view_drop = empty_view_drop;
5623 dr->drop_append_mode = drop_append_mode;
5624 g_object_set_data_full (G_OBJECT (drop),
5625 I_("gtk-icon-view-dest-row"),
5626 data: dr, destroy: (GDestroyNotify) dest_row_free);
5627}
5628
5629static GtkTreePath*
5630get_dest_row (GdkDrop *drop)
5631{
5632 DestRow *dr;
5633
5634 dr = g_object_get_data (G_OBJECT (drop), key: "gtk-icon-view-dest-row");
5635
5636 if (dr)
5637 {
5638 GtkTreePath *path = NULL;
5639
5640 if (dr->dest_row)
5641 path = gtk_tree_row_reference_get_path (reference: dr->dest_row);
5642 else if (dr->empty_view_drop)
5643 path = gtk_tree_path_new_from_indices (first_index: 0, -1);
5644 else
5645 path = NULL;
5646
5647 if (path && dr->drop_append_mode)
5648 gtk_tree_path_next (path);
5649
5650 return path;
5651 }
5652 else
5653 return NULL;
5654}
5655
5656static gboolean
5657check_model_dnd (GtkTreeModel *model,
5658 GType required_iface,
5659 const char *signal)
5660{
5661 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5662 {
5663 g_warning ("You must override the default '%s' handler "
5664 "on GtkIconView when using models that don't support "
5665 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5666 "is to connect to '%s' and call "
5667 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5668 "the default handler from running. Look at the source code "
5669 "for the default handler in gtkiconview.c to get an idea what "
5670 "your handler should do. (gtkiconview.c is in the GTK source "
5671 "code.) If you're using GTK from a language other than C, "
5672 "there may be a more natural way to override default handlers, e.g. via derivation.",
5673 signal, g_type_name (required_iface), signal);
5674 return FALSE;
5675 }
5676 else
5677 return TRUE;
5678}
5679
5680static void
5681remove_scroll_timeout (GtkIconView *icon_view)
5682{
5683 if (icon_view->priv->scroll_timeout_id != 0)
5684 {
5685 g_source_remove (tag: icon_view->priv->scroll_timeout_id);
5686
5687 icon_view->priv->scroll_timeout_id = 0;
5688 }
5689}
5690
5691static void
5692gtk_icon_view_autoscroll (GtkIconView *icon_view)
5693{
5694 int px, py, width, height;
5695 int hoffset, voffset;
5696
5697 px = icon_view->priv->event_last_x;
5698 py = icon_view->priv->event_last_y;
5699
5700 width = gtk_widget_get_width (GTK_WIDGET (icon_view));
5701 height = gtk_widget_get_height (GTK_WIDGET (icon_view));
5702
5703 /* see if we are near the edge. */
5704 voffset = py - 2 * SCROLL_EDGE_SIZE;
5705 if (voffset > 0)
5706 voffset = MAX (py - (height - 2 * SCROLL_EDGE_SIZE), 0);
5707
5708 hoffset = px - 2 * SCROLL_EDGE_SIZE;
5709 if (hoffset > 0)
5710 hoffset = MAX (px - (width - 2 * SCROLL_EDGE_SIZE), 0);
5711
5712 if (voffset != 0)
5713 gtk_adjustment_set_value (adjustment: icon_view->priv->vadjustment,
5714 value: gtk_adjustment_get_value (adjustment: icon_view->priv->vadjustment) + voffset);
5715
5716 if (hoffset != 0)
5717 gtk_adjustment_set_value (adjustment: icon_view->priv->hadjustment,
5718 value: gtk_adjustment_get_value (adjustment: icon_view->priv->hadjustment) + hoffset);
5719}
5720
5721static gboolean
5722drag_scroll_timeout (gpointer data)
5723{
5724 gtk_icon_view_autoscroll (icon_view: data);
5725
5726 return TRUE;
5727}
5728
5729static GdkDragAction
5730gtk_icon_view_get_action (GtkWidget *widget,
5731 GdkDrop *drop)
5732{
5733 GtkIconView *iconview = GTK_ICON_VIEW (widget);
5734 GdkDrag *drag = gdk_drop_get_drag (self: drop);
5735 GdkDragAction actions;
5736
5737 actions = gdk_drop_get_actions (self: drop);
5738
5739 if (drag == iconview->priv->drag &&
5740 actions & GDK_ACTION_MOVE)
5741 return GDK_ACTION_MOVE;
5742
5743 if (actions & GDK_ACTION_COPY)
5744 return GDK_ACTION_COPY;
5745
5746 if (actions & GDK_ACTION_MOVE)
5747 return GDK_ACTION_MOVE;
5748
5749 if (actions & GDK_ACTION_LINK)
5750 return GDK_ACTION_LINK;
5751
5752 return 0;
5753}
5754
5755static gboolean
5756set_destination (GtkIconView *icon_view,
5757 GdkDrop *drop,
5758 GtkDropTargetAsync *dest,
5759 int x,
5760 int y,
5761 GdkDragAction *suggested_action,
5762 GType *target)
5763{
5764 GtkWidget *widget;
5765 GtkTreePath *path = NULL;
5766 GtkIconViewDropPosition pos;
5767 GtkIconViewDropPosition old_pos;
5768 GtkTreePath *old_dest_path = NULL;
5769 GdkContentFormats *formats;
5770 gboolean can_drop = FALSE;
5771
5772 widget = GTK_WIDGET (icon_view);
5773
5774 *suggested_action = 0;
5775 *target = G_TYPE_INVALID;
5776
5777 if (!icon_view->priv->dest_set)
5778 {
5779 /* someone unset us as a drag dest, note that if
5780 * we return FALSE drag_leave isn't called
5781 */
5782
5783 gtk_icon_view_set_drag_dest_item (icon_view,
5784 NULL,
5785 pos: GTK_ICON_VIEW_DROP_LEFT);
5786
5787 remove_scroll_timeout (GTK_ICON_VIEW (widget));
5788
5789 return FALSE; /* no longer a drop site */
5790 }
5791
5792 formats = gtk_drop_target_async_get_formats (self: dest);
5793 *target = gdk_content_formats_match_gtype (first: formats, second: formats);
5794 if (*target == G_TYPE_INVALID)
5795 return FALSE;
5796
5797 if (!gtk_icon_view_get_dest_item_at_pos (icon_view, drag_x: x, drag_y: y, path: &path, pos: &pos))
5798 {
5799 int n_children;
5800 GtkTreeModel *model;
5801
5802 /* the row got dropped on empty space, let's setup a special case
5803 */
5804
5805 if (path)
5806 gtk_tree_path_free (path);
5807
5808 model = gtk_icon_view_get_model (icon_view);
5809
5810 n_children = gtk_tree_model_iter_n_children (tree_model: model, NULL);
5811 if (n_children)
5812 {
5813 pos = GTK_ICON_VIEW_DROP_BELOW;
5814 path = gtk_tree_path_new_from_indices (first_index: n_children - 1, -1);
5815 }
5816 else
5817 {
5818 pos = GTK_ICON_VIEW_DROP_ABOVE;
5819 path = gtk_tree_path_new_from_indices (first_index: 0, -1);
5820 }
5821
5822 can_drop = TRUE;
5823
5824 goto out;
5825 }
5826
5827 g_assert (path);
5828
5829 gtk_icon_view_get_drag_dest_item (icon_view,
5830 path: &old_dest_path,
5831 pos: &old_pos);
5832
5833 if (old_dest_path)
5834 gtk_tree_path_free (path: old_dest_path);
5835
5836 if (TRUE /* FIXME if the location droppable predicate */)
5837 {
5838 can_drop = TRUE;
5839 }
5840
5841out:
5842 if (can_drop)
5843 {
5844 *suggested_action = gtk_icon_view_get_action (widget, drop);
5845
5846 gtk_icon_view_set_drag_dest_item (GTK_ICON_VIEW (widget),
5847 path, pos);
5848 }
5849 else
5850 {
5851 /* can't drop here */
5852 gtk_icon_view_set_drag_dest_item (GTK_ICON_VIEW (widget),
5853 NULL,
5854 pos: GTK_ICON_VIEW_DROP_LEFT);
5855 }
5856
5857 if (path)
5858 gtk_tree_path_free (path);
5859
5860 return TRUE;
5861}
5862
5863static GtkTreePath*
5864get_logical_destination (GtkIconView *icon_view,
5865 gboolean *drop_append_mode)
5866{
5867 /* adjust path to point to the row the drop goes in front of */
5868 GtkTreePath *path = NULL;
5869 GtkIconViewDropPosition pos;
5870
5871 *drop_append_mode = FALSE;
5872
5873 gtk_icon_view_get_drag_dest_item (icon_view, path: &path, pos: &pos);
5874
5875 if (path == NULL)
5876 return NULL;
5877
5878 if (pos == GTK_ICON_VIEW_DROP_RIGHT ||
5879 pos == GTK_ICON_VIEW_DROP_BELOW)
5880 {
5881 GtkTreeIter iter;
5882 GtkTreeModel *model = icon_view->priv->model;
5883
5884 if (!gtk_tree_model_get_iter (tree_model: model, iter: &iter, path) ||
5885 !gtk_tree_model_iter_next (tree_model: model, iter: &iter))
5886 *drop_append_mode = TRUE;
5887 else
5888 {
5889 *drop_append_mode = FALSE;
5890 gtk_tree_path_next (path);
5891 }
5892 }
5893
5894 return path;
5895}
5896
5897static gboolean
5898gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view,
5899 double x,
5900 double y,
5901 GdkDevice *device)
5902{
5903 GtkTreePath *path = NULL;
5904 GtkTreeModel *model;
5905 gboolean retval = FALSE;
5906 GdkContentProvider *content;
5907 GdkPaintable *icon;
5908 GtkIconViewItem *item;
5909 GdkSurface *surface;
5910 GdkDrag *drag;
5911
5912 if (!icon_view->priv->source_set)
5913 goto out;
5914
5915 if (icon_view->priv->pressed_button < 0)
5916 goto out;
5917
5918 if (!gtk_drag_check_threshold_double (GTK_WIDGET (icon_view),
5919 start_x: icon_view->priv->press_start_x,
5920 start_y: icon_view->priv->press_start_y,
5921 current_x: x, current_y: y))
5922 goto out;
5923
5924 model = gtk_icon_view_get_model (icon_view);
5925
5926 if (model == NULL)
5927 goto out;
5928
5929 icon_view->priv->pressed_button = -1;
5930
5931 item = _gtk_icon_view_get_item_at_coords (icon_view,
5932 x: icon_view->priv->press_start_x,
5933 y: icon_view->priv->press_start_y,
5934 TRUE,
5935 NULL);
5936
5937 if (item == NULL)
5938 goto out;
5939
5940 path = gtk_tree_path_new_from_indices (first_index: item->index, -1);
5941
5942 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
5943 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model), path))
5944 goto out;
5945
5946 /* FIXME Check whether we're a start button, if not return FALSE and
5947 * free path
5948 */
5949
5950 /* Now we can begin the drag */
5951
5952 retval = TRUE;
5953
5954 surface = gtk_native_get_surface (self: gtk_widget_get_native (GTK_WIDGET (icon_view)));
5955
5956 content = gtk_icon_view_drag_data_get (icon_view, source_row: path);
5957 if (content == NULL)
5958 goto out;
5959
5960 drag = gdk_drag_begin (surface,
5961 device,
5962 content,
5963 actions: icon_view->priv->source_actions,
5964 dx: icon_view->priv->press_start_x,
5965 dy: icon_view->priv->press_start_y);
5966
5967 g_object_unref (object: content);
5968
5969 g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_icon_view_dnd_finished_cb), icon_view);
5970
5971 icon_view->priv->source_item = gtk_tree_row_reference_new (model, path);
5972
5973 x = icon_view->priv->press_start_x - item->cell_area.x + icon_view->priv->item_padding;
5974 y = icon_view->priv->press_start_y - item->cell_area.y + icon_view->priv->item_padding;
5975
5976 icon = gtk_icon_view_create_drag_icon (icon_view, path);
5977 gtk_drag_icon_set_from_paintable (drag, paintable: icon, hot_x: x, hot_y: y);
5978 g_object_unref (object: icon);
5979
5980 icon_view->priv->drag = drag;
5981
5982 g_object_unref (object: drag);
5983
5984 out:
5985 if (path)
5986 gtk_tree_path_free (path);
5987
5988 return retval;
5989}
5990
5991/* Source side drag signals */
5992static GdkContentProvider *
5993gtk_icon_view_drag_data_get (GtkIconView *icon_view,
5994 GtkTreePath *source_row)
5995{
5996 GdkContentProvider *content;
5997 GtkTreeModel *model;
5998
5999 model = gtk_icon_view_get_model (icon_view);
6000
6001 if (model == NULL)
6002 return NULL;
6003
6004 if (!icon_view->priv->source_set)
6005 return NULL;
6006
6007 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6008 * any model; for DragSource models there are some other formats
6009 * we also support.
6010 */
6011
6012 if (GTK_IS_TREE_DRAG_SOURCE (model))
6013 content = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model), path: source_row);
6014 else
6015 content = NULL;
6016
6017 /* If drag_data_get does nothing, try providing row data. */
6018 if (content == NULL)
6019 content = gtk_tree_create_row_drag_content (tree_model: model, path: source_row);
6020
6021 return content;
6022}
6023
6024static void
6025gtk_icon_view_dnd_finished_cb (GdkDrag *drag,
6026 GtkWidget *widget)
6027{
6028 GtkTreeModel *model;
6029 GtkIconView *icon_view;
6030 GtkTreePath *source_row;
6031
6032 if (gdk_drag_get_selected_action (drag) != GDK_ACTION_MOVE)
6033 return;
6034
6035 icon_view = GTK_ICON_VIEW (widget);
6036 model = gtk_icon_view_get_model (icon_view);
6037
6038 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, signal: "drag-data-delete"))
6039 return;
6040
6041 if (!icon_view->priv->source_set)
6042 return;
6043
6044 source_row = gtk_tree_row_reference_get_path (reference: icon_view->priv->source_item);
6045 if (source_row == NULL)
6046 return;
6047
6048 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), path: source_row);
6049
6050 gtk_tree_path_free (path: source_row);
6051
6052 g_clear_pointer (&icon_view->priv->source_item, gtk_tree_row_reference_free);
6053 icon_view->priv->drag = NULL;
6054}
6055
6056/* Target side drag signals */
6057static void
6058gtk_icon_view_drag_leave (GtkDropTargetAsync *dest,
6059 GdkDrop *drop,
6060 GtkIconView *icon_view)
6061{
6062 /* unset any highlight row */
6063 gtk_icon_view_set_drag_dest_item (icon_view,
6064 NULL,
6065 pos: GTK_ICON_VIEW_DROP_LEFT);
6066
6067 remove_scroll_timeout (icon_view);
6068}
6069
6070static GdkDragAction
6071gtk_icon_view_drag_motion (GtkDropTargetAsync *dest,
6072 GdkDrop *drop,
6073 double x,
6074 double y,
6075 GtkIconView *icon_view)
6076{
6077 GtkTreePath *path = NULL;
6078 GtkIconViewDropPosition pos;
6079 GdkDragAction suggested_action = 0;
6080 GType target;
6081 gboolean empty;
6082 GdkDragAction result;
6083
6084 if (!set_destination (icon_view, drop, dest, x, y, suggested_action: &suggested_action, target: &target))
6085 return 0;
6086
6087 gtk_icon_view_get_drag_dest_item (icon_view, path: &path, pos: &pos);
6088
6089 /* we only know this *after* set_desination_row */
6090 empty = icon_view->priv->empty_view_drop;
6091
6092 if (path == NULL && !empty)
6093 {
6094 /* Can't drop here. */
6095 result = 0;
6096 }
6097 else
6098 {
6099 if (icon_view->priv->scroll_timeout_id == 0)
6100 {
6101 icon_view->priv->scroll_timeout_id = g_timeout_add (interval: 50, function: drag_scroll_timeout, data: icon_view);
6102 gdk_source_set_static_name_by_id (tag: icon_view->priv->scroll_timeout_id, name: "[gtk] drag_scroll_timeout");
6103 }
6104
6105 if (target == GTK_TYPE_TREE_ROW_DATA)
6106 {
6107 /* Request data so we can use the source row when
6108 * determining whether to accept the drop
6109 */
6110 set_status_pending (drop, suggested_action);
6111 gdk_drop_read_value_async (self: drop, type: target, G_PRIORITY_DEFAULT, NULL, callback: gtk_icon_view_drag_data_received, user_data: icon_view);
6112 }
6113 else
6114 {
6115 set_status_pending (drop, suggested_action: 0);
6116 }
6117 result = suggested_action;
6118 }
6119
6120 if (path)
6121 gtk_tree_path_free (path);
6122
6123 return result;
6124}
6125
6126static gboolean
6127gtk_icon_view_drag_drop (GtkDropTargetAsync *dest,
6128 GdkDrop *drop,
6129 double x,
6130 double y,
6131 GtkIconView *icon_view)
6132{
6133 GtkTreePath *path;
6134 GdkDragAction suggested_action = 0;
6135 GType target = G_TYPE_INVALID;
6136 GtkTreeModel *model;
6137 gboolean drop_append_mode;
6138
6139 model = gtk_icon_view_get_model (icon_view);
6140
6141 remove_scroll_timeout (icon_view);
6142
6143 if (!icon_view->priv->dest_set)
6144 return FALSE;
6145
6146 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, signal: "drop"))
6147 return FALSE;
6148
6149 if (!set_destination (icon_view, drop, dest, x, y, suggested_action: &suggested_action, target: &target))
6150 return FALSE;
6151
6152 path = get_logical_destination (icon_view, drop_append_mode: &drop_append_mode);
6153
6154 if (target != G_TYPE_INVALID && path != NULL)
6155 {
6156 /* in case a motion had requested drag data, change things so we
6157 * treat drag data receives as a drop.
6158 */
6159 set_status_pending (drop, suggested_action: 0);
6160 set_dest_row (drop, model, dest_row: path,
6161 empty_view_drop: icon_view->priv->empty_view_drop, drop_append_mode);
6162 }
6163
6164 if (path)
6165 gtk_tree_path_free (path);
6166
6167 /* Unset this thing */
6168 gtk_icon_view_set_drag_dest_item (icon_view, NULL, pos: GTK_ICON_VIEW_DROP_LEFT);
6169
6170 if (target != G_TYPE_INVALID)
6171 {
6172 gdk_drop_read_value_async (self: drop, type: target, G_PRIORITY_DEFAULT, NULL, callback: gtk_icon_view_drag_data_received, user_data: icon_view);
6173 return TRUE;
6174 }
6175 else
6176 return FALSE;
6177}
6178
6179static void
6180gtk_icon_view_drag_data_received (GObject *source,
6181 GAsyncResult *result,
6182 gpointer data)
6183{
6184 GtkIconView *icon_view = data;
6185 GdkDrop *drop = GDK_DROP (source);
6186 GtkTreePath *path;
6187 GtkTreeModel *model;
6188 GtkTreePath *dest_row;
6189 GdkDragAction suggested_action;
6190 gboolean drop_append_mode;
6191 const GValue *value;
6192
6193 value = gdk_drop_read_value_finish (self: drop, result, NULL);
6194 if (!value)
6195 return;
6196
6197 model = gtk_icon_view_get_model (icon_view);
6198
6199 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, signal: "drag-data-received"))
6200 return;
6201
6202 if (!icon_view->priv->dest_set)
6203 return;
6204
6205 suggested_action = get_status_pending (drop);
6206
6207 if (suggested_action)
6208 {
6209 /* We are getting this data due to a request in drag_motion,
6210 * rather than due to a request in drag_drop, so we are just
6211 * supposed to call drag_status, not actually paste in the
6212 * data.
6213 */
6214 path = get_logical_destination (icon_view, drop_append_mode: &drop_append_mode);
6215
6216 if (path == NULL)
6217 suggested_action = 0;
6218
6219 if (suggested_action)
6220 {
6221 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6222 dest_path: path,
6223 value))
6224 suggested_action = 0;
6225 }
6226
6227 if (path)
6228 gtk_tree_path_free (path);
6229
6230 /* If you can't drop, remove user drop indicator until the next motion */
6231 if (suggested_action == 0)
6232 gtk_icon_view_set_drag_dest_item (icon_view,
6233 NULL,
6234 pos: GTK_ICON_VIEW_DROP_LEFT);
6235 return;
6236 }
6237
6238
6239 dest_row = get_dest_row (drop);
6240
6241 if (dest_row == NULL)
6242 return;
6243
6244 suggested_action = gtk_icon_view_get_action (GTK_WIDGET (icon_view), drop);
6245
6246 if (suggested_action &&
6247 !gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6248 dest: dest_row,
6249 value))
6250 suggested_action = 0;
6251
6252 gdk_drop_finish (self: drop, action: suggested_action);
6253
6254 gtk_tree_path_free (path: dest_row);
6255
6256 /* drop dest_row */
6257 set_dest_row (drop, NULL, NULL, FALSE, FALSE);
6258}
6259
6260/* Drag-and-Drop support */
6261
6262/**
6263 * gtk_icon_view_enable_model_drag_source:
6264 * @icon_view: a `GtkIconView`
6265 * @start_button_mask: Mask of allowed buttons to start drag
6266 * @formats: the formats that the drag will support
6267 * @actions: the bitmask of possible actions for a drag from this
6268 * widget
6269 *
6270 * Turns @icon_view into a drag source for automatic DND. Calling this
6271 * method sets `GtkIconView`:reorderable to %FALSE.
6272 **/
6273void
6274gtk_icon_view_enable_model_drag_source (GtkIconView *icon_view,
6275 GdkModifierType start_button_mask,
6276 GdkContentFormats *formats,
6277 GdkDragAction actions)
6278{
6279 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
6280
6281 icon_view->priv->source_formats = gdk_content_formats_ref (formats);
6282 icon_view->priv->source_actions = actions;
6283
6284 icon_view->priv->source_set = TRUE;
6285
6286 unset_reorderable (icon_view);
6287}
6288
6289/**
6290 * gtk_icon_view_enable_model_drag_dest:
6291 * @icon_view: a `GtkIconView`
6292 * @formats: the formats that the drag will support
6293 * @actions: the bitmask of possible actions for a drag to this
6294 * widget
6295 *
6296 * Turns @icon_view into a drop destination for automatic DND. Calling this
6297 * method sets `GtkIconView`:reorderable to %FALSE.
6298 **/
6299void
6300gtk_icon_view_enable_model_drag_dest (GtkIconView *icon_view,
6301 GdkContentFormats *formats,
6302 GdkDragAction actions)
6303{
6304 GtkCssNode *widget_node;
6305
6306 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
6307
6308 icon_view->priv->dest = gtk_drop_target_async_new (formats: gdk_content_formats_ref (formats), actions);
6309 g_signal_connect (icon_view->priv->dest, "drag-leave", G_CALLBACK (gtk_icon_view_drag_leave), icon_view);
6310 g_signal_connect (icon_view->priv->dest, "drag-enter", G_CALLBACK (gtk_icon_view_drag_motion), icon_view);
6311 g_signal_connect (icon_view->priv->dest, "drag-motion", G_CALLBACK (gtk_icon_view_drag_motion), icon_view);
6312 g_signal_connect (icon_view->priv->dest, "drop", G_CALLBACK (gtk_icon_view_drag_drop), icon_view);
6313 gtk_widget_add_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest));
6314
6315 icon_view->priv->dest_actions = actions;
6316
6317 icon_view->priv->dest_set = TRUE;
6318
6319 unset_reorderable (icon_view);
6320
6321 widget_node = gtk_widget_get_css_node (GTK_WIDGET (icon_view));
6322 icon_view->priv->dndnode = gtk_css_node_new ();
6323 gtk_css_node_set_name (cssnode: icon_view->priv->dndnode, name: g_quark_from_static_string (string: "dndtarget"));
6324 gtk_css_node_set_parent (cssnode: icon_view->priv->dndnode, parent: widget_node);
6325 gtk_css_node_set_state (cssnode: icon_view->priv->dndnode, state_flags: gtk_css_node_get_state (cssnode: widget_node));
6326 g_object_unref (object: icon_view->priv->dndnode);
6327}
6328
6329/**
6330 * gtk_icon_view_unset_model_drag_source:
6331 * @icon_view: a `GtkIconView`
6332 *
6333 * Undoes the effect of gtk_icon_view_enable_model_drag_source(). Calling this
6334 * method sets `GtkIconView`:reorderable to %FALSE.
6335 **/
6336void
6337gtk_icon_view_unset_model_drag_source (GtkIconView *icon_view)
6338{
6339 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
6340
6341 if (icon_view->priv->source_set)
6342 {
6343 g_clear_pointer (&icon_view->priv->source_formats, gdk_content_formats_unref);
6344 icon_view->priv->source_set = FALSE;
6345 }
6346
6347 unset_reorderable (icon_view);
6348}
6349
6350/**
6351 * gtk_icon_view_unset_model_drag_dest:
6352 * @icon_view: a `GtkIconView`
6353 *
6354 * Undoes the effect of gtk_icon_view_enable_model_drag_dest(). Calling this
6355 * method sets `GtkIconView`:reorderable to %FALSE.
6356 **/
6357void
6358gtk_icon_view_unset_model_drag_dest (GtkIconView *icon_view)
6359{
6360 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
6361
6362 if (icon_view->priv->dest_set)
6363 {
6364 gtk_widget_remove_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest));
6365 icon_view->priv->dest = NULL;
6366 icon_view->priv->dest_set = FALSE;
6367
6368 gtk_css_node_set_parent (cssnode: icon_view->priv->dndnode, NULL);
6369 icon_view->priv->dndnode = NULL;
6370 }
6371
6372 unset_reorderable (icon_view);
6373}
6374
6375/* These are useful to implement your own custom stuff. */
6376/**
6377 * gtk_icon_view_set_drag_dest_item:
6378 * @icon_view: a `GtkIconView`
6379 * @path: (nullable): The path of the item to highlight
6380 * @pos: Specifies where to drop, relative to the item
6381 *
6382 * Sets the item that is highlighted for feedback.
6383 */
6384void
6385gtk_icon_view_set_drag_dest_item (GtkIconView *icon_view,
6386 GtkTreePath *path,
6387 GtkIconViewDropPosition pos)
6388{
6389 /* Note; this function is exported to allow a custom DND
6390 * implementation, so it can't touch TreeViewDragInfo
6391 */
6392
6393 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
6394
6395 if (icon_view->priv->dest_item)
6396 {
6397 GtkTreePath *current_path;
6398 current_path = gtk_tree_row_reference_get_path (reference: icon_view->priv->dest_item);
6399 gtk_tree_row_reference_free (reference: icon_view->priv->dest_item);
6400 icon_view->priv->dest_item = NULL;
6401
6402 gtk_icon_view_queue_draw_path (icon_view, path: current_path);
6403 gtk_tree_path_free (path: current_path);
6404 }
6405
6406 /* special case a drop on an empty model */
6407 icon_view->priv->empty_view_drop = FALSE;
6408 if (pos == GTK_ICON_VIEW_DROP_ABOVE && path
6409 && gtk_tree_path_get_depth (path) == 1
6410 && gtk_tree_path_get_indices (path)[0] == 0)
6411 {
6412 int n_children;
6413
6414 n_children = gtk_tree_model_iter_n_children (tree_model: icon_view->priv->model,
6415 NULL);
6416
6417 if (n_children == 0)
6418 icon_view->priv->empty_view_drop = TRUE;
6419 }
6420
6421 icon_view->priv->dest_pos = pos;
6422
6423 if (path)
6424 {
6425 icon_view->priv->dest_item =
6426 gtk_tree_row_reference_new_proxy (G_OBJECT (icon_view),
6427 model: icon_view->priv->model, path);
6428
6429 gtk_icon_view_queue_draw_path (icon_view, path);
6430 }
6431}
6432
6433/**
6434 * gtk_icon_view_get_drag_dest_item:
6435 * @icon_view: a `GtkIconView`
6436 * @path: (out) (nullable) (optional): Return location for the path of
6437 * the highlighted item
6438 * @pos: (out) (optional): Return location for the drop position
6439 *
6440 * Gets information about the item that is highlighted for feedback.
6441 */
6442void
6443gtk_icon_view_get_drag_dest_item (GtkIconView *icon_view,
6444 GtkTreePath **path,
6445 GtkIconViewDropPosition *pos)
6446{
6447 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
6448
6449 if (path)
6450 {
6451 if (icon_view->priv->dest_item)
6452 *path = gtk_tree_row_reference_get_path (reference: icon_view->priv->dest_item);
6453 else
6454 *path = NULL;
6455 }
6456
6457 if (pos)
6458 *pos = icon_view->priv->dest_pos;
6459}
6460
6461/**
6462 * gtk_icon_view_get_dest_item_at_pos:
6463 * @icon_view: a `GtkIconView`
6464 * @drag_x: the position to determine the destination item for
6465 * @drag_y: the position to determine the destination item for
6466 * @path: (out) (optional): Return location for the path of the item
6467 * @pos: (out) (optional): Return location for the drop position
6468 *
6469 * Determines the destination item for a given position.
6470 *
6471 * Returns: whether there is an item at the given position.
6472 **/
6473gboolean
6474gtk_icon_view_get_dest_item_at_pos (GtkIconView *icon_view,
6475 int drag_x,
6476 int drag_y,
6477 GtkTreePath **path,
6478 GtkIconViewDropPosition *pos)
6479{
6480 GtkIconViewItem *item;
6481
6482 /* Note; this function is exported to allow a custom DND
6483 * implementation, so it can't touch TreeViewDragInfo
6484 */
6485
6486 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE);
6487 g_return_val_if_fail (drag_x >= 0, FALSE);
6488 g_return_val_if_fail (drag_y >= 0, FALSE);
6489
6490
6491 if (path)
6492 *path = NULL;
6493
6494 item = _gtk_icon_view_get_item_at_coords (icon_view,
6495 x: drag_x + gtk_adjustment_get_value (adjustment: icon_view->priv->hadjustment),
6496 y: drag_y + gtk_adjustment_get_value (adjustment: icon_view->priv->vadjustment),
6497 FALSE, NULL);
6498
6499 if (item == NULL)
6500 return FALSE;
6501
6502 if (path)
6503 *path = gtk_tree_path_new_from_indices (first_index: item->index, -1);
6504
6505 if (pos)
6506 {
6507 if (drag_x < item->cell_area.x + item->cell_area.width / 4)
6508 *pos = GTK_ICON_VIEW_DROP_LEFT;
6509 else if (drag_x > item->cell_area.x + item->cell_area.width * 3 / 4)
6510 *pos = GTK_ICON_VIEW_DROP_RIGHT;
6511 else if (drag_y < item->cell_area.y + item->cell_area.height / 4)
6512 *pos = GTK_ICON_VIEW_DROP_ABOVE;
6513 else if (drag_y > item->cell_area.y + item->cell_area.height * 3 / 4)
6514 *pos = GTK_ICON_VIEW_DROP_BELOW;
6515 else
6516 *pos = GTK_ICON_VIEW_DROP_INTO;
6517 }
6518
6519 return TRUE;
6520}
6521
6522/**
6523 * gtk_icon_view_create_drag_icon:
6524 * @icon_view: a `GtkIconView`
6525 * @path: a `GtkTreePath` in @icon_view
6526 *
6527 * Creates a `GdkPaintable` representation of the item at @path.
6528 * This image is used for a drag icon.
6529 *
6530 * Returns: (transfer full) (nullable): a newly-allocated `GdkPaintable` of the drag icon.
6531 **/
6532GdkPaintable *
6533gtk_icon_view_create_drag_icon (GtkIconView *icon_view,
6534 GtkTreePath *path)
6535{
6536 GtkWidget *widget;
6537 GtkSnapshot *snapshot;
6538 GdkPaintable *paintable;
6539 GList *l;
6540 int index;
6541
6542 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL);
6543 g_return_val_if_fail (path != NULL, NULL);
6544
6545 widget = GTK_WIDGET (icon_view);
6546
6547 if (!gtk_widget_get_realized (widget))
6548 return NULL;
6549
6550 index = gtk_tree_path_get_indices (path)[0];
6551
6552 for (l = icon_view->priv->items; l; l = l->next)
6553 {
6554 GtkIconViewItem *item = l->data;
6555
6556 if (index == item->index)
6557 {
6558 snapshot = gtk_snapshot_new ();
6559 gtk_icon_view_snapshot_item (icon_view, snapshot, item,
6560 x: icon_view->priv->item_padding,
6561 y: icon_view->priv->item_padding,
6562 FALSE);
6563 paintable = gtk_snapshot_free_to_paintable (snapshot, NULL);
6564
6565 return paintable;
6566 }
6567 }
6568
6569 return NULL;
6570}
6571
6572/**
6573 * gtk_icon_view_get_reorderable:
6574 * @icon_view: a `GtkIconView`
6575 *
6576 * Retrieves whether the user can reorder the list via drag-and-drop.
6577 * See gtk_icon_view_set_reorderable().
6578 *
6579 * Returns: %TRUE if the list can be reordered.
6580 **/
6581gboolean
6582gtk_icon_view_get_reorderable (GtkIconView *icon_view)
6583{
6584 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE);
6585
6586 return icon_view->priv->reorderable;
6587}
6588
6589/**
6590 * gtk_icon_view_set_reorderable:
6591 * @icon_view: A `GtkIconView`.
6592 * @reorderable: %TRUE, if the list of items can be reordered.
6593 *
6594 * This function is a convenience function to allow you to reorder models that
6595 * support the `GtkTreeDragSourceIface` and the `GtkTreeDragDestIface`. Both
6596 * `GtkTreeStore` and `GtkListStore` support these. If @reorderable is %TRUE, then
6597 * the user can reorder the model by dragging and dropping rows. The
6598 * developer can listen to these changes by connecting to the model's
6599 * row_inserted and row_deleted signals. The reordering is implemented by setting up
6600 * the icon view as a drag source and destination. Therefore, drag and
6601 * drop can not be used in a reorderable view for any other purpose.
6602 *
6603 * This function does not give you any degree of control over the order -- any
6604 * reordering is allowed. If more control is needed, you should probably
6605 * handle drag and drop manually.
6606 **/
6607void
6608gtk_icon_view_set_reorderable (GtkIconView *icon_view,
6609 gboolean reorderable)
6610{
6611 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
6612
6613 reorderable = reorderable != FALSE;
6614
6615 if (icon_view->priv->reorderable == reorderable)
6616 return;
6617
6618 if (reorderable)
6619 {
6620 GdkContentFormats *formats = gdk_content_formats_new_for_gtype (GTK_TYPE_TREE_ROW_DATA);
6621 gtk_icon_view_enable_model_drag_source (icon_view,
6622 start_button_mask: GDK_BUTTON1_MASK,
6623 formats,
6624 actions: GDK_ACTION_MOVE);
6625 gtk_icon_view_enable_model_drag_dest (icon_view,
6626 formats,
6627 actions: GDK_ACTION_MOVE);
6628 gdk_content_formats_unref (formats);
6629 }
6630 else
6631 {
6632 gtk_icon_view_unset_model_drag_source (icon_view);
6633 gtk_icon_view_unset_model_drag_dest (icon_view);
6634 }
6635
6636 icon_view->priv->reorderable = reorderable;
6637
6638 g_object_notify (G_OBJECT (icon_view), property_name: "reorderable");
6639}
6640
6641/**
6642 * gtk_icon_view_set_activate_on_single_click:
6643 * @icon_view: a `GtkIconView`
6644 * @single: %TRUE to emit item-activated on a single click
6645 *
6646 * Causes the `GtkIconView`::item-activated signal to be emitted on
6647 * a single click instead of a double click.
6648 **/
6649void
6650gtk_icon_view_set_activate_on_single_click (GtkIconView *icon_view,
6651 gboolean single)
6652{
6653 g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
6654
6655 single = single != FALSE;
6656
6657 if (icon_view->priv->activate_on_single_click == single)
6658 return;
6659
6660 icon_view->priv->activate_on_single_click = single;
6661 g_object_notify (G_OBJECT (icon_view), property_name: "activate-on-single-click");
6662}
6663
6664/**
6665 * gtk_icon_view_get_activate_on_single_click:
6666 * @icon_view: a `GtkIconView`
6667 *
6668 * Gets the setting set by gtk_icon_view_set_activate_on_single_click().
6669 *
6670 * Returns: %TRUE if item-activated will be emitted on a single click
6671 **/
6672gboolean
6673gtk_icon_view_get_activate_on_single_click (GtkIconView *icon_view)
6674{
6675 g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE);
6676
6677 return icon_view->priv->activate_on_single_click;
6678}
6679
6680static gboolean
6681gtk_icon_view_buildable_custom_tag_start (GtkBuildable *buildable,
6682 GtkBuilder *builder,
6683 GObject *child,
6684 const char *tagname,
6685 GtkBuildableParser *parser,
6686 gpointer *data)
6687{
6688 if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
6689 tagname, parser, data))
6690 return TRUE;
6691
6692 return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
6693 tagname, parser, data);
6694}
6695
6696static void
6697gtk_icon_view_buildable_custom_tag_end (GtkBuildable *buildable,
6698 GtkBuilder *builder,
6699 GObject *child,
6700 const char *tagname,
6701 gpointer data)
6702{
6703 if (!_gtk_cell_layout_buildable_custom_tag_end (buildable, builder,
6704 child, tagname, data))
6705 parent_buildable_iface->custom_tag_end (buildable, builder,
6706 child, tagname, data);
6707}
6708

source code of gtk/gtk/gtkiconview.c