X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkiconview.c;h=0c6dc038ce354701c740569e7f2b9e35d77f2ef9;hb=b9e189150bfd62c9e956e29324622058672600f0;hp=29a31ed7661c71730088b37bf66d2b31267a08d6;hpb=2353d60b8a7a4f2ba619a4226fb7df04f126334a;p=~andy%2Fgtk diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c index 29a31ed76..0c6dc038c 100644 --- a/gtk/gtkiconview.c +++ b/gtk/gtkiconview.c @@ -44,7 +44,7 @@ #include "gtktreednd.h" #include "gtktypebuiltins.h" #include "gtkprivate.h" -#include "a11y/gtkiconviewaccessible.h" +#include "a11y/gtkiconviewaccessibleprivate.h" /** * SECTION:gtkiconview @@ -66,8 +66,6 @@ #define SCROLL_EDGE_SIZE 15 -#define GTK_ICON_VIEW_PRIORITY_LAYOUT (GDK_PRIORITY_REDRAW + 5) - typedef struct _GtkIconViewChild GtkIconViewChild; struct _GtkIconViewChild { @@ -109,12 +107,11 @@ enum PROP_TOOLTIP_COLUMN, PROP_ITEM_PADDING, PROP_CELL_AREA, - - /* For scrollable interface */ PROP_HADJUSTMENT, PROP_VADJUSTMENT, PROP_HSCROLL_POLICY, - PROP_VSCROLL_POLICY + PROP_VSCROLL_POLICY, + PROP_ACTIVATE_ON_SINGLE_CLICK }; /* GObject vfuncs */ @@ -138,12 +135,23 @@ static void gtk_icon_view_unrealize (GtkWidget static void gtk_icon_view_style_updated (GtkWidget *widget); static void gtk_icon_view_state_flags_changed (GtkWidget *widget, GtkStateFlags previous_state); +static GtkSizeRequestMode gtk_icon_view_get_request_mode (GtkWidget *widget); static void gtk_icon_view_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural); +static void gtk_icon_view_get_preferred_width_for_height + (GtkWidget *widget, + gint height, + gint *minimum, + gint *natural); static void gtk_icon_view_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural); +static void gtk_icon_view_get_preferred_height_for_width + (GtkWidget *widget, + gint width, + gint *minimum, + gint *natural); static void gtk_icon_view_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static gboolean gtk_icon_view_draw (GtkWidget *widget, @@ -197,7 +205,6 @@ static void gtk_icon_view_queue_draw_path (GtkIco GtkTreePath *path); static void gtk_icon_view_queue_draw_item (GtkIconView *icon_view, GtkIconViewItem *item); -static void gtk_icon_view_queue_layout (GtkIconView *icon_view); static void gtk_icon_view_start_rubberbanding (GtkIconView *icon_view, GdkDevice *device, gint x, @@ -211,7 +218,6 @@ static gboolean gtk_icon_view_item_hit_test (GtkIco gint width, gint height); static gboolean gtk_icon_view_unselect_all_internal (GtkIconView *icon_view); -static void gtk_icon_view_cache_widths (GtkIconView *icon_view); static void gtk_icon_view_update_rubberband (gpointer data); static void gtk_icon_view_item_invalidate_size (GtkIconViewItem *item); static void gtk_icon_view_invalidate_sizes (GtkIconView *icon_view); @@ -255,9 +261,6 @@ static void gtk_icon_view_remove_editable (GtkCel GtkCellRenderer *renderer, GtkCellEditable *editable, GtkIconView *icon_view); -static void gtk_icon_view_context_changed (GtkCellAreaContext *context, - GParamSpec *pspec, - GtkIconView *icon_view); static void update_text_cell (GtkIconView *icon_view); static void update_pixbuf_cell (GtkIconView *icon_view); @@ -349,8 +352,11 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) widget_class->realize = gtk_icon_view_realize; widget_class->unrealize = gtk_icon_view_unrealize; widget_class->style_updated = gtk_icon_view_style_updated; + widget_class->get_request_mode = gtk_icon_view_get_request_mode; widget_class->get_preferred_width = gtk_icon_view_get_preferred_width; widget_class->get_preferred_height = gtk_icon_view_get_preferred_height; + widget_class->get_preferred_width_for_height = gtk_icon_view_get_preferred_width_for_height; + widget_class->get_preferred_height_for_width = gtk_icon_view_get_preferred_height_for_width; widget_class->size_allocate = gtk_icon_view_size_allocate; widget_class->draw = gtk_icon_view_draw; widget_class->motion_notify_event = gtk_icon_view_motion; @@ -637,6 +643,22 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) GTK_TYPE_CELL_AREA, GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + /** + * GtkIconView:activate-on-single-click: + * + * The activate-on-single-click property specifies whether the "item-activated" signal + * will be emitted after a single click. + * + * Since: 3.8 + */ + g_object_class_install_property (gobject_class, + PROP_ACTIVATE_ON_SINGLE_CLICK, + g_param_spec_boolean ("activate-on-single-click", + P_("Activate on Single Click"), + P_("Activate row on a single click"), + FALSE, + GTK_PARAM_READWRITE)); + /* Scrollable interface properties */ g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment"); g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment"); @@ -666,10 +688,12 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) * @path: the #GtkTreePath for the activated item * * The ::item-activated signal is emitted when the method - * gtk_icon_view_item_activated() is called or the user double - * clicks an item. It is also emitted when a non-editable item - * is selected and one of the keys: Space, Return or Enter is - * pressed. + * gtk_icon_view_item_activated() is called, when the user double + * clicks an item with the "activate-on-single-click" property set + * to %FALSE, or when the user single clicks an item when the + * "activate-on-single-click" property set to %TRUE. It is also + * emitted when a non-editable item is selected and one of the keys: + * Space, Return or Enter is pressed. */ icon_view_signals[ITEM_ACTIVATED] = g_signal_new (I_("item-activated"), @@ -962,11 +986,15 @@ gtk_icon_view_init (GtkIconView *icon_view) icon_view->priv->column_spacing = 6; icon_view->priv->margin = 6; icon_view->priv->item_padding = 6; + icon_view->priv->activate_on_single_click = FALSE; icon_view->priv->draw_focus = TRUE; icon_view->priv->row_contexts = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); + + gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (icon_view)), + GTK_STYLE_CLASS_VIEW); } /* GObject methods */ @@ -999,9 +1027,6 @@ gtk_icon_view_dispose (GObject *object) if (priv->cell_area_context) { - g_signal_handler_disconnect (priv->cell_area_context, priv->context_changed_id); - priv->context_changed_id = 0; - g_object_unref (priv->cell_area_context); priv->cell_area_context = NULL; } @@ -1089,6 +1114,10 @@ gtk_icon_view_set_property (GObject *object, gtk_icon_view_set_item_padding (icon_view, g_value_get_int (value)); break; + case PROP_ACTIVATE_ON_SINGLE_CLICK: + gtk_icon_view_set_activate_on_single_click (icon_view, g_value_get_boolean (value)); + break; + case PROP_CELL_AREA: /* Construct-only, can only be assigned once */ area = g_value_get_object (value); @@ -1185,6 +1214,10 @@ gtk_icon_view_get_property (GObject *object, g_value_set_int (value, icon_view->priv->item_padding); break; + case PROP_ACTIVATE_ON_SINGLE_CLICK: + g_value_set_boolean (value, icon_view->priv->activate_on_single_click); + break; + case PROP_CELL_AREA: g_value_set_object (value, icon_view->priv->cell_area); break; @@ -1216,12 +1249,6 @@ gtk_icon_view_destroy (GtkWidget *widget) gtk_icon_view_set_model (icon_view, NULL); - if (icon_view->priv->layout_idle_id != 0) - { - g_source_remove (icon_view->priv->layout_idle_id); - icon_view->priv->layout_idle_id = 0; - } - if (icon_view->priv->scroll_to_path != NULL) { gtk_tree_row_reference_free (icon_view->priv->scroll_to_path); @@ -1274,7 +1301,7 @@ gtk_icon_view_realize (GtkWidget *widget) window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gtk_widget_set_window (widget, window); - gdk_window_set_user_data (window, widget); + gtk_widget_register_window (widget, window); gtk_widget_get_allocation (widget, &allocation); @@ -1295,15 +1322,11 @@ gtk_icon_view_realize (GtkWidget *widget) icon_view->priv->bin_window = gdk_window_new (window, &attributes, attributes_mask); - gdk_window_set_user_data (icon_view->priv->bin_window, widget); + gtk_widget_register_window (widget, icon_view->priv->bin_window); context = gtk_widget_get_style_context (widget); - - gtk_style_context_save (context); - gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); gtk_style_context_set_background (context, icon_view->priv->bin_window); gtk_style_context_set_background (context, window); - gtk_style_context_restore (context); gdk_window_show (icon_view->priv->bin_window); } @@ -1315,7 +1338,7 @@ gtk_icon_view_unrealize (GtkWidget *widget) icon_view = GTK_ICON_VIEW (widget); - gdk_window_set_user_data (icon_view->priv->bin_window, NULL); + gtk_widget_unregister_window (widget, icon_view->priv->bin_window); gdk_window_destroy (icon_view->priv->bin_window); icon_view->priv->bin_window = NULL; @@ -1332,14 +1355,8 @@ _gtk_icon_view_update_background (GtkIconView *icon_view) GtkStyleContext *context; context = gtk_widget_get_style_context (widget); - - gtk_style_context_save (context); - gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); - gtk_style_context_set_background (context, gtk_widget_get_window (widget)); gtk_style_context_set_background (context, icon_view->priv->bin_window); - - gtk_style_context_restore (context); } } @@ -1360,20 +1377,396 @@ gtk_icon_view_style_updated (GtkWidget *widget) gtk_widget_queue_resize (widget); } +static gint +gtk_icon_view_get_n_items (GtkIconView *icon_view) +{ + GtkIconViewPrivate *priv = icon_view->priv; + + if (priv->model == NULL) + return 0; + + return gtk_tree_model_iter_n_children (priv->model, NULL); +} + +static void +adjust_wrap_width (GtkIconView *icon_view) +{ + if (icon_view->priv->text_cell) + { + gint wrap_width = 50; + + /* Here we go with the same old guess, try the icon size and set double + * the size of the first icon found in the list, naive but works much + * of the time */ + if (icon_view->priv->items && icon_view->priv->pixbuf_cell) + { + gtk_cell_renderer_get_preferred_width (icon_view->priv->pixbuf_cell, + GTK_WIDGET (icon_view), + &wrap_width, NULL); + + wrap_width = MAX (wrap_width * 2, 50); + } + + g_object_set (icon_view->priv->text_cell, "wrap-width", wrap_width, NULL); + g_object_set (icon_view->priv->text_cell, "width", wrap_width, NULL); + } +} + +/* General notes about layout + * + * The icon view is layouted like this: + * + * +----------+ s +----------+ + * | padding | p | padding | + * | +------+ | a | +------+ | + * | | cell | | c | | cell | | + * | +------+ | i | +------+ | + * | | n | | + * +----------+ g +----------+ + * + * In size request and allocation code, there are 3 sizes that are used: + * * cell size + * This is the size returned by gtk_cell_area_get_preferred_foo(). In places + * where code is interacting with the cell area and renderers this is useful. + * * padded size + * This is the cell size plus the item padding on each side. + * * spaced size + * This is the padded size plus the spacing. This is what's used for most + * calculations because it can (ab)use the following formula: + * iconview_size = 2 * margin + n_items * spaced_size - spacing + * So when reading this code and fixing my bugs where I confuse these two, be + * aware of this distinction. + */ +static void +cell_area_get_preferred_size (GtkIconView *icon_view, + GtkCellAreaContext *context, + GtkOrientation orientation, + gint for_size, + gint *minimum, + gint *natural) +{ + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (for_size > 0) + gtk_cell_area_get_preferred_width_for_height (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + for_size, + minimum, natural); + else + gtk_cell_area_get_preferred_width (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + minimum, natural); + } + else + { + if (for_size > 0) + gtk_cell_area_get_preferred_height_for_width (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + for_size, + minimum, natural); + else + gtk_cell_area_get_preferred_height (icon_view->priv->cell_area, + context, + GTK_WIDGET (icon_view), + minimum, natural); + } +} + +static gboolean +gtk_icon_view_is_empty (GtkIconView *icon_view) +{ + return icon_view->priv->items == NULL; +} + +static void +gtk_icon_view_get_preferred_item_size (GtkIconView *icon_view, + GtkOrientation orientation, + gint for_size, + gint *minimum, + gint *natural) +{ + GtkIconViewPrivate *priv = icon_view->priv; + GtkCellAreaContext *context; + GList *items; + + g_assert (!gtk_icon_view_is_empty (icon_view)); + + context = gtk_cell_area_create_context (priv->cell_area); + + for_size -= 2 * priv->item_padding; + + if (for_size > 0) + { + /* This is necessary for the context to work properly */ + for (items = priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + + _gtk_icon_view_set_cell_data (icon_view, item); + cell_area_get_preferred_size (icon_view, context, 1 - orientation, -1, NULL, NULL); + } + } + + for (items = priv->items; items; items = items->next) + { + GtkIconViewItem *item = items->data; + + _gtk_icon_view_set_cell_data (icon_view, item); + if (items == priv->items) + adjust_wrap_width (icon_view); + cell_area_get_preferred_size (icon_view, context, orientation, for_size, NULL, NULL); + } + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (for_size > 0) + gtk_cell_area_context_get_preferred_width_for_height (context, + for_size, + minimum, natural); + else + gtk_cell_area_context_get_preferred_width (context, + minimum, natural); + } + else + { + if (for_size > 0) + gtk_cell_area_context_get_preferred_height_for_width (context, + for_size, + minimum, natural); + else + gtk_cell_area_context_get_preferred_height (context, + minimum, natural); + } + + if (orientation == GTK_ORIENTATION_HORIZONTAL && priv->item_width >= 0) + { + if (minimum) + *minimum = MAX (*minimum, priv->item_width); + if (natural) + *natural = *minimum; + } + + if (minimum) + *minimum = MAX (1, *minimum + 2 * priv->item_padding); + if (natural) + *natural = MAX (1, *natural + 2 * priv->item_padding); + + g_object_unref (context); +} + +static void +gtk_icon_view_compute_n_items_for_size (GtkIconView *icon_view, + GtkOrientation orientation, + gint size, + gint *min_items, + gint *min_item_size, + gint *max_items, + gint *max_item_size) +{ + GtkIconViewPrivate *priv = icon_view->priv; + int minimum, natural, spacing; + + g_return_if_fail (min_item_size == NULL || min_items != NULL); + g_return_if_fail (max_item_size == NULL || max_items != NULL); + g_return_if_fail (!gtk_icon_view_is_empty (icon_view)); + + gtk_icon_view_get_preferred_item_size (icon_view, orientation, -1, &minimum, &natural); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + spacing = priv->column_spacing; + else + spacing = priv->row_spacing; + + size -= 2 * priv->margin; + size += spacing; + minimum += spacing; + natural += spacing; + + if (priv->columns > 0) + { + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (min_items) + *min_items = priv->columns; + if (max_items) + *max_items = priv->columns; + } + else + { + int n_items = gtk_icon_view_get_n_items (icon_view); + + if (min_items) + *min_items = (n_items + priv->columns - 1) / priv->columns; + if (max_items) + *max_items = (n_items + priv->columns - 1) / priv->columns; + } + } + else + { + if (max_items) + { + if (size <= minimum) + *max_items = 1; + else + *max_items = size / minimum; + } + + if (min_items) + { + if (size <= natural) + *min_items = 1; + else + *min_items = size / natural; + } + } + + if (min_item_size) + { + *min_item_size = size / *min_items; + *min_item_size = CLAMP (*min_item_size, minimum, natural); + *min_item_size -= spacing; + *min_item_size -= 2 * priv->item_padding; + } + + if (max_item_size) + { + *max_item_size = size / *max_items; + *max_item_size = CLAMP (*max_item_size, minimum, natural); + *max_item_size -= spacing; + *max_item_size -= 2 * priv->item_padding; + } +} + +static GtkSizeRequestMode +gtk_icon_view_get_request_mode (GtkWidget *widget) +{ + return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; +} + +static void +gtk_icon_view_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + GtkIconViewPrivate *priv = icon_view->priv; + int item_min, item_nat; + + if (gtk_icon_view_is_empty (icon_view)) + { + *minimum = *natural = 2 * priv->margin; + return; + } + + gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_HORIZONTAL, -1, &item_min, &item_nat); + + if (priv->columns > 0) + { + *minimum = item_min * priv->columns + priv->column_spacing * (priv->columns - 1); + *natural = item_nat * priv->columns + priv->column_spacing * (priv->columns - 1); + } + else + { + int n_items = gtk_icon_view_get_n_items (icon_view); + + *minimum = item_min; + *natural = item_nat * n_items + priv->column_spacing * (n_items - 1); + } + + *minimum += 2 * priv->margin; + *natural += 2 * priv->margin; +} + +static void +gtk_icon_view_get_preferred_width_for_height (GtkWidget *widget, + gint height, + gint *minimum, + gint *natural) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + GtkIconViewPrivate *priv = icon_view->priv; + int item_min, item_nat, rows, row_height, n_items; + + if (gtk_icon_view_is_empty (icon_view)) + { + *minimum = *natural = 2 * priv->margin; + return; + } + + gtk_icon_view_compute_n_items_for_size (icon_view, GTK_ORIENTATION_VERTICAL, height, &rows, &row_height, NULL, NULL); + n_items = gtk_icon_view_get_n_items (icon_view); + + gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_HORIZONTAL, row_height, &item_min, &item_nat); + *minimum = (item_min + priv->column_spacing) * ((n_items + rows - 1) / rows) - priv->column_spacing; + *natural = (item_nat + priv->column_spacing) * ((n_items + rows - 1) / rows) - priv->column_spacing; + + *minimum += 2 * priv->margin; + *natural += 2 * priv->margin; +} + static void -gtk_icon_view_get_preferred_width (GtkWidget *widget, - gint *minimum, - gint *natural) +gtk_icon_view_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural) { - *minimum = *natural = GTK_ICON_VIEW (widget)->priv->width; + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + GtkIconViewPrivate *priv = icon_view->priv; + int item_min, item_nat, n_items; + + if (gtk_icon_view_is_empty (icon_view)) + { + *minimum = *natural = 2 * priv->margin; + return; + } + + gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_VERTICAL, -1, &item_min, &item_nat); + n_items = gtk_icon_view_get_n_items (icon_view); + + if (priv->columns > 0) + { + int n_rows = (n_items + priv->columns - 1) / priv->columns; + + *minimum = item_min * n_rows + priv->row_spacing * (n_rows - 1); + *natural = item_nat * n_rows + priv->row_spacing * (n_rows - 1); + } + else + { + *minimum = item_min; + *natural = item_nat * n_items + priv->row_spacing * (n_items - 1); + } + + *minimum += 2 * priv->margin; + *natural += 2 * priv->margin; } static void -gtk_icon_view_get_preferred_height (GtkWidget *widget, - gint *minimum, - gint *natural) +gtk_icon_view_get_preferred_height_for_width (GtkWidget *widget, + gint width, + gint *minimum, + gint *natural) { - *minimum = *natural = GTK_ICON_VIEW (widget)->priv->height; + GtkIconView *icon_view = GTK_ICON_VIEW (widget); + GtkIconViewPrivate *priv = icon_view->priv; + int item_min, item_nat, columns, column_width, n_items; + + if (gtk_icon_view_is_empty (icon_view)) + { + *minimum = *natural = 2 * priv->margin; + return; + } + + gtk_icon_view_compute_n_items_for_size (icon_view, GTK_ORIENTATION_HORIZONTAL, width, NULL, NULL, &columns, &column_width); + n_items = gtk_icon_view_get_n_items (icon_view); + + gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_VERTICAL, column_width, &item_min, &item_nat); + *minimum = (item_min + priv->row_spacing) * ((n_items + columns - 1) / columns) - priv->row_spacing; + *natural = (item_nat + priv->row_spacing) * ((n_items + columns - 1) / columns) - priv->row_spacing; + + *minimum += 2 * priv->margin; + *natural += 2 * priv->margin; } static void @@ -1398,6 +1791,8 @@ gtk_icon_view_size_allocate (GtkWidget *widget, gtk_widget_set_allocation (widget, allocation); + gtk_icon_view_layout (icon_view); + if (gtk_widget_get_realized (widget)) { gdk_window_move_resize (gtk_widget_get_window (widget), @@ -1408,8 +1803,6 @@ gtk_icon_view_size_allocate (GtkWidget *widget, MAX (icon_view->priv->height, allocation->height)); } - gtk_icon_view_layout (icon_view); - gtk_icon_view_allocate_children (icon_view); /* Delay signal emission */ @@ -1449,9 +1842,16 @@ gtk_icon_view_draw (GtkWidget *widget, gint dest_index; GtkIconViewDropPosition dest_pos; GtkIconViewItem *dest_item = NULL; + GtkStyleContext *context; icon_view = GTK_ICON_VIEW (widget); + context = gtk_widget_get_style_context (widget); + gtk_render_background (context, cr, + 0, 0, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); + if (!gtk_cairo_should_draw_window (cr, icon_view->priv->bin_window)) return FALSE; @@ -1476,10 +1876,10 @@ gtk_icon_view_draw (GtkWidget *widget, GtkIconViewItem *item = icons->data; GdkRectangle paint_area; - paint_area.x = ((GdkRectangle *)item)->x - icon_view->priv->item_padding; - paint_area.y = ((GdkRectangle *)item)->y - icon_view->priv->item_padding; - paint_area.width = ((GdkRectangle *)item)->width + icon_view->priv->item_padding * 2; - paint_area.height = ((GdkRectangle *)item)->height + icon_view->priv->item_padding * 2; + paint_area.x = item->cell_area.x - icon_view->priv->item_padding; + paint_area.y = item->cell_area.y - icon_view->priv->item_padding; + paint_area.width = item->cell_area.width + icon_view->priv->item_padding * 2; + paint_area.height = item->cell_area.height + icon_view->priv->item_padding * 2; cairo_save (cr); @@ -1489,7 +1889,7 @@ gtk_icon_view_draw (GtkWidget *widget, if (gdk_cairo_get_clip_rectangle (cr, NULL)) { gtk_icon_view_paint_item (icon_view, cr, item, - ((GdkRectangle *)item)->x, ((GdkRectangle *)item)->y, + item->cell_area.x, item->cell_area.y, icon_view->priv->draw_focus); if (dest_index == item->index) @@ -1752,18 +2152,6 @@ gtk_icon_view_remove_editable (GtkCellArea *area, gtk_tree_path_free (path); } -static void -gtk_icon_view_context_changed (GtkCellAreaContext *context, - GParamSpec *pspec, - GtkIconView *icon_view) -{ - if (!strcmp (pspec->name, "minimum-width") || - !strcmp (pspec->name, "natural-width") || - !strcmp (pspec->name, "minimum-height") || - !strcmp (pspec->name, "natural-height")) - gtk_icon_view_invalidate_sizes (icon_view); -} - /** * gtk_icon_view_set_cursor: * @icon_view: A #GtkIconView @@ -1816,7 +2204,7 @@ gtk_icon_view_set_cursor (GtkIconView *icon_view, context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); _gtk_icon_view_set_cell_data (icon_view, item); gtk_cell_area_activate (icon_view->priv->cell_area, context, - GTK_WIDGET (icon_view), (GdkRectangle *)item, + GTK_WIDGET (icon_view), &item->cell_area, 0 /* XXX flags */, TRUE); } } @@ -1897,7 +2285,7 @@ gtk_icon_view_button_press (GtkWidget *widget, &cell); /* - * We consider only the the cells' area as the item area if the + * We consider only the cells' area as the item area if the * item is not selected, but if it *is* selected, the complete * selection rectangle is considered to be part of the item. */ @@ -1974,7 +2362,7 @@ gtk_icon_view_button_press (GtkWidget *widget, _gtk_icon_view_set_cell_data (icon_view, item); gtk_cell_area_activate (icon_view->priv->cell_area, context, GTK_WIDGET (icon_view), - (GdkRectangle *)item, 0/* XXX flags */, FALSE); + &item->cell_area, 0/* XXX flags */, FALSE); } } else @@ -1993,7 +2381,9 @@ gtk_icon_view_button_press (GtkWidget *widget, icon_view->priv->draw_focus = FALSE; } - if (event->button == GDK_BUTTON_PRIMARY && event->type == GDK_2BUTTON_PRESS) + if (!icon_view->priv->activate_on_single_click + && event->button == GDK_BUTTON_PRIMARY + && event->type == GDK_2BUTTON_PRESS) { item = _gtk_icon_view_get_item_at_coords (icon_view, event->x, event->y, @@ -2019,6 +2409,12 @@ gtk_icon_view_button_press (GtkWidget *widget, return event->button == GDK_BUTTON_PRIMARY; } +static gboolean +button_event_modifies_selection (GdkEventButton *event) +{ + return (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0; +} + static gboolean gtk_icon_view_button_release (GtkWidget *widget, GdkEventButton *event) @@ -2034,6 +2430,28 @@ gtk_icon_view_button_release (GtkWidget *widget, remove_scroll_timeout (icon_view); + if (event->button == GDK_BUTTON_PRIMARY + && icon_view->priv->activate_on_single_click + && !button_event_modifies_selection (event) + && icon_view->priv->last_single_clicked != NULL) + { + GtkIconViewItem *item; + + item = _gtk_icon_view_get_item_at_coords (icon_view, + event->x, event->y, + FALSE, + NULL); + if (item == icon_view->priv->last_single_clicked) + { + GtkTreePath *path; + path = gtk_tree_path_new_from_indices (item->index, -1); + gtk_icon_view_item_activated (icon_view, path); + gtk_tree_path_free (path); + } + + icon_view->priv->last_single_clicked = NULL; + } + return TRUE; } @@ -2243,7 +2661,7 @@ gtk_icon_view_item_hit_test (GtkIconView *icon_view, { HitTestData data = { { x, y, width, height }, FALSE }; GtkCellAreaContext *context; - GdkRectangle *item_area = (GdkRectangle *)item; + GdkRectangle *item_area = &item->cell_area; if (MIN (x + width, item_area->x + item_area->width) - MAX (x, item_area->x) <= 0 || MIN (y + height, item_area->y + item_area->height) - MAX (y, item_area->y) <= 0) @@ -2322,7 +2740,7 @@ gtk_icon_view_real_activate_cursor_item (GtkIconView *icon_view) _gtk_icon_view_set_cell_data (icon_view, icon_view->priv->cursor_item); gtk_cell_area_activate (icon_view->priv->cell_area, context, GTK_WIDGET (icon_view), - (GdkRectangle *)icon_view->priv->cursor_item, + &icon_view->priv->cursor_item->cell_area, 0 /* XXX flags */, FALSE); @@ -2362,20 +2780,6 @@ gtk_icon_view_real_toggle_cursor_item (GtkIconView *icon_view) } } -/* Internal functions */ -static void -gtk_icon_view_process_updates (GtkIconView *icon_view) -{ - /* Prior to drawing, we check if a layout has been scheduled. If so, - * do it now that all cell view items have valid sizes before we proceeed - * (and resize the bin_window if required). - */ - if (icon_view->priv->layout_idle_id != 0) - gtk_icon_view_layout (icon_view); - - gdk_window_process_updates (icon_view->priv->bin_window, TRUE); -} - static void gtk_icon_view_set_hadjustment_values (GtkIconView *icon_view) { @@ -2511,266 +2915,139 @@ gtk_icon_view_adjustment_changed (GtkAdjustment *adjustment, if (icon_view->priv->doing_rubberband) gtk_icon_view_update_rubberband (GTK_WIDGET (icon_view)); - - _gtk_icon_view_accessible_adjustment_changed (icon_view); - - gtk_icon_view_process_updates (icon_view); - } -} - -static GList * -gtk_icon_view_layout_single_row (GtkIconView *icon_view, - GList *first_item, - gint item_width, - gint row, - gint *y, - gint *maximum_width) -{ - GtkAllocation allocation; - GtkCellAreaContext *context; - GtkIconViewPrivate *priv = icon_view->priv; - GtkWidget *widget = GTK_WIDGET (icon_view); - gint x, current_width; - GList *items, *last_item; - gint col; - gint max_height = 0; - gboolean rtl; - - rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; - - x = 0; - col = 0; - items = first_item; - current_width = 0; - - x += priv->margin; - current_width += 2 * priv->margin; - - gtk_widget_get_allocation (widget, &allocation); - context = gtk_cell_area_copy_context (priv->cell_area, priv->cell_area_context); - g_ptr_array_add (priv->row_contexts, context); - - /* In the first loop we iterate horizontally until we hit allocation width - * and collect the aligned height-for-width */ - items = first_item; - while (items) - { - GtkIconViewItem *item = items->data; - GdkRectangle *item_area = (GdkRectangle *)item; - - item_area->width = item_width; - - current_width += item_area->width + icon_view->priv->item_padding * 2; - - if (items != first_item) - { - if ((icon_view->priv->columns <= 0 && current_width > allocation.width) || - (icon_view->priv->columns > 0 && col >= icon_view->priv->columns)) - break; - } - - /* Get this item's particular width & height (all alignments are cached by now) */ - _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_get_preferred_height_for_width (priv->cell_area, - context, - widget, item_width, - NULL, NULL); - - current_width += icon_view->priv->column_spacing; - - item_area->y = *y + icon_view->priv->item_padding; - item_area->x = x + icon_view->priv->item_padding; - - x = current_width - icon_view->priv->margin; - - if (current_width > *maximum_width) - *maximum_width = current_width; - - item->row = row; - item->col = col; - - col ++; - items = items->next; - } - - last_item = items; - - gtk_cell_area_context_get_preferred_height_for_width (context, item_width, &max_height, NULL); - gtk_cell_area_context_allocate (context, item_width, max_height); - - /* In the second loop the item height has been aligned and derived and - * we just set the height and handle rtl layout */ - for (items = first_item; items != last_item; items = items->next) - { - GtkIconViewItem *item = items->data; - GdkRectangle *item_area = (GdkRectangle *)item; - - if (rtl) - { - item_area->x = *maximum_width - item_area->width - item_area->x; - item->col = col - 1 - item->col; - } - - /* All items in the same row get the same height */ - item_area->height = max_height; + _gtk_icon_view_accessible_adjustment_changed (icon_view); } - - /* Adjust the new y coordinate. */ - *y += max_height + icon_view->priv->row_spacing + icon_view->priv->item_padding * 2; - - return last_item; } -static void -adjust_wrap_width (GtkIconView *icon_view) +static gint +compare_sizes (gconstpointer p1, + gconstpointer p2, + gpointer unused) { - if (icon_view->priv->text_cell) - { - gint wrap_width = 50; - - /* Here we go with the same old guess, try the icon size and set double - * the size of the first icon found in the list, naive but works much - * of the time */ - if (icon_view->priv->items && icon_view->priv->pixbuf_cell) - { - _gtk_icon_view_set_cell_data (icon_view, icon_view->priv->items->data); - gtk_cell_renderer_get_preferred_width (icon_view->priv->pixbuf_cell, - GTK_WIDGET (icon_view), - &wrap_width, NULL); - - wrap_width = MAX (wrap_width * 2, 50); - } - - g_object_set (icon_view->priv->text_cell, "wrap-width", wrap_width, NULL); - g_object_set (icon_view->priv->text_cell, "width", wrap_width, NULL); - } + return GPOINTER_TO_INT (((const GtkRequestedSize *) p1)->data) + - GPOINTER_TO_INT (((const GtkRequestedSize *) p2)->data); } static void gtk_icon_view_layout (GtkIconView *icon_view) { - GtkAllocation allocation; - GtkWidget *widget; - GList *icons; - gint y = 0, maximum_width = 0; - gint row; - gint item_width; - gboolean size_changed = FALSE; + GtkIconViewPrivate *priv = icon_view->priv; + GtkWidget *widget = GTK_WIDGET (icon_view); + GList *items; + gint item_width; /* this doesn't include item_padding */ + gint n_columns, n_rows, n_items; + gint col, row; + GtkRequestedSize *sizes; + gboolean rtl; - if (icon_view->priv->layout_idle_id != 0) - { - g_source_remove (icon_view->priv->layout_idle_id); - icon_view->priv->layout_idle_id = 0; - } - - if (icon_view->priv->model == NULL) + if (gtk_icon_view_is_empty (icon_view)) return; - widget = GTK_WIDGET (icon_view); - - item_width = icon_view->priv->item_width; - - /* Update the wrap width for the text cell before going and requesting sizes */ - adjust_wrap_width (icon_view); - - /* Update the context widths for any invalidated items */ - gtk_icon_view_cache_widths (icon_view); + rtl = gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL; + n_items = gtk_icon_view_get_n_items (icon_view); - /* Fetch the new item width if needed */ - if (item_width < 0) - gtk_cell_area_context_get_preferred_width (icon_view->priv->cell_area_context, - &item_width, NULL); + gtk_icon_view_compute_n_items_for_size (icon_view, + GTK_ORIENTATION_HORIZONTAL, + gtk_widget_get_allocated_width (widget), + NULL, NULL, + &n_columns, &item_width); + n_rows = (n_items + n_columns - 1) / n_columns; - gtk_cell_area_context_allocate (icon_view->priv->cell_area_context, item_width, -1); - - icons = icon_view->priv->items; - y += icon_view->priv->margin; - row = 0; + priv->width = n_columns * (item_width + 2 * priv->item_padding + priv->column_spacing) - priv->column_spacing; + priv->width += 2 * priv->margin; + priv->width = MAX (priv->width, gtk_widget_get_allocated_width (widget)); /* Clear the per row contexts */ g_ptr_array_set_size (icon_view->priv->row_contexts, 0); - - do - { - icons = gtk_icon_view_layout_single_row (icon_view, icons, - item_width, row, - &y, &maximum_width); - row++; - } - while (icons != NULL); - if (maximum_width != icon_view->priv->width) + gtk_cell_area_context_reset (priv->cell_area_context); + /* because layouting is complicated. We designed an API + * that is O(N²) and nonsensical. + * And we're proud of it. */ + for (items = priv->items; items; items = items->next) { - icon_view->priv->width = maximum_width; - size_changed = TRUE; + _gtk_icon_view_set_cell_data (icon_view, items->data); + gtk_cell_area_get_preferred_width (priv->cell_area, + priv->cell_area_context, + widget, + NULL, NULL); } - y += icon_view->priv->margin; - - if (y != icon_view->priv->height) + sizes = g_newa (GtkRequestedSize, n_rows); + items = priv->items; + priv->height = priv->margin; + + /* Collect the heights for all rows */ + for (row = 0; row < n_rows; row++) { - icon_view->priv->height = y; - size_changed = TRUE; - } + GtkCellAreaContext *context = gtk_cell_area_copy_context (priv->cell_area, priv->cell_area_context); + g_ptr_array_add (priv->row_contexts, context); - gtk_icon_view_set_hadjustment_values (icon_view); - gtk_icon_view_set_vadjustment_values (icon_view); + for (col = 0; col < n_columns && items; col++, items = items->next) + { + GtkIconViewItem *item = items->data; - if (size_changed) - gtk_widget_queue_resize_no_redraw (widget); + _gtk_icon_view_set_cell_data (icon_view, item); + gtk_cell_area_get_preferred_height_for_width (priv->cell_area, + context, + widget, + item_width, + NULL, NULL); + } + + sizes[row].data = GINT_TO_POINTER (row); + gtk_cell_area_context_get_preferred_height_for_width (context, + item_width, + &sizes[row].minimum_size, + &sizes[row].natural_size); + priv->height += sizes[row].minimum_size + 2 * priv->item_padding + priv->row_spacing; + } - gtk_widget_get_allocation (widget, &allocation); - if (gtk_widget_get_realized (GTK_WIDGET (icon_view))) - gdk_window_resize (icon_view->priv->bin_window, - MAX (icon_view->priv->width, allocation.width), - MAX (icon_view->priv->height, allocation.height)); + priv->height -= priv->row_spacing; + priv->height += priv->margin; + priv->height = MIN (priv->height, gtk_widget_get_allocated_height (widget)); - if (icon_view->priv->scroll_to_path) - { - GtkTreePath *path; + gtk_distribute_natural_allocation (gtk_widget_get_allocated_height (widget) - priv->height, + n_rows, + sizes); - path = gtk_tree_row_reference_get_path (icon_view->priv->scroll_to_path); - gtk_tree_row_reference_free (icon_view->priv->scroll_to_path); - icon_view->priv->scroll_to_path = NULL; - - gtk_icon_view_scroll_to_path (icon_view, path, - icon_view->priv->scroll_to_use_align, - icon_view->priv->scroll_to_row_align, - icon_view->priv->scroll_to_col_align); - gtk_tree_path_free (path); - } + /* Actually allocate the rows */ + g_qsort_with_data (sizes, n_rows, sizeof (GtkRequestedSize), compare_sizes, NULL); - gtk_widget_queue_draw (widget); -} + items = priv->items; + priv->height = priv->margin; -/* This ensures that all widths have been cached in the - * context and we have proper alignments to go on. - */ -static void -gtk_icon_view_cache_widths (GtkIconView *icon_view) -{ - GList *items; + for (row = 0; row < n_rows; row++) + { + GtkCellAreaContext *context = g_ptr_array_index (priv->row_contexts, row); + gtk_cell_area_context_allocate (context, item_width, sizes[row].minimum_size); - g_signal_handler_block (icon_view->priv->cell_area_context, - icon_view->priv->context_changed_id); + priv->height += priv->item_padding; - for (items = icon_view->priv->items; items; items = items->next) - { - GtkIconViewItem *item = items->data; + for (col = 0; col < n_columns && items; col++, items = items->next) + { + GtkIconViewItem *item = items->data; + + item->cell_area.x = priv->margin + (col * 2 + 1) * priv->item_padding + col * (priv->column_spacing + item_width); + item->cell_area.width = item_width; + item->cell_area.y = priv->height; + item->cell_area.height = sizes[row].minimum_size; + item->row = row; + item->col = col; + if (rtl) + { + item->cell_area.x = priv->width - item_width - item->cell_area.x; + item->col = n_columns - 1 - col; + } + } - /* Only fetch the width of items with invalidated sizes */ - if (item->cell_area.width < 0) - { - _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_get_preferred_width (icon_view->priv->cell_area, - icon_view->priv->cell_area_context, - GTK_WIDGET (icon_view), NULL, NULL); - } + priv->height += sizes[row].minimum_size + priv->item_padding + priv->row_spacing; } - g_signal_handler_unblock (icon_view->priv->cell_area_context, - icon_view->priv->context_changed_id); + priv->height -= priv->row_spacing; + priv->height += priv->margin; + priv->height = MAX (priv->height, gtk_widget_get_allocated_height (widget)); } static void @@ -2780,18 +3057,8 @@ gtk_icon_view_invalidate_sizes (GtkIconView *icon_view) g_list_foreach (icon_view->priv->items, (GFunc)gtk_icon_view_item_invalidate_size, NULL); - /* Reset the context */ - if (icon_view->priv->cell_area_context) - { - g_signal_handler_block (icon_view->priv->cell_area_context, - icon_view->priv->context_changed_id); - gtk_cell_area_context_reset (icon_view->priv->cell_area_context); - g_signal_handler_unblock (icon_view->priv->cell_area_context, - icon_view->priv->context_changed_id); - } - /* Re-layout the items */ - gtk_icon_view_queue_layout (icon_view); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); } static void @@ -2817,7 +3084,7 @@ gtk_icon_view_paint_item (GtkIconView *icon_view, GtkIconViewPrivate *priv = icon_view->priv; GtkCellAreaContext *context; - if (priv->model == NULL) + if (priv->model == NULL || item->cell_area.width <= 0 || item->cell_area.height <= 0) return; _gtk_icon_view_set_cell_data (icon_view, item); @@ -2826,19 +3093,16 @@ gtk_icon_view_paint_item (GtkIconView *icon_view, state = gtk_widget_get_state_flags (widget); gtk_style_context_save (style_context); - gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_VIEW); gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_CELL); state &= ~(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_PRELIGHT); + if ((state & GTK_STATE_FLAG_FOCUSED) && + item == icon_view->priv->cursor_item) + flags |= GTK_CELL_RENDERER_FOCUSED; + if (item->selected) { - if ((state & GTK_STATE_FLAG_FOCUSED) && - item == icon_view->priv->cursor_item) - { - flags |= GTK_CELL_RENDERER_FOCUSED; - } - state |= GTK_STATE_FLAG_SELECTED; flags |= GTK_CELL_RENDERER_SELECTED; } @@ -2937,7 +3201,7 @@ gtk_icon_view_queue_draw_item (GtkIconView *icon_view, GtkIconViewItem *item) { GdkRectangle rect; - GdkRectangle *item_area = (GdkRectangle *)item; + GdkRectangle *item_area = &item->cell_area; rect.x = item_area->x - icon_view->priv->item_padding; rect.y = item_area->y - icon_view->priv->item_padding; @@ -2948,31 +3212,6 @@ gtk_icon_view_queue_draw_item (GtkIconView *icon_view, gdk_window_invalidate_rect (icon_view->priv->bin_window, &rect, TRUE); } -static gboolean -layout_callback (gpointer user_data) -{ - GtkIconView *icon_view; - - icon_view = GTK_ICON_VIEW (user_data); - - icon_view->priv->layout_idle_id = 0; - - gtk_icon_view_layout (icon_view); - - return FALSE; -} - -static void -gtk_icon_view_queue_layout (GtkIconView *icon_view) -{ - if (icon_view->priv->layout_idle_id != 0) - return; - - icon_view->priv->layout_idle_id = - gdk_threads_add_idle_full (GTK_ICON_VIEW_PRIORITY_LAYOUT, - layout_callback, icon_view, NULL); -} - void _gtk_icon_view_set_cursor_item (GtkIconView *icon_view, GtkIconViewItem *item, @@ -3063,7 +3302,7 @@ _gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, for (items = icon_view->priv->items; items; items = items->next) { GtkIconViewItem *item = items->data; - GdkRectangle *item_area = (GdkRectangle *)item; + GdkRectangle *item_area = &item->cell_area; if (x >= item_area->x - icon_view->priv->column_spacing/2 && x <= item_area->x + item_area->width + icon_view->priv->column_spacing/2 && @@ -3204,22 +3443,16 @@ gtk_icon_view_row_inserted (GtkTreeModel *model, GtkIconView *icon_view = GTK_ICON_VIEW (data); gint index; GtkIconViewItem *item; - gboolean iters_persist; GList *list; /* ignore changes in branches */ if (gtk_tree_path_get_depth (path) > 1) return; - iters_persist = gtk_tree_model_get_flags (icon_view->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST; - index = gtk_tree_path_get_indices(path)[0]; item = gtk_icon_view_item_new (); - if (iters_persist) - item->iter = *iter; - item->index = index; /* FIXME: We can be more efficient here, @@ -3239,7 +3472,7 @@ gtk_icon_view_row_inserted (GtkTreeModel *model, verify_items (icon_view); - gtk_icon_view_queue_layout (icon_view); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); } static void @@ -3290,7 +3523,7 @@ gtk_icon_view_row_deleted (GtkTreeModel *model, verify_items (icon_view); - gtk_icon_view_queue_layout (icon_view); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); if (emit) g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); @@ -3338,7 +3571,7 @@ gtk_icon_view_rows_reordered (GtkTreeModel *model, g_list_free (icon_view->priv->items); icon_view->priv->items = items; - gtk_icon_view_queue_layout (icon_view); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); verify_items (icon_view); } @@ -3348,11 +3581,8 @@ gtk_icon_view_build_items (GtkIconView *icon_view) { GtkTreeIter iter; int i; - gboolean iters_persist; GList *items = NULL; - iters_persist = gtk_tree_model_get_flags (icon_view->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST; - if (!gtk_tree_model_get_iter_first (icon_view->priv->model, &iter)) return; @@ -3363,9 +3593,6 @@ gtk_icon_view_build_items (GtkIconView *icon_view) { GtkIconViewItem *item = gtk_icon_view_item_new (); - if (iters_persist) - item->iter = iter; - item->index = i; i++; @@ -4069,9 +4296,6 @@ gtk_icon_view_ensure_cell_area (GtkIconView *icon_view, priv->remove_editable_id = g_signal_connect (priv->cell_area, "remove-editable", G_CALLBACK (gtk_icon_view_remove_editable), icon_view); - priv->context_changed_id = - g_signal_connect (priv->cell_area_context, "notify", - G_CALLBACK (gtk_icon_view_context_changed), icon_view); update_text_cell (icon_view); update_pixbuf_cell (icon_view); @@ -4093,22 +4317,13 @@ void _gtk_icon_view_set_cell_data (GtkIconView *icon_view, GtkIconViewItem *item) { - gboolean iters_persist; GtkTreeIter iter; + GtkTreePath *path; - iters_persist = gtk_tree_model_get_flags (icon_view->priv->model) & GTK_TREE_MODEL_ITERS_PERSIST; - - if (!iters_persist) - { - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices (item->index, -1); - if (!gtk_tree_model_get_iter (icon_view->priv->model, &iter, path)) - return; - gtk_tree_path_free (path); - } - else - iter = item->iter; + path = gtk_tree_path_new_from_indices (item->index, -1); + if (!gtk_tree_model_get_iter (icon_view->priv->model, &iter, path)) + return; + gtk_tree_path_free (path); gtk_cell_area_apply_attributes (icon_view->priv->cell_area, icon_view->priv->model, @@ -4288,6 +4503,69 @@ gtk_icon_view_get_item_at_pos (GtkIconView *icon_view, return (item != NULL); } +/** + * gtk_icon_view_get_cell_rect: + * @icon_view: a #GtkIconView + * @path: a #GtkTreePath + * @cell: (allow-none): a #GtkCellRenderer or %NULL + * @rect: (out): rectangle to fill with cell rect + * + * Fills the bounding rectangle in widget coordinates for the cell specified by + * @path and @cell. If @cell is %NULL the main cell area is used. + * + * This function is only valid if @icon_view is realized. + * + * Return value: %FALSE if there is no such item, %TRUE otherwise + * + * Since: 3.6 + */ +gboolean +gtk_icon_view_get_cell_rect (GtkIconView *icon_view, + GtkTreePath *path, + GtkCellRenderer *cell, + GdkRectangle *rect) +{ + GtkIconViewItem *item = NULL; + gint x, y; + + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); + g_return_val_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell), FALSE); + + if (gtk_tree_path_get_depth (path) > 0) + item = g_list_nth_data (icon_view->priv->items, + gtk_tree_path_get_indices(path)[0]); + + if (!item) + return FALSE; + + if (cell) + { + GtkCellAreaContext *context; + + context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); + _gtk_icon_view_set_cell_data (icon_view, item); + gtk_cell_area_get_cell_allocation (icon_view->priv->cell_area, context, + GTK_WIDGET (icon_view), + cell, &item->cell_area, rect); + } + else + { + rect->x = item->cell_area.x - icon_view->priv->item_padding; + rect->y = item->cell_area.y - icon_view->priv->item_padding; + rect->width = item->cell_area.width + icon_view->priv->item_padding * 2; + rect->height = item->cell_area.height + icon_view->priv->item_padding * 2; + } + + if (icon_view->priv->bin_window) + { + gdk_window_get_position (icon_view->priv->bin_window, &x, &y); + rect->x += x; + rect->y += y; + } + + return TRUE; +} + /** * gtk_icon_view_set_tooltip_item: * @icon_view: a #GtkIconView @@ -4332,46 +4610,15 @@ gtk_icon_view_set_tooltip_cell (GtkIconView *icon_view, GtkCellRenderer *cell) { GdkRectangle rect; - GtkIconViewItem *item = NULL; - gint x, y; - + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell)); - if (gtk_tree_path_get_depth (path) > 0) - item = g_list_nth_data (icon_view->priv->items, - gtk_tree_path_get_indices(path)[0]); - - if (!item) + if (!gtk_icon_view_get_cell_rect (icon_view, path, cell, &rect)) return; - if (cell) - { - GtkCellAreaContext *context; - - context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); - _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_get_cell_allocation (icon_view->priv->cell_area, context, - GTK_WIDGET (icon_view), - cell, (GdkRectangle *)item, &rect); - } - else - { - rect.x = item->cell_area.x - icon_view->priv->item_padding; - rect.y = item->cell_area.y - icon_view->priv->item_padding; - rect.width = item->cell_area.width + icon_view->priv->item_padding * 2; - rect.height = item->cell_area.height + icon_view->priv->item_padding * 2; - } - - if (icon_view->priv->bin_window) - { - gdk_window_get_position (icon_view->priv->bin_window, &x, &y); - rect.x += x; - rect.y += y; - } - - gtk_tooltip_set_tip_area (tooltip, &rect); + gtk_tooltip_set_tip_area (tooltip, &rect); } @@ -4381,7 +4628,8 @@ gtk_icon_view_set_tooltip_cell (GtkIconView *icon_view, * @x: (inout): the x coordinate (relative to widget coordinates) * @y: (inout): the y coordinate (relative to widget coordinates) * @keyboard_tip: whether this is a keyboard tooltip or not - * @model: (out) (allow-none): a pointer to receive a #GtkTreeModel or %NULL + * @model: (out) (allow-none) (transfer none): a pointer to receive a + * #GtkTreeModel or %NULL * @path: (out) (allow-none): a pointer to receive a #GtkTreePath or %NULL * @iter: (out) (allow-none): a pointer to receive a #GtkTreeIter or %NULL * @@ -4590,7 +4838,7 @@ gtk_icon_view_get_visible_range (GtkIconView *icon_view, for (icons = icon_view->priv->items; icons; icons = icons->next) { GtkIconViewItem *item = icons->data; - GdkRectangle *item_area = (GdkRectangle *)item; + GdkRectangle *item_area = &item->cell_area; if ((item_area->x + item_area->width >= (int)gtk_adjustment_get_value (icon_view->priv->hadjustment)) && (item_area->y + item_area->height >= (int)gtk_adjustment_get_value (icon_view->priv->vadjustment)) && @@ -4706,6 +4954,8 @@ void gtk_icon_view_set_model (GtkIconView *icon_view, GtkTreeModel *model) { + gboolean dirty; + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model)); @@ -4722,6 +4972,8 @@ gtk_icon_view_set_model (GtkIconView *icon_view, if (icon_view->priv->cell_area) gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + dirty = gtk_icon_view_unselect_all_internal (icon_view); + if (model) { GType column_type; @@ -4802,14 +5054,14 @@ gtk_icon_view_set_model (GtkIconView *icon_view, icon_view); gtk_icon_view_build_items (icon_view); - - gtk_icon_view_layout (icon_view); } g_object_notify (G_OBJECT (icon_view), "model"); - if (gtk_widget_get_realized (GTK_WIDGET (icon_view))) - gtk_widget_queue_resize (GTK_WIDGET (icon_view)); + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); + + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); } /** @@ -5180,7 +5432,7 @@ gtk_icon_view_unselect_path (GtkIconView *icon_view, * * To free the return value, use: * |[ - * g_list_free_full (list, (GDestroyNotify) gtk_tree_patch_free); + * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); * ]| * * Return value: (element-type GtkTreePath) (transfer full): A #GList containing a #GtkTreePath for each selected row. @@ -5465,7 +5717,7 @@ gtk_icon_view_set_columns (GtkIconView *icon_view, if (icon_view->priv->cell_area) gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - gtk_icon_view_queue_layout (icon_view); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); g_object_notify (G_OBJECT (icon_view), "columns"); } @@ -5946,22 +6198,22 @@ gtk_icon_view_autoscroll (GtkIconView *icon_view, GdkDevice *device) { GdkWindow *window; - gint px, py, x, y, width, height; + gint px, py, width, height; gint hoffset, voffset; window = gtk_widget_get_window (GTK_WIDGET (icon_view)); gdk_window_get_device_position (window, device, &px, &py, NULL); - gdk_window_get_geometry (window, &x, &y, &width, &height); + gdk_window_get_geometry (window, NULL, NULL, &width, &height); /* see if we are near the edge. */ - voffset = py - (y + 2 * SCROLL_EDGE_SIZE); + voffset = py - 2 * SCROLL_EDGE_SIZE; if (voffset > 0) - voffset = MAX (py - (y + height - 2 * SCROLL_EDGE_SIZE), 0); + voffset = MAX (py - (height - 2 * SCROLL_EDGE_SIZE), 0); - hoffset = px - (x + 2 * SCROLL_EDGE_SIZE); + hoffset = px - 2 * SCROLL_EDGE_SIZE; if (hoffset > 0) - hoffset = MAX (px - (x + width - 2 * SCROLL_EDGE_SIZE), 0); + hoffset = MAX (px - (width - 2 * SCROLL_EDGE_SIZE), 0); if (voffset != 0) gtk_adjustment_set_value (icon_view->priv->vadjustment, @@ -6557,7 +6809,7 @@ gtk_icon_view_drag_data_received (GtkWidget *widget, /* Drag-and-Drop support */ /** * gtk_icon_view_enable_model_drag_source: - * @icon_view: a #GtkIconTreeView + * @icon_view: a #GtkIconView * @start_button_mask: Mask of allowed buttons to start drag * @targets: (array length=n_targets): the table of targets that the drag will * support @@ -6981,6 +7233,49 @@ gtk_icon_view_set_reorderable (GtkIconView *icon_view, g_object_notify (G_OBJECT (icon_view), "reorderable"); } +/** + * gtk_icon_view_set_activate_on_single_click: + * @icon_view: a #GtkIconView + * @single: %TRUE to emit item-activated on a single click + * + * Causes the #GtkIconView::item-activated signal to be emitted on + * a single click instead of a double click. + * + * Since: 3.8 + **/ +void +gtk_icon_view_set_activate_on_single_click (GtkIconView *icon_view, + gboolean single) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + single = single != FALSE; + + if (icon_view->priv->activate_on_single_click == single) + return; + + icon_view->priv->activate_on_single_click = single; + g_object_notify (G_OBJECT (icon_view), "activate-on-single-click"); +} + +/** + * gtk_icon_view_get_activate_on_single_click: + * @icon_view: a #GtkIconView + * + * Gets the setting set by gtk_icon_view_set_activate_on_single_click(). + * + * Return value: %TRUE if item-activated will be emitted on a single click + * + * Since: 3.8 + **/ +gboolean +gtk_icon_view_get_activate_on_single_click (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); + + return icon_view->priv->activate_on_single_click; +} + static gboolean gtk_icon_view_buildable_custom_tag_start (GtkBuildable *buildable, GtkBuilder *builder,