X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkiconview.c;h=0c6dc038ce354701c740569e7f2b9e35d77f2ef9;hb=6767541ead7cc150d1dd066d3b84d85559500c28;hp=ffc47b07b7157c451e4bf26e1cc6a1e5ecf26f26;hpb=e8eeb39d980fd993e17181dc45e44a84eaff109f;p=~andy%2Fgtk diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c index ffc47b07b..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 @@ -107,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 */ @@ -644,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"); @@ -673,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"), @@ -969,8 +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 */ @@ -1007,6 +1031,12 @@ gtk_icon_view_dispose (GObject *object) priv->cell_area_context = NULL; } + if (priv->row_contexts) + { + g_ptr_array_free (priv->row_contexts, TRUE); + priv->row_contexts = NULL; + } + if (priv->cell_area) { gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); @@ -1084,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); @@ -1180,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; @@ -1263,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); @@ -1284,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); } @@ -1304,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; @@ -1321,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); } } @@ -1384,6 +1412,31 @@ adjust_wrap_width (GtkIconView *icon_view) } } +/* 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, @@ -1422,6 +1475,12 @@ cell_area_get_preferred_size (GtkIconView *icon_view, } } +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, @@ -1433,6 +1492,8 @@ gtk_icon_view_get_preferred_item_size (GtkIconView *icon_view, 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; @@ -1459,7 +1520,26 @@ gtk_icon_view_get_preferred_item_size (GtkIconView *icon_view, cell_area_get_preferred_size (icon_view, context, orientation, for_size, NULL, NULL); } - cell_area_get_preferred_size (icon_view, context, orientation, for_size, minimum, natural); + 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) { @@ -1470,9 +1550,9 @@ gtk_icon_view_get_preferred_item_size (GtkIconView *icon_view, } if (minimum) - *minimum += 2 * priv->item_padding; + *minimum = MAX (1, *minimum + 2 * priv->item_padding); if (natural) - *natural += 2 * priv->item_padding; + *natural = MAX (1, *natural + 2 * priv->item_padding); g_object_unref (context); } @@ -1482,10 +1562,28 @@ gtk_icon_view_compute_n_items_for_size (GtkIconView *icon_view, GtkOrientation orientation, gint size, gint *min_items, - gint *max_items) + gint *min_item_size, + gint *max_items, + gint *max_item_size) { GtkIconViewPrivate *priv = icon_view->priv; - int minimum, natural; + 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) { @@ -1505,41 +1603,40 @@ gtk_icon_view_compute_n_items_for_size (GtkIconView *icon_view, if (max_items) *max_items = (n_items + priv->columns - 1) / priv->columns; } - - return; - } - - size -= 2 * priv->margin; - - gtk_icon_view_get_preferred_item_size (icon_view, orientation, -1, &minimum, &natural); - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - size += priv->column_spacing; - minimum += priv->column_spacing; - natural += priv->column_spacing; } else { - size += priv->row_spacing; - minimum += priv->row_spacing; - natural += priv->row_spacing; + 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 (max_items) + if (min_item_size) { - if (size <= minimum) - *max_items = 1; - else - *max_items = size / minimum; + *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 (min_items) + if (max_item_size) { - if (size <= natural) - *min_items = 1; - else - *min_items = size / natural; + *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; } } @@ -1558,6 +1655,12 @@ gtk_icon_view_get_preferred_width (GtkWidget *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) @@ -1569,16 +1672,8 @@ gtk_icon_view_get_preferred_width (GtkWidget *widget, { int n_items = gtk_icon_view_get_n_items (icon_view); - if (n_items == 0) - { - *minimum = 0; - *natural = 0; - } - else - { - *minimum = item_min; - *natural = item_nat * n_items + priv->column_spacing * (n_items - 1); - } + *minimum = item_min; + *natural = item_nat * n_items + priv->column_spacing * (n_items - 1); } *minimum += 2 * priv->margin; @@ -1593,16 +1688,20 @@ gtk_icon_view_get_preferred_width_for_height (GtkWidget *widget, { GtkIconView *icon_view = GTK_ICON_VIEW (widget); GtkIconViewPrivate *priv = icon_view->priv; - int item_min, item_nat, rows, n_items; + int item_min, item_nat, rows, row_height, n_items; - gtk_icon_view_compute_n_items_for_size (icon_view, GTK_ORIENTATION_VERTICAL, height, &rows, NULL); - n_items = gtk_icon_view_get_n_items (icon_view); + if (gtk_icon_view_is_empty (icon_view)) + { + *minimum = *natural = 2 * priv->margin; + return; + } - height = height + priv->row_spacing - 2 * priv->margin; + 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, height / rows - priv->row_spacing, &item_min, &item_nat); - *minimum = item_min * ((n_items + rows - 1) / rows); - *natural = item_nat * ((n_items + rows - 1) / rows); + 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; @@ -1617,6 +1716,12 @@ gtk_icon_view_get_preferred_height (GtkWidget *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); @@ -1629,16 +1734,8 @@ gtk_icon_view_get_preferred_height (GtkWidget *widget, } else { - if (n_items == 0) - { - *minimum = 0; - *natural = 0; - } - else - { - *minimum = item_min; - *natural = item_nat * n_items + priv->row_spacing * (n_items - 1); - } + *minimum = item_min; + *natural = item_nat * n_items + priv->row_spacing * (n_items - 1); } *minimum += 2 * priv->margin; @@ -1653,14 +1750,18 @@ gtk_icon_view_get_preferred_height_for_width (GtkWidget *widget, { GtkIconView *icon_view = GTK_ICON_VIEW (widget); GtkIconViewPrivate *priv = icon_view->priv; - int item_min, item_nat, columns, n_items; + int item_min, item_nat, columns, column_width, n_items; - gtk_icon_view_compute_n_items_for_size (icon_view, GTK_ORIENTATION_HORIZONTAL, width, NULL, &columns); - n_items = gtk_icon_view_get_n_items (icon_view); + if (gtk_icon_view_is_empty (icon_view)) + { + *minimum = *natural = 2 * priv->margin; + return; + } - width = width + priv->column_spacing - 2 * priv->margin; + 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, width / columns - priv->column_spacing, &item_min, &item_nat); + 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; @@ -1741,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; @@ -2091,10 +2199,11 @@ gtk_icon_view_set_cursor (GtkIconView *icon_view, if (start_editing && icon_view->priv->cell_area) { + 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_context_allocate (icon_view->priv->cell_area_context, item->cell_area.width, item->cell_area.height); - gtk_cell_area_activate (icon_view->priv->cell_area, - icon_view->priv->cell_area_context, + gtk_cell_area_activate (icon_view->priv->cell_area, context, GTK_WIDGET (icon_view), &item->cell_area, 0 /* XXX flags */, TRUE); } @@ -2176,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. */ @@ -2246,10 +2355,12 @@ gtk_icon_view_button_press (GtkWidget *widget, if (cell != NULL && gtk_cell_renderer_is_activatable (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_context_allocate (icon_view->priv->cell_area_context, item->cell_area.width, item->cell_area.height); - gtk_cell_area_activate (icon_view->priv->cell_area, - icon_view->priv->cell_area_context, + gtk_cell_area_activate (icon_view->priv->cell_area, context, GTK_WIDGET (icon_view), &item->cell_area, 0/* XXX flags */, FALSE); } @@ -2270,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, @@ -2296,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) @@ -2311,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; } @@ -2519,16 +2660,17 @@ gtk_icon_view_item_hit_test (GtkIconView *icon_view, gint height) { HitTestData data = { { x, y, width, height }, FALSE }; + GtkCellAreaContext *context; 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) return FALSE; + context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); + _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_context_allocate (icon_view->priv->cell_area_context, item->cell_area.width, item->cell_area.height); - gtk_cell_area_foreach_alloc (icon_view->priv->cell_area, - icon_view->priv->cell_area_context, + gtk_cell_area_foreach_alloc (icon_view->priv->cell_area, context, GTK_WIDGET (icon_view), item_area, item_area, (GtkCellAllocCallback)hit_test, &data); @@ -2588,16 +2730,15 @@ static gboolean gtk_icon_view_real_activate_cursor_item (GtkIconView *icon_view) { GtkTreePath *path; + GtkCellAreaContext *context; if (!icon_view->priv->cursor_item) return FALSE; + context = g_ptr_array_index (icon_view->priv->row_contexts, icon_view->priv->cursor_item->row); + _gtk_icon_view_set_cell_data (icon_view, icon_view->priv->cursor_item); - gtk_cell_area_context_allocate (icon_view->priv->cell_area_context, - icon_view->priv->cursor_item->cell_area.width, - icon_view->priv->cursor_item->cell_area.height); - gtk_cell_area_activate (icon_view->priv->cell_area, - icon_view->priv->cell_area_context, + gtk_cell_area_activate (icon_view->priv->cell_area, context, GTK_WIDGET (icon_view), &icon_view->priv->cursor_item->cell_area, 0 /* XXX flags */, @@ -2774,7 +2915,7 @@ 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); } } @@ -2794,41 +2935,33 @@ gtk_icon_view_layout (GtkIconView *icon_view) GtkIconViewPrivate *priv = icon_view->priv; GtkWidget *widget = GTK_WIDGET (icon_view); GList *items; - gint min_item_width, max_item_width; /* These include item_padding */ gint item_width; /* this doesn't include item_padding */ gint n_columns, n_rows, n_items; gint col, row; GtkRequestedSize *sizes; + gboolean rtl; + + if (gtk_icon_view_is_empty (icon_view)) + return; + rtl = gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL; n_items = gtk_icon_view_get_n_items (icon_view); gtk_icon_view_compute_n_items_for_size (icon_view, GTK_ORIENTATION_HORIZONTAL, gtk_widget_get_allocated_width (widget), - NULL, - &n_columns); + NULL, NULL, + &n_columns, &item_width); n_rows = (n_items + n_columns - 1) / n_columns; - gtk_icon_view_get_preferred_item_size (icon_view, GTK_ORIENTATION_HORIZONTAL, -1, &min_item_width, &max_item_width); + 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)); - if (n_columns <= 1) - { - /* We might need vertical scrolling here */ - int min_width = min_item_width + 2 * priv->margin; - priv->width = MAX (min_width, gtk_widget_get_allocated_width (widget)); - } - else - { - priv->width = gtk_widget_get_allocated_width (widget); - } - - item_width = (priv->width - 2 * priv->margin + priv->column_spacing) / n_columns; - item_width -= priv->column_spacing; - item_width = MIN (item_width, max_item_width); - item_width -= 2 * priv->item_padding; + /* Clear the per row contexts */ + g_ptr_array_set_size (icon_view->priv->row_contexts, 0); gtk_cell_area_context_reset (priv->cell_area_context); - gtk_cell_area_context_allocate (priv->cell_area_context, item_width, -1); /* because layouting is complicated. We designed an API * that is O(N²) and nonsensical. * And we're proud of it. */ @@ -2848,20 +2981,23 @@ gtk_icon_view_layout (GtkIconView *icon_view) /* Collect the heights for all rows */ for (row = 0; row < n_rows; row++) { + GtkCellAreaContext *context = gtk_cell_area_copy_context (priv->cell_area, priv->cell_area_context); + g_ptr_array_add (priv->row_contexts, context); + for (col = 0; col < n_columns && items; col++, items = items->next) { GtkIconViewItem *item = items->data; _gtk_icon_view_set_cell_data (icon_view, item); gtk_cell_area_get_preferred_height_for_width (priv->cell_area, - priv->cell_area_context, + context, widget, item_width, NULL, NULL); } sizes[row].data = GINT_TO_POINTER (row); - gtk_cell_area_context_get_preferred_height_for_width (priv->cell_area_context, + gtk_cell_area_context_get_preferred_height_for_width (context, item_width, &sizes[row].minimum_size, &sizes[row].natural_size); @@ -2884,6 +3020,9 @@ gtk_icon_view_layout (GtkIconView *icon_view) 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); + priv->height += priv->item_padding; for (col = 0; col < n_columns && items; col++, items = items->next) @@ -2896,8 +3035,13 @@ gtk_icon_view_layout (GtkIconView *icon_view) 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; + } } - + priv->height += sizes[row].minimum_size + priv->item_padding + priv->row_spacing; } @@ -2938,8 +3082,9 @@ gtk_icon_view_paint_item (GtkIconView *icon_view, GtkStyleContext *style_context; GtkWidget *widget = GTK_WIDGET (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); @@ -2948,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; } @@ -2992,8 +3134,8 @@ gtk_icon_view_paint_item (GtkIconView *icon_view, cell_area.width = item->cell_area.width; cell_area.height = item->cell_area.height; - gtk_cell_area_context_allocate (priv->cell_area_context, item->cell_area.width, item->cell_area.height); - gtk_cell_area_render (priv->cell_area, priv->cell_area_context, + context = g_ptr_array_index (priv->row_contexts, item->row); + gtk_cell_area_render (priv->cell_area, context, widget, cr, &cell_area, &cell_area, flags, draw_focus); @@ -3170,19 +3312,17 @@ _gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, if (only_in_cell || cell_at_pos) { GtkCellRenderer *cell = NULL; + GtkCellAreaContext *context; + context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); _gtk_icon_view_set_cell_data (icon_view, item); if (x >= item_area->x && x <= item_area->x + item_area->width && y >= item_area->y && y <= item_area->y + item_area->height) - { - gtk_cell_area_context_allocate (icon_view->priv->cell_area_context, item->cell_area.width, item->cell_area.height); - cell = gtk_cell_area_get_cell_at_position (icon_view->priv->cell_area, - icon_view->priv->cell_area_context, - GTK_WIDGET (icon_view), - item_area, - x, y, NULL); - } + cell = gtk_cell_area_get_cell_at_position (icon_view->priv->cell_area, context, + GTK_WIDGET (icon_view), + item_area, + x, y, NULL); if (cell_at_pos) *cell_at_pos = cell; @@ -4363,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 @@ -4407,45 +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) - { - _gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_context_allocate (icon_view->priv->cell_area_context, item->cell_area.width, item->cell_area.height); - gtk_cell_area_get_cell_allocation (icon_view->priv->cell_area, - 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; - } - - gtk_tooltip_set_tip_area (tooltip, &rect); + gtk_tooltip_set_tip_area (tooltip, &rect); } @@ -4455,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 * @@ -4780,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)); @@ -4796,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; @@ -4880,6 +5058,9 @@ gtk_icon_view_set_model (GtkIconView *icon_view, g_object_notify (G_OBJECT (icon_view), "model"); + if (dirty) + g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); } @@ -5251,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. @@ -6628,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 @@ -7052,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,