X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkiconview.c;h=0c6dc038ce354701c740569e7f2b9e35d77f2ef9;hb=d98b4fd0b433d56f7b2148f617baf212014a3b3e;hp=161de84c0146a99b60d665aae57f4668f37e643a;hpb=2fb1c064020c5db189285b1d5e8b8dcea8e9d09b;p=~andy%2Fgtk diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c index 161de84c0..0c6dc038c 100644 --- a/gtk/gtkiconview.c +++ b/gtk/gtkiconview.c @@ -12,18 +12,17 @@ * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ #include "config.h" #include -#include - #include "gtkiconview.h" +#include "gtkiconviewprivate.h" + +#include "gtkadjustment.h" #include "gtkcelllayout.h" #include "gtkcellrenderer.h" #include "gtkcellareabox.h" @@ -40,12 +39,12 @@ #include "gtkwindow.h" #include "gtkentry.h" #include "gtkcombobox.h" -#include "gtktextbuffer.h" #include "gtkscrollable.h" #include "gtksizerequest.h" #include "gtktreednd.h" #include "gtktypebuiltins.h" #include "gtkprivate.h" +#include "a11y/gtkiconviewaccessibleprivate.h" /** * SECTION:gtkiconview @@ -67,25 +66,6 @@ #define SCROLL_EDGE_SIZE 15 -#define GTK_ICON_VIEW_PRIORITY_LAYOUT (GDK_PRIORITY_REDRAW + 5) - -typedef struct _GtkIconViewItem GtkIconViewItem; -struct _GtkIconViewItem -{ - /* First member is always the rectangle so it - * can be cast to a rectangle. */ - GdkRectangle cell_area; - - GtkTreeIter iter; - gint index; - - gint row, col; - - guint selected : 1; - guint selected_before_rubberbanding : 1; - -}; - typedef struct _GtkIconViewChild GtkIconViewChild; struct _GtkIconViewChild { @@ -93,103 +73,6 @@ struct _GtkIconViewChild GdkRectangle area; }; -struct _GtkIconViewPrivate -{ - GtkCellArea *cell_area; - GtkCellAreaContext *cell_area_context; - - gulong add_editable_id; - gulong remove_editable_id; - gulong context_changed_id; - - GPtrArray *row_contexts; - - gint width, height; - - GtkSelectionMode selection_mode; - - GdkWindow *bin_window; - - GList *children; - - GtkTreeModel *model; - - GList *items; - - GtkAdjustment *hadjustment; - GtkAdjustment *vadjustment; - - guint layout_idle_id; - - gint rubberband_x1, rubberband_y1; - gint rubberband_x2, rubberband_y2; - GdkDevice *rubberband_device; - - guint scroll_timeout_id; - gint scroll_value_diff; - gint event_last_x, event_last_y; - - GtkIconViewItem *anchor_item; - GtkIconViewItem *cursor_item; - - GtkIconViewItem *last_single_clicked; - - GtkOrientation item_orientation; - - gint columns; - gint item_width; - gint spacing; - gint row_spacing; - gint column_spacing; - gint margin; - gint item_padding; - - gint text_column; - gint markup_column; - gint pixbuf_column; - - GtkCellRenderer *pixbuf_cell; - GtkCellRenderer *text_cell; - - gint tooltip_column; - - /* Drag-and-drop. */ - GdkModifierType start_button_mask; - gint pressed_button; - gint press_start_x; - gint press_start_y; - - GdkDragAction source_actions; - GdkDragAction dest_actions; - - GtkTreeRowReference *dest_item; - GtkIconViewDropPosition dest_pos; - - /* scroll to */ - GtkTreeRowReference *scroll_to_path; - gfloat scroll_to_row_align; - gfloat scroll_to_col_align; - guint scroll_to_use_align : 1; - - guint source_set : 1; - guint dest_set : 1; - guint reorderable : 1; - guint empty_view_drop :1; - - guint ctrl_pressed : 1; - guint shift_pressed : 1; - - guint draw_focus : 1; - - /* GtkScrollablePolicy needs to be checked when - * driving the scrollable adjustment values */ - guint hscroll_policy : 1; - guint vscroll_policy : 1; - - guint doing_rubberband : 1; - -}; - /* Signals */ enum { @@ -224,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 */ @@ -250,16 +132,26 @@ static void gtk_icon_view_get_property (GObject static void gtk_icon_view_destroy (GtkWidget *widget); static void gtk_icon_view_realize (GtkWidget *widget); static void gtk_icon_view_unrealize (GtkWidget *widget); -static void gtk_icon_view_style_set (GtkWidget *widget, - GtkStyle *previous_style); -static void gtk_icon_view_state_changed (GtkWidget *widget, - GtkStateType previous_state); +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, @@ -274,7 +166,6 @@ static gboolean gtk_icon_view_key_press (GtkWidget GdkEventKey *event); static gboolean gtk_icon_view_key_release (GtkWidget *widget, GdkEventKey *event); -static AtkObject *gtk_icon_view_get_accessible (GtkWidget *widget); /* GtkContainer vfuncs */ @@ -299,9 +190,6 @@ static void gtk_icon_view_set_hadjustment (GtkIco GtkAdjustment *adjustment); static void gtk_icon_view_set_vadjustment (GtkIconView *icon_view, GtkAdjustment *adjustment); -static void gtk_icon_view_accessible_set_adjustment (AtkObject *accessible, - GtkOrientation orientation, - GtkAdjustment *adjustment); static void gtk_icon_view_adjustment_changed (GtkAdjustment *adjustment, GtkIconView *icon_view); static void gtk_icon_view_layout (GtkIconView *icon_view); @@ -317,10 +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_set_cursor_item (GtkIconView *icon_view, - GtkIconViewItem *item, - GtkCellRenderer *cursor_cell); static void gtk_icon_view_start_rubberbanding (GtkIconView *icon_view, GdkDevice *device, gint x, @@ -334,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); @@ -356,20 +239,12 @@ static void gtk_icon_view_move_cursor_start_end (GtkIco gint count); static void gtk_icon_view_scroll_to_item (GtkIconView *icon_view, GtkIconViewItem *item); -static void gtk_icon_view_select_item (GtkIconView *icon_view, - GtkIconViewItem *item); -static void gtk_icon_view_unselect_item (GtkIconView *icon_view, - GtkIconViewItem *item); static gboolean gtk_icon_view_select_all_between (GtkIconView *icon_view, GtkIconViewItem *anchor, GtkIconViewItem *cursor); -static GtkIconViewItem * gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, - gint x, - gint y, - gboolean only_in_cell, - GtkCellRenderer **cell_at_pos); -static void gtk_icon_view_set_cell_data (GtkIconView *icon_view, - GtkIconViewItem *item); + +static void gtk_icon_view_ensure_cell_area (GtkIconView *icon_view, + GtkCellArea *cell_area); static GtkCellArea *gtk_icon_view_cell_layout_get_area (GtkCellLayout *layout); @@ -386,9 +261,8 @@ 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); /* Source side drag signals */ static void gtk_icon_view_drag_begin (GtkWidget *widget, @@ -477,10 +351,12 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) widget_class->destroy = gtk_icon_view_destroy; widget_class->realize = gtk_icon_view_realize; widget_class->unrealize = gtk_icon_view_unrealize; - widget_class->style_set = gtk_icon_view_style_set; - widget_class->get_accessible = gtk_icon_view_get_accessible; + 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; @@ -496,7 +372,7 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) widget_class->drag_motion = gtk_icon_view_drag_motion; widget_class->drag_drop = gtk_icon_view_drag_drop; widget_class->drag_data_received = gtk_icon_view_drag_data_received; - widget_class->state_changed = gtk_icon_view_state_changed; + widget_class->state_flags_changed = gtk_icon_view_state_flags_changed; container_class->remove = gtk_icon_view_remove; container_class->forall = gtk_icon_view_forall; @@ -754,6 +630,9 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) * * The #GtkCellArea used to layout cell renderers for this view. * + * If no area is specified when creating the icon view with gtk_icon_view_new_with_area() + * a #GtkCellAreaBox will be used. + * * Since: 3.0 */ g_object_class_install_property (gobject_class, @@ -764,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"); @@ -793,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"), @@ -1039,6 +936,8 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) GTK_MOVEMENT_VISUAL_POSITIONS, 1); gtk_icon_view_add_move_binding (binding_set, GDK_KEY_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1); + + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ICON_VIEW_ACCESSIBLE); } static void @@ -1087,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 */ @@ -1101,37 +1004,14 @@ gtk_icon_view_constructor (GType type, GObjectConstructParam *construct_properties) { GtkIconView *icon_view; - GtkIconViewPrivate *priv; GObject *object; object = G_OBJECT_CLASS (gtk_icon_view_parent_class)->constructor (type, n_construct_properties, construct_properties); icon_view = (GtkIconView *) object; - priv = icon_view->priv; - - if (!priv->cell_area) - { - priv->cell_area = gtk_cell_area_box_new (); - g_object_ref_sink (priv->cell_area); - } - - if (GTK_IS_ORIENTABLE (priv->cell_area)) - gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->cell_area), priv->item_orientation); - - gtk_cell_area_set_style_detail (priv->cell_area, "icon_view"); - - priv->cell_area_context = gtk_cell_area_create_context (priv->cell_area); - priv->add_editable_id = - g_signal_connect (priv->cell_area, "add-editable", - G_CALLBACK (gtk_icon_view_add_editable), 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); + gtk_icon_view_ensure_cell_area (icon_view, NULL); return object; } @@ -1147,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; } @@ -1237,12 +1114,24 @@ 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); - if (area) - icon_view->priv->cell_area = g_object_ref_sink (area); + { + if (icon_view->priv->cell_area != NULL) + { + g_warning ("cell-area has already been set, ignoring construct property"); + g_object_ref_sink (area); + g_object_unref (area); + } + else + gtk_icon_view_ensure_cell_area (icon_view, area); + } break; case PROP_HADJUSTMENT: @@ -1325,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; @@ -1356,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); @@ -1393,6 +1280,7 @@ gtk_icon_view_realize (GtkWidget *widget) GdkWindow *window; GdkWindowAttr attributes; gint attributes_mask; + GtkStyleContext *context; gtk_widget_set_realized (widget, TRUE); @@ -1413,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); @@ -1423,21 +1311,22 @@ gtk_icon_view_realize (GtkWidget *widget) attributes.width = MAX (icon_view->priv->width, allocation.width); attributes.height = MAX (icon_view->priv->height, allocation.height); attributes.event_mask = (GDK_EXPOSURE_MASK | - GDK_SCROLL_MASK | - GDK_POINTER_MOTION_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_KEY_PRESS_MASK | - GDK_KEY_RELEASE_MASK) | + GDK_SCROLL_MASK | + GDK_SMOOTH_SCROLL_MASK | + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK) | gtk_widget_get_events (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); - gtk_widget_style_attach (widget); - gdk_window_set_background (icon_view->priv->bin_window, - >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]); + context = gtk_widget_get_style_context (widget); + gtk_style_context_set_background (context, icon_view->priv->bin_window); + gtk_style_context_set_background (context, window); gdk_window_show (icon_view->priv->bin_window); } @@ -1449,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; @@ -1457,238 +1346,610 @@ gtk_icon_view_unrealize (GtkWidget *widget) } static void -gtk_icon_view_state_changed (GtkWidget *widget, - GtkStateType previous_state) +_gtk_icon_view_update_background (GtkIconView *icon_view) { - GtkIconView *icon_view = GTK_ICON_VIEW (widget); - GtkStateType state; - GtkStyle *style; + GtkWidget *widget = GTK_WIDGET (icon_view); if (gtk_widget_get_realized (widget)) { - style = gtk_widget_get_style (widget); - state = gtk_widget_get_state (widget); + GtkStyleContext *context; - gdk_window_set_background (gtk_widget_get_window (widget), &style->base[state]); - gdk_window_set_background (icon_view->priv->bin_window, &style->base[state]); + context = gtk_widget_get_style_context (widget); + gtk_style_context_set_background (context, gtk_widget_get_window (widget)); + gtk_style_context_set_background (context, icon_view->priv->bin_window); } +} +static void +gtk_icon_view_state_flags_changed (GtkWidget *widget, + GtkStateFlags previous_state) +{ + _gtk_icon_view_update_background (GTK_ICON_VIEW (widget)); gtk_widget_queue_draw (widget); } static void -gtk_icon_view_style_set (GtkWidget *widget, - GtkStyle *previous_style) +gtk_icon_view_style_updated (GtkWidget *widget) { - GtkIconView *icon_view = GTK_ICON_VIEW (widget); - GtkStateType state; - GtkStyle *style; - - if (gtk_widget_get_realized (widget)) - { - style = gtk_widget_get_style (widget); - state = gtk_widget_get_state (widget); - - gdk_window_set_background (gtk_widget_get_window (widget), &style->base[state]); - gdk_window_set_background (icon_view->priv->bin_window, &style->base[state]); - } + GTK_WIDGET_CLASS (gtk_icon_view_parent_class)->style_updated (widget); + _gtk_icon_view_update_background (GTK_ICON_VIEW (widget)); gtk_widget_queue_resize (widget); } -static void -gtk_icon_view_get_preferred_width (GtkWidget *widget, - gint *minimum, - gint *natural) +static gint +gtk_icon_view_get_n_items (GtkIconView *icon_view) { - *minimum = *natural = GTK_ICON_VIEW (widget)->priv->width; -} + GtkIconViewPrivate *priv = icon_view->priv; -static void -gtk_icon_view_get_preferred_height (GtkWidget *widget, - gint *minimum, - gint *natural) -{ - *minimum = *natural = GTK_ICON_VIEW (widget)->priv->height; + if (priv->model == NULL) + return 0; + + return gtk_tree_model_iter_n_children (priv->model, NULL); } static void -gtk_icon_view_allocate_children (GtkIconView *icon_view) +adjust_wrap_width (GtkIconView *icon_view) { - GList *list; - - for (list = icon_view->priv->children; list; list = list->next) + if (icon_view->priv->text_cell) { - GtkIconViewChild *child = list->data; + gint wrap_width = 50; - /* totally ignore our child's requisition */ - gtk_widget_size_allocate (child->widget, &child->area); + /* 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 -gtk_icon_view_size_allocate (GtkWidget *widget, - GtkAllocation *allocation) +cell_area_get_preferred_size (GtkIconView *icon_view, + GtkCellAreaContext *context, + GtkOrientation orientation, + gint for_size, + gint *minimum, + gint *natural) { - GtkIconView *icon_view = GTK_ICON_VIEW (widget); - - gtk_widget_set_allocation (widget, allocation); - - if (gtk_widget_get_realized (widget)) + if (orientation == GTK_ORIENTATION_HORIZONTAL) { - gdk_window_move_resize (gtk_widget_get_window (widget), - allocation->x, allocation->y, - allocation->width, allocation->height); - gdk_window_resize (icon_view->priv->bin_window, - MAX (icon_view->priv->width, allocation->width), - MAX (icon_view->priv->height, allocation->height)); + 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); } - - gtk_icon_view_layout (icon_view); - - gtk_icon_view_allocate_children (icon_view); - - /* Delay signal emission */ - g_object_freeze_notify (G_OBJECT (icon_view->priv->hadjustment)); - g_object_freeze_notify (G_OBJECT (icon_view->priv->vadjustment)); - - gtk_icon_view_set_hadjustment_values (icon_view); - gtk_icon_view_set_vadjustment_values (icon_view); - - if (gtk_widget_get_realized (widget) && - icon_view->priv->scroll_to_path) + else { - GtkTreePath *path; - 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); + 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); } - - /* Emit any pending signals now */ - g_object_thaw_notify (G_OBJECT (icon_view->priv->hadjustment)); - g_object_thaw_notify (G_OBJECT (icon_view->priv->vadjustment)); } static gboolean -gtk_icon_view_draw (GtkWidget *widget, - cairo_t *cr) +gtk_icon_view_is_empty (GtkIconView *icon_view) { - GtkIconView *icon_view; - GList *icons; - GtkTreePath *path; - gint dest_index; - GtkIconViewDropPosition dest_pos; - GtkIconViewItem *dest_item = NULL; + return icon_view->priv->items == NULL; +} - icon_view = GTK_ICON_VIEW (widget); +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; - if (!gtk_cairo_should_draw_window (cr, icon_view->priv->bin_window)) - return FALSE; + g_assert (!gtk_icon_view_is_empty (icon_view)); - cairo_save (cr); + context = gtk_cell_area_create_context (priv->cell_area); - gtk_cairo_transform_to_window (cr, widget, icon_view->priv->bin_window); - - cairo_set_line_width (cr, 1.); + for_size -= 2 * priv->item_padding; - gtk_icon_view_get_drag_dest_item (icon_view, &path, &dest_pos); + 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; - if (path) + _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) { - dest_index = gtk_tree_path_get_indices (path)[0]; - gtk_tree_path_free (path); + 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); } - else - dest_index = -1; - for (icons = icon_view->priv->items; icons; icons = icons->next) + if (orientation == GTK_ORIENTATION_HORIZONTAL) { - GtkIconViewItem *item = icons->data; - GdkRectangle paint_area; + 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); + } - 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; - - cairo_save (cr); + if (orientation == GTK_ORIENTATION_HORIZONTAL && priv->item_width >= 0) + { + if (minimum) + *minimum = MAX (*minimum, priv->item_width); + if (natural) + *natural = *minimum; + } - cairo_rectangle (cr, paint_area.x, paint_area.y, paint_area.width, paint_area.height); - cairo_clip (cr); + if (minimum) + *minimum = MAX (1, *minimum + 2 * priv->item_padding); + if (natural) + *natural = MAX (1, *natural + 2 * priv->item_padding); - if (gdk_cairo_get_clip_rectangle (cr, NULL)) - { - gtk_icon_view_paint_item (icon_view, cr, item, - ((GdkRectangle *)item)->x, ((GdkRectangle *)item)->y, - icon_view->priv->draw_focus); - - if (dest_index == item->index) - dest_item = item; - } + g_object_unref (context); +} - cairo_restore (cr); - } +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; - if (dest_item) - { - GtkStateType state; - GtkStyle *style; + 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)); - style = gtk_widget_get_style (widget); - state = gtk_widget_get_state (widget); + gtk_icon_view_get_preferred_item_size (icon_view, orientation, -1, &minimum, &natural); - switch (dest_pos) + 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_height (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + 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_for_width (GtkWidget *widget, + gint width, + gint *minimum, + gint *natural) +{ + 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 +gtk_icon_view_allocate_children (GtkIconView *icon_view) +{ + GList *list; + + for (list = icon_view->priv->children; list; list = list->next) + { + GtkIconViewChild *child = list->data; + + /* totally ignore our child's requisition */ + gtk_widget_size_allocate (child->widget, &child->area); + } +} + +static void +gtk_icon_view_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (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), + allocation->x, allocation->y, + allocation->width, allocation->height); + gdk_window_resize (icon_view->priv->bin_window, + MAX (icon_view->priv->width, allocation->width), + MAX (icon_view->priv->height, allocation->height)); + } + + gtk_icon_view_allocate_children (icon_view); + + /* Delay signal emission */ + g_object_freeze_notify (G_OBJECT (icon_view->priv->hadjustment)); + g_object_freeze_notify (G_OBJECT (icon_view->priv->vadjustment)); + + gtk_icon_view_set_hadjustment_values (icon_view); + gtk_icon_view_set_vadjustment_values (icon_view); + + if (gtk_widget_get_realized (widget) && + icon_view->priv->scroll_to_path) + { + GtkTreePath *path; + 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); + } + + /* Emit any pending signals now */ + g_object_thaw_notify (G_OBJECT (icon_view->priv->hadjustment)); + g_object_thaw_notify (G_OBJECT (icon_view->priv->vadjustment)); +} + +static gboolean +gtk_icon_view_draw (GtkWidget *widget, + cairo_t *cr) +{ + GtkIconView *icon_view; + GList *icons; + GtkTreePath *path; + 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; + + cairo_save (cr); + + gtk_cairo_transform_to_window (cr, widget, icon_view->priv->bin_window); + + cairo_set_line_width (cr, 1.); + + gtk_icon_view_get_drag_dest_item (icon_view, &path, &dest_pos); + + if (path) + { + dest_index = gtk_tree_path_get_indices (path)[0]; + gtk_tree_path_free (path); + } + else + dest_index = -1; + + for (icons = icon_view->priv->items; icons; icons = icons->next) + { + GtkIconViewItem *item = icons->data; + GdkRectangle paint_area; + + 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); + + cairo_rectangle (cr, paint_area.x, paint_area.y, paint_area.width, paint_area.height); + cairo_clip (cr); + + if (gdk_cairo_get_clip_rectangle (cr, NULL)) + { + gtk_icon_view_paint_item (icon_view, cr, item, + item->cell_area.x, item->cell_area.y, + icon_view->priv->draw_focus); + + if (dest_index == item->index) + dest_item = item; + } + + cairo_restore (cr); + } + + if (dest_item && + dest_pos != GTK_ICON_VIEW_NO_DROP) + { + GtkStyleContext *context; + GdkRectangle rect = { 0 }; + + context = gtk_widget_get_style_context (widget); + + switch (dest_pos) { case GTK_ICON_VIEW_DROP_INTO: - gtk_paint_focus (style, cr, state, widget, - "iconview-drop-indicator", - dest_item->cell_area.x, dest_item->cell_area.y, - dest_item->cell_area.width, dest_item->cell_area.height); + rect = dest_item->cell_area; break; case GTK_ICON_VIEW_DROP_ABOVE: - gtk_paint_focus (style, cr, state, widget, - "iconview-drop-indicator", - dest_item->cell_area.x, dest_item->cell_area.y - 1, - dest_item->cell_area.width, 2); + rect.x = dest_item->cell_area.x; + rect.y = dest_item->cell_area.y - 1; + rect.width = dest_item->cell_area.width; + rect.height = 2; break; case GTK_ICON_VIEW_DROP_LEFT: - gtk_paint_focus (style, cr, state, widget, - "iconview-drop-indicator", - dest_item->cell_area.x - 1, dest_item->cell_area.y, - 2, dest_item->cell_area.height); + rect.x = dest_item->cell_area.x - 1; + rect.y = dest_item->cell_area.y; + rect.width = 2; + rect.height = dest_item->cell_area.height; break; case GTK_ICON_VIEW_DROP_BELOW: - gtk_paint_focus (style, cr, state, widget, - "iconview-drop-indicator", - dest_item->cell_area.x, - dest_item->cell_area.y + dest_item->cell_area.height - 1, - dest_item->cell_area.width, 2); + rect.x = dest_item->cell_area.x; + rect.y = dest_item->cell_area.y + dest_item->cell_area.height - 1; + rect.width = dest_item->cell_area.width; + rect.height = 2; break; case GTK_ICON_VIEW_DROP_RIGHT: - gtk_paint_focus (style, cr, state, widget, - "iconview-drop-indicator", - dest_item->cell_area.x + dest_item->cell_area.width - 1, - dest_item->cell_area.y, - 2, dest_item->cell_area.height); + rect.x = dest_item->cell_area.x + dest_item->cell_area.width - 1; + rect.y = dest_item->cell_area.y; + rect.width = 2; + rect.height = dest_item->cell_area.height; case GTK_ICON_VIEW_NO_DROP: ; break; - } + } + + gtk_render_focus (context, cr, + rect.x, rect.y, + rect.width, rect.height); } - + if (icon_view->priv->doing_rubberband) gtk_icon_view_paint_rubberband (icon_view, cr); cairo_restore (cr); - GTK_WIDGET_CLASS (gtk_icon_view_parent_class)->draw (widget, cr); - - return TRUE; + return GTK_WIDGET_CLASS (gtk_icon_view_parent_class)->draw (widget, cr); } static gboolean @@ -1745,6 +2006,33 @@ gtk_icon_view_motion (GtkWidget *widget, else remove_scroll_timeout (icon_view); } + else + { + GtkIconViewItem *item, *last_prelight_item; + GtkCellRenderer *cell = NULL; + + last_prelight_item = icon_view->priv->last_prelight; + item = _gtk_icon_view_get_item_at_coords (icon_view, + event->x, event->y, + FALSE, + &cell); + + if (item != NULL) + { + item->prelight = TRUE; + gtk_icon_view_queue_draw_item (icon_view, item); + } + + if (last_prelight_item != NULL && + last_prelight_item != item) + { + last_prelight_item->prelight = FALSE; + gtk_icon_view_queue_draw_item (icon_view, + icon_view->priv->last_prelight); + } + + icon_view->priv->last_prelight = item; + } return TRUE; } @@ -1864,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 @@ -1907,7 +2183,8 @@ gtk_icon_view_set_cursor (GtkIconView *icon_view, g_return_if_fail (path != NULL); g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell)); - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); if (gtk_tree_path_get_depth (path) == 1) item = g_list_nth_data (icon_view->priv->items, @@ -1916,17 +2193,18 @@ gtk_icon_view_set_cursor (GtkIconView *icon_view, if (!item) return; - gtk_icon_view_set_cursor_item (icon_view, item, cell); + _gtk_icon_view_set_cursor_item (icon_view, item, cell); gtk_icon_view_scroll_to_path (icon_view, path, FALSE, 0.0, 0.0); - if (start_editing) + 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_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); } } @@ -1967,7 +2245,7 @@ gtk_icon_view_get_cursor (GtkIconView *icon_view, *path = NULL; } - if (cell != NULL && item != NULL) + if (cell != NULL && item != NULL && icon_view->priv->cell_area != NULL) *cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); return (item != NULL); @@ -1990,15 +2268,24 @@ gtk_icon_view_button_press (GtkWidget *widget, if (!gtk_widget_has_focus (widget)) gtk_widget_grab_focus (widget); - if (event->button == 1 && event->type == GDK_BUTTON_PRESS) + if (event->button == GDK_BUTTON_PRIMARY && event->type == GDK_BUTTON_PRESS) { - item = gtk_icon_view_get_item_at_coords (icon_view, + GdkModifierType extend_mod_mask; + GdkModifierType modify_mod_mask; + + extend_mod_mask = + gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION); + + modify_mod_mask = + gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION); + + item = _gtk_icon_view_get_item_at_coords (icon_view, event->x, event->y, FALSE, &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. */ @@ -2014,14 +2301,14 @@ gtk_icon_view_button_press (GtkWidget *widget, if (icon_view->priv->selection_mode == GTK_SELECTION_NONE) { - gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); + _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); } else if (icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE && - (event->state & GDK_SHIFT_MASK)) + (event->state & extend_mod_mask)) { gtk_icon_view_unselect_all_internal (icon_view); - gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); + _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); if (!icon_view->priv->anchor_item) icon_view->priv->anchor_item = item; else @@ -2034,7 +2321,7 @@ gtk_icon_view_button_press (GtkWidget *widget, { if ((icon_view->priv->selection_mode == GTK_SELECTION_MULTIPLE || ((icon_view->priv->selection_mode == GTK_SELECTION_SINGLE) && item->selected)) && - (event->state & GDK_CONTROL_MASK)) + (event->state & modify_mod_mask)) { item->selected = !item->selected; gtk_icon_view_queue_draw_item (icon_view, item); @@ -2048,7 +2335,7 @@ gtk_icon_view_button_press (GtkWidget *widget, gtk_icon_view_queue_draw_item (icon_view, item); dirty = TRUE; } - gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); + _gtk_icon_view_set_cursor_item (icon_view, item, cursor_cell); icon_view->priv->anchor_item = item; } @@ -2072,16 +2359,16 @@ gtk_icon_view_button_press (GtkWidget *widget, context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); - gtk_icon_view_set_cell_data (icon_view, item); + _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 { if (icon_view->priv->selection_mode != GTK_SELECTION_BROWSE && - !(event->state & GDK_CONTROL_MASK)) + !(event->state & modify_mod_mask)) { dirty = gtk_icon_view_unselect_all_internal (icon_view); } @@ -2094,9 +2381,11 @@ gtk_icon_view_button_press (GtkWidget *widget, icon_view->priv->draw_focus = FALSE; } - if (event->button == 1 && 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, + item = _gtk_icon_view_get_item_at_coords (icon_view, event->x, event->y, FALSE, NULL); @@ -2117,7 +2406,13 @@ gtk_icon_view_button_press (GtkWidget *widget, if (dirty) g_signal_emit (icon_view, icon_view_signals[SELECTION_CHANGED], 0); - return event->button == 1; + 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 @@ -2135,11 +2430,33 @@ gtk_icon_view_button_release (GtkWidget *widget, remove_scroll_timeout (icon_view); - return TRUE; -} + 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; -static gboolean -gtk_icon_view_key_press (GtkWidget *widget, + 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; +} + +static gboolean +gtk_icon_view_key_press (GtkWidget *widget, GdkEventKey *event) { GtkIconView *icon_view = GTK_ICON_VIEW (widget); @@ -2344,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) @@ -2352,7 +2669,7 @@ gtk_icon_view_item_hit_test (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_icon_view_set_cell_data (icon_view, item); gtk_cell_area_foreach_alloc (icon_view->priv->cell_area, context, GTK_WIDGET (icon_view), item_area, item_area, @@ -2404,28 +2721,28 @@ static void gtk_icon_view_real_select_cursor_item (GtkIconView *icon_view) { gtk_icon_view_unselect_all (icon_view); - + if (icon_view->priv->cursor_item != NULL) - gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); + _gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); } static gboolean gtk_icon_view_real_activate_cursor_item (GtkIconView *icon_view) { GtkTreePath *path; - gboolean activated; 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); - activated = gtk_cell_area_activate (icon_view->priv->cell_area, context, - GTK_WIDGET (icon_view), - (GdkRectangle *)icon_view->priv->cursor_item, - 0 /* XXX flags */, FALSE); + _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), + &icon_view->priv->cursor_item->cell_area, + 0 /* XXX flags */, + FALSE); path = gtk_tree_path_new_from_indices (icon_view->priv->cursor_item->index, -1); gtk_icon_view_item_activated (icon_view, path); @@ -2445,13 +2762,13 @@ gtk_icon_view_real_toggle_cursor_item (GtkIconView *icon_view) case GTK_SELECTION_NONE: break; case GTK_SELECTION_BROWSE: - gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); + _gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); break; case GTK_SELECTION_SINGLE: if (icon_view->priv->cursor_item->selected) - gtk_icon_view_unselect_item (icon_view, icon_view->priv->cursor_item); + _gtk_icon_view_unselect_item (icon_view, icon_view->priv->cursor_item); else - gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); + _gtk_icon_view_select_item (icon_view, icon_view->priv->cursor_item); break; case GTK_SELECTION_MULTIPLE: icon_view->priv->cursor_item->selected = !icon_view->priv->cursor_item->selected; @@ -2463,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) { @@ -2545,7 +2848,6 @@ gtk_icon_view_set_hadjustment (GtkIconView *icon_view, GtkAdjustment *adjustment) { GtkIconViewPrivate *priv = icon_view->priv; - AtkObject *atk_obj; if (adjustment && priv->hadjustment == adjustment) return; @@ -2567,11 +2869,6 @@ gtk_icon_view_set_hadjustment (GtkIconView *icon_view, priv->hadjustment = g_object_ref_sink (adjustment); gtk_icon_view_set_hadjustment_values (icon_view); - atk_obj = gtk_widget_get_accessible (GTK_WIDGET (icon_view)); - gtk_icon_view_accessible_set_adjustment (atk_obj, - GTK_ORIENTATION_HORIZONTAL, - adjustment); - g_object_notify (G_OBJECT (icon_view), "hadjustment"); } @@ -2580,7 +2877,6 @@ gtk_icon_view_set_vadjustment (GtkIconView *icon_view, GtkAdjustment *adjustment) { GtkIconViewPrivate *priv = icon_view->priv; - AtkObject *atk_obj; if (adjustment && priv->vadjustment == adjustment) return; @@ -2602,11 +2898,6 @@ gtk_icon_view_set_vadjustment (GtkIconView *icon_view, priv->vadjustment = g_object_ref_sink (adjustment); gtk_icon_view_set_vadjustment_values (icon_view); - atk_obj = gtk_widget_get_accessible (GTK_WIDGET (icon_view)); - gtk_icon_view_accessible_set_adjustment (atk_obj, - GTK_ORIENTATION_VERTICAL, - adjustment); - g_object_notify (G_OBJECT (icon_view), "vadjustment"); } @@ -2625,263 +2916,138 @@ 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_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; + rtl = gtk_widget_get_direction (GTK_WIDGET (icon_view)) == GTK_TEXT_DIR_RTL; + n_items = gtk_icon_view_get_n_items (icon_view); - /* Update the wrap width for the text cell before going and requesting sizes */ - adjust_wrap_width (icon_view); + 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; - /* Update the context widths for any invalidated items */ - gtk_icon_view_cache_widths (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_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) - { - icon_view->priv->width = maximum_width; - size_changed = TRUE; - } - y += icon_view->priv->margin; - - if (y != icon_view->priv->height) + 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->height = y; - 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); } - gtk_icon_view_set_hadjustment_values (icon_view); - gtk_icon_view_set_vadjustment_values (icon_view); - - if (size_changed) - gtk_widget_queue_resize_no_redraw (widget); - - 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)); + sizes = g_newa (GtkRequestedSize, n_rows); + items = priv->items; + priv->height = priv->margin; - if (icon_view->priv->scroll_to_path) + /* Collect the heights for all rows */ + for (row = 0; row < n_rows; row++) { - GtkTreePath *path; + GtkCellAreaContext *context = gtk_cell_area_copy_context (priv->cell_area, priv->cell_area_context); + g_ptr_array_add (priv->row_contexts, context); - 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; + 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, + context, + widget, + item_width, + NULL, 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); + 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_queue_draw (widget); -} -/* 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; + priv->height -= priv->row_spacing; + priv->height += priv->margin; + priv->height = MIN (priv->height, gtk_widget_get_allocated_height (widget)); - g_signal_handler_block (icon_view->priv->cell_area_context, - icon_view->priv->context_changed_id); + gtk_distribute_natural_allocation (gtk_widget_get_allocated_height (widget) - priv->height, + n_rows, + sizes); - for (items = icon_view->priv->items; items; items = items->next) + /* Actually allocate the rows */ + g_qsort_with_data (sizes, n_rows, sizeof (GtkRequestedSize), compare_sizes, NULL); + + items = priv->items; + priv->height = priv->margin; + + for (row = 0; row < n_rows; row++) { - GtkIconViewItem *item = items->data; + GtkCellAreaContext *context = g_ptr_array_index (priv->row_contexts, row); + gtk_cell_area_context_allocate (context, item_width, sizes[row].minimum_size); - /* 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 += priv->item_padding; + + 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; + } + } + + 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 @@ -2891,15 +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 */ - 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 @@ -2911,52 +3070,63 @@ gtk_icon_view_item_invalidate_size (GtkIconViewItem *item) static void gtk_icon_view_paint_item (GtkIconView *icon_view, - cairo_t *cr, - GtkIconViewItem *item, - gint x, - gint y, - gboolean draw_focus) + cairo_t *cr, + GtkIconViewItem *item, + gint x, + gint y, + gboolean draw_focus) { GdkRectangle cell_area; - GtkStateType state; - GtkCellRendererState flags; + GtkStateFlags state = 0; + GtkCellRendererState flags = 0; + GtkStyleContext *style_context; GtkWidget *widget = GTK_WIDGET (icon_view); GtkIconViewPrivate *priv = icon_view->priv; - GtkStyle *style; GtkCellAreaContext *context; - if (priv->model == NULL) + if (priv->model == NULL || item->cell_area.width <= 0 || item->cell_area.height <= 0) return; - - style = gtk_widget_get_style (widget); - gtk_icon_view_set_cell_data (icon_view, item); - + + _gtk_icon_view_set_cell_data (icon_view, item); + + style_context = gtk_widget_get_style_context (widget); + state = gtk_widget_get_state_flags (widget); + + gtk_style_context_save (style_context); + 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) { - flags = GTK_CELL_RENDERER_SELECTED; - if (gtk_widget_has_focus (widget)) - state = GTK_STATE_SELECTED; - else - state = GTK_STATE_ACTIVE; + state |= GTK_STATE_FLAG_SELECTED; + flags |= GTK_CELL_RENDERER_SELECTED; } - else + + if (item->prelight) { - flags = 0; - state = GTK_STATE_NORMAL; + state |= GTK_STATE_FLAG_PRELIGHT; + flags |= GTK_CELL_RENDERER_PRELIT; } + gtk_style_context_set_state (style_context, state); + if (item->selected) { - gtk_paint_flat_box (style, - cr, - GTK_STATE_SELECTED, - GTK_SHADOW_NONE, - GTK_WIDGET (icon_view), - "icon_view_item", - x - icon_view->priv->item_padding, - y - icon_view->priv->item_padding, - item->cell_area.width + icon_view->priv->item_padding * 2, - item->cell_area.height + icon_view->priv->item_padding * 2); + gtk_render_background (style_context, cr, + x - icon_view->priv->item_padding, + y - icon_view->priv->item_padding, + item->cell_area.width + icon_view->priv->item_padding * 2, + item->cell_area.height + icon_view->priv->item_padding * 2); + gtk_render_frame (style_context, cr, + x - icon_view->priv->item_padding, + y - icon_view->priv->item_padding, + item->cell_area.width + icon_view->priv->item_padding * 2, + item->cell_area.height + icon_view->priv->item_padding * 2); } cell_area.x = x; @@ -2964,22 +3134,20 @@ gtk_icon_view_paint_item (GtkIconView *icon_view, cell_area.width = item->cell_area.width; cell_area.height = item->cell_area.height; - if (gtk_widget_has_focus (widget) && item == icon_view->priv->cursor_item) - flags |= GTK_CELL_RENDERER_FOCUSED; - 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); + widget, cr, &cell_area, &cell_area, flags, + draw_focus); + + gtk_style_context_restore (style_context); } static void gtk_icon_view_paint_rubberband (GtkIconView *icon_view, cairo_t *cr) { + GtkStyleContext *context; GdkRectangle rect; - GdkColor *fill_color_gdk; - guchar fill_color_alpha; cairo_save (cr); @@ -2988,28 +3156,22 @@ gtk_icon_view_paint_rubberband (GtkIconView *icon_view, rect.width = ABS (icon_view->priv->rubberband_x1 - icon_view->priv->rubberband_x2) + 1; rect.height = ABS (icon_view->priv->rubberband_y1 - icon_view->priv->rubberband_y2) + 1; - gtk_widget_style_get (GTK_WIDGET (icon_view), - "selection-box-color", &fill_color_gdk, - "selection-box-alpha", &fill_color_alpha, - NULL); - - if (!fill_color_gdk) - fill_color_gdk = gdk_color_copy (>k_widget_get_style (GTK_WIDGET (icon_view))->base[GTK_STATE_SELECTED]); + context = gtk_widget_get_style_context (GTK_WIDGET (icon_view)); - gdk_cairo_set_source_color (cr, fill_color_gdk); + gtk_style_context_save (context); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND); gdk_cairo_rectangle (cr, &rect); cairo_clip (cr); - cairo_paint_with_alpha (cr, fill_color_alpha / 255.); - - cairo_rectangle (cr, - rect.x + 0.5, rect.y + 0.5, - rect.width - 1, rect.height - 1); - cairo_stroke (cr); - - gdk_color_free (fill_color_gdk); + gtk_render_background (context, cr, + rect.x, rect.y, + rect.width, rect.height); + gtk_render_frame (context, cr, + rect.x, rect.y, + rect.width, rect.height); + gtk_style_context_restore (context); cairo_restore (cr); } @@ -3039,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; @@ -3050,35 +3212,10 @@ 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); -} - -static void -gtk_icon_view_set_cursor_item (GtkIconView *icon_view, - GtkIconViewItem *item, - GtkCellRenderer *cursor_cell) +void +_gtk_icon_view_set_cursor_item (GtkIconView *icon_view, + GtkIconViewItem *item, + GtkCellRenderer *cursor_cell) { AtkObject *obj; AtkObject *item_obj; @@ -3150,12 +3287,12 @@ gtk_icon_view_item_free (GtkIconViewItem *item) g_slice_free (GtkIconViewItem, item); } -static GtkIconViewItem * -gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, - gint x, - gint y, - gboolean only_in_cell, - GtkCellRenderer **cell_at_pos) +GtkIconViewItem * +_gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, + gint x, + gint y, + gboolean only_in_cell, + GtkCellRenderer **cell_at_pos) { GList *items; @@ -3165,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 && @@ -3178,7 +3315,7 @@ gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, GtkCellAreaContext *context; context = g_ptr_array_index (icon_view->priv->row_contexts, item->row); - gtk_icon_view_set_cell_data (icon_view, item); + _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) @@ -3201,9 +3338,9 @@ gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, return NULL; } -static void -gtk_icon_view_select_item (GtkIconView *icon_view, - GtkIconViewItem *item) +void +_gtk_icon_view_select_item (GtkIconView *icon_view, + GtkIconViewItem *item) { g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); g_return_if_fail (item != NULL); @@ -3225,9 +3362,9 @@ gtk_icon_view_select_item (GtkIconView *icon_view, } -static void -gtk_icon_view_unselect_item (GtkIconView *icon_view, - GtkIconViewItem *item) +void +_gtk_icon_view_unselect_item (GtkIconView *icon_view, + GtkIconViewItem *item) { g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); g_return_if_fail (item != NULL); @@ -3267,29 +3404,29 @@ verify_items (GtkIconView *icon_view) static void gtk_icon_view_row_changed (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) { GtkIconView *icon_view = GTK_ICON_VIEW (data); - GtkIconViewItem *item; - gint index; /* ignore changes in branches */ if (gtk_tree_path_get_depth (path) > 1) return; - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - - index = gtk_tree_path_get_indices(path)[0]; - item = g_list_nth_data (icon_view->priv->items, index); + /* An icon view subclass might add it's own model and populate + * things at init() time instead of waiting for the constructor() + * to be called + */ + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); /* Here we can use a "grow-only" strategy for optimization - * and only invalidate a single item and queue a relayout - * instead of invalidating the whole thing. + * and only invalidate a single item and queue a relayout + * instead of invalidating the whole thing. * * For now GtkIconView still cant deal with huge models - * so just invalidate the whole thing when the model + * so just invalidate the whole thing when the model * changes. */ gtk_icon_view_invalidate_sizes (icon_view); @@ -3306,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, @@ -3341,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 @@ -3364,7 +3495,8 @@ gtk_icon_view_row_deleted (GtkTreeModel *model, list = g_list_nth (icon_view->priv->items, index); item = list->data; - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); if (item == icon_view->priv->anchor_item) icon_view->priv->anchor_item = NULL; @@ -3372,6 +3504,9 @@ gtk_icon_view_row_deleted (GtkTreeModel *model, if (item == icon_view->priv->cursor_item) icon_view->priv->cursor_item = NULL; + if (item == icon_view->priv->last_prelight) + icon_view->priv->last_prelight = NULL; + if (item->selected) emit = TRUE; @@ -3388,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); @@ -3412,7 +3547,8 @@ gtk_icon_view_rows_reordered (GtkTreeModel *model, if (iter != NULL) return; - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); length = gtk_tree_model_iter_n_children (model, NULL); @@ -3435,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); } @@ -3445,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; @@ -3460,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++; @@ -3528,10 +3658,20 @@ gtk_icon_view_real_move_cursor (GtkIconView *icon_view, if (gtk_get_current_event_state (&state)) { - if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) - icon_view->priv->ctrl_pressed = TRUE; - if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) - icon_view->priv->shift_pressed = TRUE; + GdkModifierType extend_mod_mask; + GdkModifierType modify_mod_mask; + + extend_mod_mask = + gtk_widget_get_modifier_mask (GTK_WIDGET (icon_view), + GDK_MODIFIER_INTENT_EXTEND_SELECTION); + modify_mod_mask = + gtk_widget_get_modifier_mask (GTK_WIDGET (icon_view), + GDK_MODIFIER_INTENT_MODIFY_SELECTION); + + if ((state & modify_mod_mask) == modify_mod_mask) + icon_view->priv->modify_selection_pressed = TRUE; + if ((state & extend_mod_mask) == extend_mod_mask) + icon_view->priv->extend_selection_pressed = TRUE; } /* else we assume not pressed */ @@ -3554,8 +3694,8 @@ gtk_icon_view_real_move_cursor (GtkIconView *icon_view, g_assert_not_reached (); } - icon_view->priv->ctrl_pressed = FALSE; - icon_view->priv->shift_pressed = FALSE; + icon_view->priv->modify_selection_pressed = FALSE; + icon_view->priv->extend_selection_pressed = FALSE; icon_view->priv->draw_focus = TRUE; @@ -3693,7 +3833,7 @@ gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view, gint count) { GtkIconViewItem *item; - GtkCellRenderer *cell; + GtkCellRenderer *cell = NULL; gboolean dirty = FALSE; gint step; GtkDirectionType direction; @@ -3712,11 +3852,18 @@ gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view, else list = g_list_last (icon_view->priv->items); - item = list ? list->data : NULL; + if (list) + { + item = list->data; - /* Give focus to the first cell initially */ - gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_focus (icon_view->priv->cell_area, direction); + /* Give focus to the first cell initially */ + _gtk_icon_view_set_cell_data (icon_view, item); + gtk_cell_area_focus (icon_view->priv->cell_area, direction); + } + else + { + item = NULL; + } } else { @@ -3728,7 +3875,7 @@ gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view, while (item) { - gtk_icon_view_set_cell_data (icon_view, item); + _gtk_icon_view_set_cell_data (icon_view, item); if (gtk_cell_area_focus (icon_view->priv->cell_area, direction)) break; @@ -3754,16 +3901,16 @@ gtk_icon_view_move_cursor_up_down (GtkIconView *icon_view, return; } - if (icon_view->priv->ctrl_pressed || - !icon_view->priv->shift_pressed || + if (icon_view->priv->modify_selection_pressed || + !icon_view->priv->extend_selection_pressed || !icon_view->priv->anchor_item || icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) icon_view->priv->anchor_item = item; cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); - gtk_icon_view_set_cursor_item (icon_view, item, cell); + _gtk_icon_view_set_cursor_item (icon_view, item, cell); - if (!icon_view->priv->ctrl_pressed && + if (!icon_view->priv->modify_selection_pressed && icon_view->priv->selection_mode != GTK_SELECTION_NONE) { dirty = gtk_icon_view_unselect_all_internal (icon_view); @@ -3810,15 +3957,15 @@ gtk_icon_view_move_cursor_page_up_down (GtkIconView *icon_view, if (!item) return; - if (icon_view->priv->ctrl_pressed || - !icon_view->priv->shift_pressed || + if (icon_view->priv->modify_selection_pressed || + !icon_view->priv->extend_selection_pressed || !icon_view->priv->anchor_item || icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) icon_view->priv->anchor_item = item; - gtk_icon_view_set_cursor_item (icon_view, item, NULL); + _gtk_icon_view_set_cursor_item (icon_view, item, NULL); - if (!icon_view->priv->ctrl_pressed && + if (!icon_view->priv->modify_selection_pressed && icon_view->priv->selection_mode != GTK_SELECTION_NONE) { dirty = gtk_icon_view_unselect_all_internal (icon_view); @@ -3857,11 +4004,18 @@ gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view, else list = g_list_last (icon_view->priv->items); - item = list ? list->data : NULL; + if (list) + { + item = list->data; - /* Give focus to the first cell initially */ - gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_focus (icon_view->priv->cell_area, direction); + /* Give focus to the first cell initially */ + _gtk_icon_view_set_cell_data (icon_view, item); + gtk_cell_area_focus (icon_view->priv->cell_area, direction); + } + else + { + item = NULL; + } } else { @@ -3873,7 +4027,7 @@ gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view, while (item) { - gtk_icon_view_set_cell_data (icon_view, item); + _gtk_icon_view_set_cell_data (icon_view, item); if (gtk_cell_area_focus (icon_view->priv->cell_area, direction)) break; @@ -3899,16 +4053,16 @@ gtk_icon_view_move_cursor_left_right (GtkIconView *icon_view, return; } - if (icon_view->priv->ctrl_pressed || - !icon_view->priv->shift_pressed || + if (icon_view->priv->modify_selection_pressed || + !icon_view->priv->extend_selection_pressed || !icon_view->priv->anchor_item || icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) icon_view->priv->anchor_item = item; cell = gtk_cell_area_get_focus_cell (icon_view->priv->cell_area); - gtk_icon_view_set_cursor_item (icon_view, item, cell); + _gtk_icon_view_set_cursor_item (icon_view, item, cell); - if (!icon_view->priv->ctrl_pressed && + if (!icon_view->priv->modify_selection_pressed && icon_view->priv->selection_mode != GTK_SELECTION_NONE) { dirty = gtk_icon_view_unselect_all_internal (icon_view); @@ -3947,15 +4101,15 @@ gtk_icon_view_move_cursor_start_end (GtkIconView *icon_view, if (!item) return; - if (icon_view->priv->ctrl_pressed || - !icon_view->priv->shift_pressed || + if (icon_view->priv->modify_selection_pressed || + !icon_view->priv->extend_selection_pressed || !icon_view->priv->anchor_item || icon_view->priv->selection_mode != GTK_SELECTION_MULTIPLE) icon_view->priv->anchor_item = item; - gtk_icon_view_set_cursor_item (icon_view, item, NULL); + _gtk_icon_view_set_cursor_item (icon_view, item, NULL); - if (!icon_view->priv->ctrl_pressed && + if (!icon_view->priv->modify_selection_pressed && icon_view->priv->selection_mode != GTK_SELECTION_NONE) { dirty = gtk_icon_view_unselect_all_internal (icon_view); @@ -4069,76 +4223,107 @@ gtk_icon_view_scroll_to_path (GtkIconView *icon_view, } -static void -gtk_icon_view_scroll_to_item (GtkIconView *icon_view, - GtkIconViewItem *item) +static void +gtk_icon_view_scroll_to_item (GtkIconView *icon_view, + GtkIconViewItem *item) { - GtkAllocation allocation; + GtkIconViewPrivate *priv = icon_view->priv; GtkWidget *widget = GTK_WIDGET (icon_view); - gint x, y, width, height; - GdkRectangle item_area = - { - item->cell_area.x - icon_view->priv->item_padding, - item->cell_area.y - icon_view->priv->item_padding, - item->cell_area.width + icon_view->priv->item_padding * 2, - item->cell_area.height + icon_view->priv->item_padding * 2 - }; - - width = gdk_window_get_width (icon_view->priv->bin_window); - height = gdk_window_get_height (icon_view->priv->bin_window); - gdk_window_get_position (icon_view->priv->bin_window, &x, &y); + GtkAdjustment *hadj, *vadj; + GtkAllocation allocation; + gint x, y; + GdkRectangle item_area; + item_area.x = item->cell_area.x - priv->item_padding; + item_area.y = item->cell_area.y - priv->item_padding; + item_area.width = item->cell_area.width + priv->item_padding * 2; + item_area.height = item->cell_area.height + priv->item_padding * 2; + + gdk_window_get_position (icon_view->priv->bin_window, &x, &y); gtk_widget_get_allocation (widget, &allocation); + hadj = icon_view->priv->hadjustment; + vadj = icon_view->priv->vadjustment; + if (y + item_area.y < 0) - gtk_adjustment_set_value (icon_view->priv->vadjustment, - gtk_adjustment_get_value (icon_view->priv->vadjustment) + y + item_area.y); + gtk_adjustment_set_value (vadj, + gtk_adjustment_get_value (vadj) + + y + item_area.y); else if (y + item_area.y + item_area.height > allocation.height) - gtk_adjustment_set_value (icon_view->priv->vadjustment, - gtk_adjustment_get_value (icon_view->priv->vadjustment) + y + item_area.y + - item_area.height - allocation.height); + gtk_adjustment_set_value (vadj, + gtk_adjustment_get_value (vadj) + + y + item_area.y + item_area.height - allocation.height); if (x + item_area.x < 0) - gtk_adjustment_set_value (icon_view->priv->hadjustment, - gtk_adjustment_get_value (icon_view->priv->hadjustment) + x + item_area.x); + gtk_adjustment_set_value (hadj, + gtk_adjustment_get_value (hadj) + + x + item_area.x); else if (x + item_area.x + item_area.width > allocation.width) - gtk_adjustment_set_value (icon_view->priv->hadjustment, - gtk_adjustment_get_value (icon_view->priv->hadjustment) + x + item_area.x + - item_area.width - allocation.width); + gtk_adjustment_set_value (hadj, + gtk_adjustment_get_value (hadj) + + x + item_area.x + item_area.width - allocation.width); - gtk_adjustment_changed (icon_view->priv->hadjustment); - gtk_adjustment_changed (icon_view->priv->vadjustment); + gtk_adjustment_changed (hadj); + gtk_adjustment_changed (vadj); } /* GtkCellLayout implementation */ -static GtkCellArea * -gtk_icon_view_cell_layout_get_area (GtkCellLayout *cell_layout) -{ - GtkIconView *icon_view = GTK_ICON_VIEW (cell_layout); - - return icon_view->priv->cell_area; -} static void -gtk_icon_view_set_cell_data (GtkIconView *icon_view, - GtkIconViewItem *item) +gtk_icon_view_ensure_cell_area (GtkIconView *icon_view, + GtkCellArea *cell_area) { - gboolean iters_persist; - GtkTreeIter iter; - - 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; + GtkIconViewPrivate *priv = icon_view->priv; + + if (priv->cell_area) + return; + + if (cell_area) + priv->cell_area = cell_area; + else + priv->cell_area = gtk_cell_area_box_new (); + + g_object_ref_sink (priv->cell_area); + + if (GTK_IS_ORIENTABLE (priv->cell_area)) + gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->cell_area), priv->item_orientation); + + priv->cell_area_context = gtk_cell_area_create_context (priv->cell_area); + + priv->add_editable_id = + g_signal_connect (priv->cell_area, "add-editable", + G_CALLBACK (gtk_icon_view_add_editable), icon_view); + priv->remove_editable_id = + g_signal_connect (priv->cell_area, "remove-editable", + G_CALLBACK (gtk_icon_view_remove_editable), icon_view); + + update_text_cell (icon_view); + update_pixbuf_cell (icon_view); +} + +static GtkCellArea * +gtk_icon_view_cell_layout_get_area (GtkCellLayout *cell_layout) +{ + GtkIconView *icon_view = GTK_ICON_VIEW (cell_layout); + GtkIconViewPrivate *priv = icon_view->priv; + + if (G_UNLIKELY (!priv->cell_area)) + gtk_icon_view_ensure_cell_area (icon_view, NULL); + + return icon_view->priv->cell_area; +} + +void +_gtk_icon_view_set_cell_data (GtkIconView *icon_view, + GtkIconViewItem *item) +{ + GtkTreeIter iter; + 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); gtk_cell_area_apply_attributes (icon_view->priv->cell_area, icon_view->priv->model, @@ -4260,7 +4445,7 @@ gtk_icon_view_get_path_at_pos (GtkIconView *icon_view, g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL); - item = gtk_icon_view_get_item_at_coords (icon_view, x, y, TRUE, NULL); + item = _gtk_icon_view_get_item_at_coords (icon_view, x, y, TRUE, NULL); if (!item) return NULL; @@ -4302,7 +4487,7 @@ gtk_icon_view_get_item_at_pos (GtkIconView *icon_view, g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); - item = gtk_icon_view_get_item_at_coords (icon_view, x, y, TRUE, &renderer); + item = _gtk_icon_view_get_item_at_coords (icon_view, x, y, TRUE, &renderer); if (path != NULL) { @@ -4318,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 @@ -4362,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); } @@ -4411,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 * @@ -4525,7 +4743,7 @@ gtk_icon_view_set_tooltip_query_cb (GtkWidget *widget, * for you. @column should be set to the column in @icon_view's model * containing the tooltip texts, or -1 to disable this feature. * - * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and + * When enabled, #GtkWidget:has-tooltip will be set to %TRUE and * @icon_view will connect a #GtkWidget::query-tooltip signal handler. * * Note that the signal handler sets the text with gtk_tooltip_set_markup(), @@ -4620,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)) && @@ -4736,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)); @@ -4752,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; @@ -4799,12 +5021,12 @@ gtk_icon_view_set_model (GtkIconView *icon_view, g_object_unref (icon_view->priv->model); - g_list_foreach (icon_view->priv->items, (GFunc)gtk_icon_view_item_free, NULL); - g_list_free (icon_view->priv->items); + g_list_free_full (icon_view->priv->items, (GDestroyNotify) gtk_icon_view_item_free); icon_view->priv->items = NULL; icon_view->priv->anchor_item = NULL; icon_view->priv->cursor_item = NULL; icon_view->priv->last_single_clicked = NULL; + icon_view->priv->last_prelight = NULL; icon_view->priv->width = 0; icon_view->priv->height = 0; } @@ -4832,14 +5054,14 @@ gtk_icon_view_set_model (GtkIconView *icon_view, icon_view); gtk_icon_view_build_items (icon_view); - - gtk_icon_view_queue_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)); } /** @@ -4865,6 +5087,9 @@ gtk_icon_view_get_model (GtkIconView *icon_view) static void update_text_cell (GtkIconView *icon_view) { + if (!icon_view->priv->cell_area) + return; + if (icon_view->priv->text_column == -1 && icon_view->priv->markup_column == -1) { @@ -4915,6 +5140,9 @@ update_text_cell (GtkIconView *icon_view) static void update_pixbuf_cell (GtkIconView *icon_view) { + if (!icon_view->priv->cell_area) + return; + if (icon_view->priv->pixbuf_column == -1) { if (icon_view->priv->pixbuf_cell != NULL) @@ -4985,8 +5213,8 @@ gtk_icon_view_set_text_column (GtkIconView *icon_view, icon_view->priv->text_column = column; } - - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); update_text_cell (icon_view); @@ -5048,7 +5276,8 @@ gtk_icon_view_set_markup_column (GtkIconView *icon_view, icon_view->priv->markup_column = column; } - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); update_text_cell (icon_view); @@ -5108,7 +5337,8 @@ gtk_icon_view_set_pixbuf_column (GtkIconView *icon_view, icon_view->priv->pixbuf_column = column; } - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); update_pixbuf_cell (icon_view); @@ -5160,7 +5390,7 @@ gtk_icon_view_select_path (GtkIconView *icon_view, gtk_tree_path_get_indices(path)[0]); if (item) - gtk_icon_view_select_item (icon_view, item); + _gtk_icon_view_select_item (icon_view, item); } /** @@ -5188,7 +5418,7 @@ gtk_icon_view_unselect_path (GtkIconView *icon_view, if (!item) return; - gtk_icon_view_unselect_item (icon_view, item); + _gtk_icon_view_unselect_item (icon_view, item); } /** @@ -5202,8 +5432,7 @@ gtk_icon_view_unselect_path (GtkIconView *icon_view, * * To free the return value, use: * |[ - * g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL); - * g_list_free (list); + * 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. @@ -5425,11 +5654,15 @@ gtk_icon_view_set_item_orientation (GtkIconView *icon_view, { icon_view->priv->item_orientation = orientation; - if (GTK_IS_ORIENTABLE (icon_view->priv->cell_area)) - gtk_orientable_set_orientation (GTK_ORIENTABLE (icon_view->priv->cell_area), - icon_view->priv->item_orientation); + if (icon_view->priv->cell_area) + { + if (GTK_IS_ORIENTABLE (icon_view->priv->cell_area)) + gtk_orientable_set_orientation (GTK_ORIENTABLE (icon_view->priv->cell_area), + icon_view->priv->item_orientation); + + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + } - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); gtk_icon_view_invalidate_sizes (icon_view); update_text_cell (icon_view); @@ -5481,8 +5714,10 @@ gtk_icon_view_set_columns (GtkIconView *icon_view, { icon_view->priv->columns = columns; - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); - gtk_icon_view_queue_layout (icon_view); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + + gtk_widget_queue_resize (GTK_WIDGET (icon_view)); g_object_notify (G_OBJECT (icon_view), "columns"); } @@ -5527,7 +5762,9 @@ gtk_icon_view_set_item_width (GtkIconView *icon_view, { icon_view->priv->item_width = item_width; - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + gtk_icon_view_invalidate_sizes (icon_view); update_text_cell (icon_view); @@ -5576,9 +5813,11 @@ gtk_icon_view_set_spacing (GtkIconView *icon_view, { icon_view->priv->spacing = spacing; - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + gtk_icon_view_invalidate_sizes (icon_view); - + g_object_notify (G_OBJECT (icon_view), "spacing"); } } @@ -5621,9 +5860,11 @@ gtk_icon_view_set_row_spacing (GtkIconView *icon_view, { icon_view->priv->row_spacing = row_spacing; - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + gtk_icon_view_invalidate_sizes (icon_view); - + g_object_notify (G_OBJECT (icon_view), "row-spacing"); } } @@ -5666,9 +5907,11 @@ gtk_icon_view_set_column_spacing (GtkIconView *icon_view, { icon_view->priv->column_spacing = column_spacing; - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + gtk_icon_view_invalidate_sizes (icon_view); - + g_object_notify (G_OBJECT (icon_view), "column-spacing"); } } @@ -5712,9 +5955,11 @@ gtk_icon_view_set_margin (GtkIconView *icon_view, { icon_view->priv->margin = margin; - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + gtk_icon_view_invalidate_sizes (icon_view); - + g_object_notify (G_OBJECT (icon_view), "margin"); } } @@ -5757,9 +6002,11 @@ gtk_icon_view_set_item_padding (GtkIconView *icon_view, { icon_view->priv->item_padding = item_padding; - gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + if (icon_view->priv->cell_area) + gtk_cell_area_stop_editing (icon_view->priv->cell_area, TRUE); + gtk_icon_view_invalidate_sizes (icon_view); - + g_object_notify (G_OBJECT (icon_view), "item-padding"); } } @@ -5947,25 +6194,26 @@ remove_scroll_timeout (GtkIconView *icon_view) } static void -gtk_icon_view_autoscroll (GtkIconView *icon_view) +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_pointer (window, &px, &py, NULL); - gdk_window_get_geometry (window, &x, &y, &width, &height); + gdk_window_get_device_position (window, device, &px, &py, NULL); + 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, @@ -5976,17 +6224,26 @@ gtk_icon_view_autoscroll (GtkIconView *icon_view) gtk_adjustment_get_value (icon_view->priv->hadjustment) + hoffset); } +typedef struct { + GtkIconView *icon_view; + GdkDevice *device; +} DragScrollData; static gboolean -drag_scroll_timeout (gpointer data) +drag_scroll_timeout (gpointer datap) { - GtkIconView *icon_view = GTK_ICON_VIEW (data); + DragScrollData *data = datap; - gtk_icon_view_autoscroll (icon_view); + gtk_icon_view_autoscroll (data->icon_view, data->device); return TRUE; } +static void +drag_scroll_data_free (DragScrollData *data) +{ + g_slice_free (DragScrollData, data); +} static gboolean set_destination (GtkIconView *icon_view, @@ -6223,7 +6480,7 @@ gtk_icon_view_drag_begin (GtkWidget *widget, if (!icon_view->priv->dest_set && !icon_view->priv->source_set) return; - item = gtk_icon_view_get_item_at_coords (icon_view, + item = _gtk_icon_view_get_item_at_coords (icon_view, icon_view->priv->press_start_x, icon_view->priv->press_start_y, TRUE, @@ -6379,8 +6636,12 @@ gtk_icon_view_drag_motion (GtkWidget *widget, { if (icon_view->priv->scroll_timeout_id == 0) { + DragScrollData *data = g_slice_new (DragScrollData); + data->icon_view = icon_view; + data->device = gdk_drag_context_get_device (context); + icon_view->priv->scroll_timeout_id = - gdk_threads_add_timeout (50, drag_scroll_timeout, icon_view); + gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT, 50, drag_scroll_timeout, data, (GDestroyNotify) drag_scroll_data_free); } if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW")) @@ -6548,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 @@ -6784,7 +7045,7 @@ gtk_icon_view_get_dest_item_at_pos (GtkIconView *icon_view, if (path) *path = NULL; - item = gtk_icon_view_get_item_at_coords (icon_view, + item = _gtk_icon_view_get_item_at_coords (icon_view, drag_x + gtk_adjustment_get_value (icon_view->priv->hadjustment), drag_y + gtk_adjustment_get_value (icon_view->priv->vadjustment), FALSE, NULL); @@ -6829,6 +7090,7 @@ gtk_icon_view_create_drag_icon (GtkIconView *icon_view, GtkTreePath *path) { GtkWidget *widget; + GtkStyleContext *context; cairo_t *cr; cairo_surface_t *surface; GList *l; @@ -6838,6 +7100,7 @@ gtk_icon_view_create_drag_icon (GtkIconView *icon_view, g_return_val_if_fail (path != NULL, NULL); widget = GTK_WIDGET (icon_view); + context = gtk_widget_get_style_context (widget); if (!gtk_widget_get_realized (widget)) return NULL; @@ -6865,9 +7128,8 @@ gtk_icon_view_create_drag_icon (GtkIconView *icon_view, cr = cairo_create (surface); cairo_set_line_width (cr, 1.); - gdk_cairo_set_source_color (cr, >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]); - cairo_rectangle (cr, 0, 0, rect.width + 2, rect.height + 2); - cairo_fill (cr); + gtk_render_background (context, cr, 0, 0, + rect.width + 2, rect.height + 2); cairo_save (cr); @@ -6971,2381 +7233,74 @@ gtk_icon_view_set_reorderable (GtkIconView *icon_view, g_object_notify (G_OBJECT (icon_view), "reorderable"); } - -/* Accessibility Support */ - -static gpointer accessible_parent_class; -static gpointer accessible_item_parent_class; -static GQuark accessible_private_data_quark = 0; - -#define GTK_TYPE_ICON_VIEW_ITEM_ACCESSIBLE (gtk_icon_view_item_accessible_get_type ()) -#define GTK_ICON_VIEW_ITEM_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_ICON_VIEW_ITEM_ACCESSIBLE, GtkIconViewItemAccessible)) -#define GTK_IS_ICON_VIEW_ITEM_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_ICON_VIEW_ITEM_ACCESSIBLE)) - -static GType gtk_icon_view_item_accessible_get_type (void); - -enum { - ACTION_ACTIVATE, - LAST_ACTION -}; - -typedef struct -{ - AtkObject parent; - - GtkIconViewItem *item; - - GtkWidget *widget; - - AtkStateSet *state_set; - - gchar *text; - - GtkTextBuffer *text_buffer; - - gchar *action_descriptions[LAST_ACTION]; - gchar *image_description; - guint action_idle_handler; -} GtkIconViewItemAccessible; - -static const gchar *const gtk_icon_view_item_accessible_action_names[] = -{ - "activate", - NULL -}; - -static const gchar *const gtk_icon_view_item_accessible_action_descriptions[] = -{ - "Activate item", - NULL -}; -typedef struct _GtkIconViewItemAccessibleClass -{ - AtkObjectClass parent_class; - -} GtkIconViewItemAccessibleClass; - -static gboolean gtk_icon_view_item_accessible_is_showing (GtkIconViewItemAccessible *item); - -static gboolean -gtk_icon_view_item_accessible_idle_do_action (gpointer data) -{ - GtkIconViewItemAccessible *item; - GtkIconView *icon_view; - GtkTreePath *path; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (data); - item->action_idle_handler = 0; - - if (item->widget != NULL) - { - icon_view = GTK_ICON_VIEW (item->widget); - path = gtk_tree_path_new_from_indices (item->item->index, -1); - gtk_icon_view_item_activated (icon_view, path); - gtk_tree_path_free (path); - } - - return FALSE; -} - -static gboolean -gtk_icon_view_item_accessible_action_do_action (AtkAction *action, - gint i) -{ - GtkIconViewItemAccessible *item; - - if (i < 0 || i >= LAST_ACTION) - return FALSE; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (action); - - if (!GTK_IS_ICON_VIEW (item->widget)) - return FALSE; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return FALSE; - - switch (i) - { - case ACTION_ACTIVATE: - if (!item->action_idle_handler) - item->action_idle_handler = gdk_threads_add_idle (gtk_icon_view_item_accessible_idle_do_action, item); - break; - default: - g_assert_not_reached (); - return FALSE; - - } - return TRUE; -} - -static gint -gtk_icon_view_item_accessible_action_get_n_actions (AtkAction *action) -{ - return LAST_ACTION; -} - -static const gchar * -gtk_icon_view_item_accessible_action_get_description (AtkAction *action, - gint i) +/** + * 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) { - GtkIconViewItemAccessible *item; - - if (i < 0 || i >= LAST_ACTION) - return NULL; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (action); + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); - if (item->action_descriptions[i]) - return item->action_descriptions[i]; - else - return gtk_icon_view_item_accessible_action_descriptions[i]; -} + single = single != FALSE; -static const gchar * -gtk_icon_view_item_accessible_action_get_name (AtkAction *action, - gint i) -{ - if (i < 0 || i >= LAST_ACTION) - return NULL; + if (icon_view->priv->activate_on_single_click == single) + return; - return gtk_icon_view_item_accessible_action_names[i]; + icon_view->priv->activate_on_single_click = single; + g_object_notify (G_OBJECT (icon_view), "activate-on-single-click"); } -static gboolean -gtk_icon_view_item_accessible_action_set_description (AtkAction *action, - gint i, - const gchar *description) +/** + * 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) { - GtkIconViewItemAccessible *item; - - if (i < 0 || i >= LAST_ACTION) - return FALSE; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (action); - - g_free (item->action_descriptions[i]); - - item->action_descriptions[i] = g_strdup (description); - - return TRUE; -} - -static void -atk_action_item_interface_init (AtkActionIface *iface) -{ - iface->do_action = gtk_icon_view_item_accessible_action_do_action; - iface->get_n_actions = gtk_icon_view_item_accessible_action_get_n_actions; - iface->get_description = gtk_icon_view_item_accessible_action_get_description; - iface->get_name = gtk_icon_view_item_accessible_action_get_name; - iface->set_description = gtk_icon_view_item_accessible_action_set_description; -} - -static const gchar * -gtk_icon_view_item_accessible_image_get_image_description (AtkImage *image) -{ - GtkIconViewItemAccessible *item; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (image); - - return item->image_description; -} - -static gboolean -gtk_icon_view_item_accessible_image_set_image_description (AtkImage *image, - const gchar *description) -{ - GtkIconViewItemAccessible *item; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (image); - - g_free (item->image_description); - item->image_description = g_strdup (description); - - return TRUE; -} - -typedef struct { - GdkRectangle box; - gboolean pixbuf_found; -} GetPixbufBoxData; - -static gboolean -get_pixbuf_foreach (GtkCellRenderer *renderer, - const GdkRectangle *cell_area, - const GdkRectangle *cell_background, - GetPixbufBoxData *data) -{ - if (GTK_IS_CELL_RENDERER_PIXBUF (renderer)) - { - data->box = *cell_area; - data->pixbuf_found = TRUE; - } - return (data->pixbuf_found != FALSE); -} - -static gboolean -get_pixbuf_box (GtkIconView *icon_view, - GtkIconViewItem *item, - GdkRectangle *box) -{ - GetPixbufBoxData data = { { 0, }, FALSE }; - 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_foreach_alloc (icon_view->priv->cell_area, context, - GTK_WIDGET (icon_view), - (GdkRectangle *)item, (GdkRectangle *)item, - (GtkCellAllocCallback)get_pixbuf_foreach, &data); - - return data.pixbuf_found; -} - -static gboolean -get_text_foreach (GtkCellRenderer *renderer, - gchar **text) -{ - if (GTK_IS_CELL_RENDERER_TEXT (renderer)) - { - g_object_get (renderer, "text", text, NULL); - - return TRUE; - } - return FALSE; -} - -static gchar * -get_text (GtkIconView *icon_view, - GtkIconViewItem *item) -{ - gchar *text = NULL; - - gtk_icon_view_set_cell_data (icon_view, item); - gtk_cell_area_foreach (icon_view->priv->cell_area, - (GtkCellCallback)get_text_foreach, &text); - - return text; -} - -static void -gtk_icon_view_item_accessible_image_get_image_size (AtkImage *image, - gint *width, - gint *height) -{ - GtkIconViewItemAccessible *item; - GdkRectangle box; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (image); - - if (!GTK_IS_ICON_VIEW (item->widget)) - return; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return; - - if (get_pixbuf_box (GTK_ICON_VIEW (item->widget), item->item, &box)) - { - *width = box.width; - *height = box.height; - } -} - -static void -gtk_icon_view_item_accessible_image_get_image_position (AtkImage *image, - gint *x, - gint *y, - AtkCoordType coord_type) -{ - GtkIconViewItemAccessible *item; - GdkRectangle box; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (image); - - if (!GTK_IS_ICON_VIEW (item->widget)) - return; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return; - - atk_component_get_position (ATK_COMPONENT (image), x, y, coord_type); - - if (get_pixbuf_box (GTK_ICON_VIEW (item->widget), item->item, &box)) - { - *x+= box.x - item->item->cell_area.x; - *y+= box.y - item->item->cell_area.y; - } - -} - -static void -atk_image_item_interface_init (AtkImageIface *iface) -{ - iface->get_image_description = gtk_icon_view_item_accessible_image_get_image_description; - iface->set_image_description = gtk_icon_view_item_accessible_image_set_image_description; - iface->get_image_size = gtk_icon_view_item_accessible_image_get_image_size; - iface->get_image_position = gtk_icon_view_item_accessible_image_get_image_position; -} - -static gchar * -gtk_icon_view_item_accessible_text_get_text (AtkText *text, - gint start_pos, - gint end_pos) -{ - GtkIconViewItemAccessible *item; - GtkTextIter start, end; - GtkTextBuffer *buffer; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (text); - - if (!GTK_IS_ICON_VIEW (item->widget)) - return NULL; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return NULL; - - buffer = item->text_buffer; - gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos); - if (end_pos < 0) - gtk_text_buffer_get_end_iter (buffer, &end); - else - gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos); - - return gtk_text_buffer_get_text (buffer, &start, &end, FALSE); -} - -static gunichar -gtk_icon_view_item_accessible_text_get_character_at_offset (AtkText *text, - gint offset) -{ - GtkIconViewItemAccessible *item; - GtkTextIter start, end; - GtkTextBuffer *buffer; - gchar *string; - gunichar unichar; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (text); - - if (!GTK_IS_ICON_VIEW (item->widget)) - return '\0'; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return '\0'; - - buffer = item->text_buffer; - if (offset >= gtk_text_buffer_get_char_count (buffer)) - return '\0'; - - gtk_text_buffer_get_iter_at_offset (buffer, &start, offset); - end = start; - gtk_text_iter_forward_char (&end); - string = gtk_text_buffer_get_slice (buffer, &start, &end, FALSE); - unichar = g_utf8_get_char (string); - g_free(string); - - return unichar; -} - -#if 0 -static void -get_pango_text_offsets (PangoLayout *layout, - GtkTextBuffer *buffer, - gint function, - AtkTextBoundary boundary_type, - gint offset, - gint *start_offset, - gint *end_offset, - GtkTextIter *start_iter, - GtkTextIter *end_iter) -{ - PangoLayoutIter *iter; - PangoLayoutLine *line, *prev_line = NULL, *prev_prev_line = NULL; - gint index, start_index, end_index; - const gchar *text; - gboolean found = FALSE; - - text = pango_layout_get_text (layout); - index = g_utf8_offset_to_pointer (text, offset) - text; - iter = pango_layout_get_iter (layout); - do - { - line = pango_layout_iter_get_line_readonly (iter); - start_index = line->start_index; - end_index = start_index + line->length; - - if (index >= start_index && index <= end_index) - { - /* - * Found line for offset - */ - switch (function) - { - case 0: - /* - * We want the previous line - */ - if (prev_line) - { - switch (boundary_type) - { - case ATK_TEXT_BOUNDARY_LINE_START: - end_index = start_index; - start_index = prev_line->start_index; - break; - case ATK_TEXT_BOUNDARY_LINE_END: - if (prev_prev_line) - start_index = prev_prev_line->start_index + - prev_prev_line->length; - end_index = prev_line->start_index + prev_line->length; - break; - default: - g_assert_not_reached(); - } - } - else - start_index = end_index = 0; - break; - case 1: - switch (boundary_type) - { - case ATK_TEXT_BOUNDARY_LINE_START: - if (pango_layout_iter_next_line (iter)) - end_index = pango_layout_iter_get_line_readonly (iter)->start_index; - break; - case ATK_TEXT_BOUNDARY_LINE_END: - if (prev_line) - start_index = prev_line->start_index + - prev_line->length; - break; - default: - g_assert_not_reached(); - } - break; - case 2: - /* - * We want the next line - */ - if (pango_layout_iter_next_line (iter)) - { - line = pango_layout_iter_get_line_readonly (iter); - switch (boundary_type) - { - case ATK_TEXT_BOUNDARY_LINE_START: - start_index = line->start_index; - if (pango_layout_iter_next_line (iter)) - end_index = pango_layout_iter_get_line_readonly (iter)->start_index; - else - end_index = start_index + line->length; - break; - case ATK_TEXT_BOUNDARY_LINE_END: - start_index = end_index; - end_index = line->start_index + line->length; - break; - default: - g_assert_not_reached(); - } - } - else - start_index = end_index; - break; - } - found = TRUE; - break; - } - prev_prev_line = prev_line; - prev_line = line; - } - while (pango_layout_iter_next_line (iter)); - - if (!found) - { - start_index = prev_line->start_index + prev_line->length; - end_index = start_index; - } - pango_layout_iter_free (iter); - *start_offset = g_utf8_pointer_to_offset (text, text + start_index); - *end_offset = g_utf8_pointer_to_offset (text, text + end_index); - - gtk_text_buffer_get_iter_at_offset (buffer, start_iter, *start_offset); - gtk_text_buffer_get_iter_at_offset (buffer, end_iter, *end_offset); -} -#endif - -static gchar* -gtk_icon_view_item_accessible_text_get_text_before_offset (AtkText *text, - gint offset, - AtkTextBoundary boundary_type, - gint *start_offset, - gint *end_offset) -{ - GtkIconViewItemAccessible *item; - GtkTextIter start, end; - GtkTextBuffer *buffer; -#if 0 - GtkIconView *icon_view; -#endif - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (text); - - if (!GTK_IS_ICON_VIEW (item->widget)) - return NULL; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return NULL; - - buffer = item->text_buffer; - - if (!gtk_text_buffer_get_char_count (buffer)) - { - *start_offset = 0; - *end_offset = 0; - return g_strdup (""); - } - gtk_text_buffer_get_iter_at_offset (buffer, &start, offset); - - end = start; - - switch (boundary_type) - { - case ATK_TEXT_BOUNDARY_CHAR: - gtk_text_iter_backward_char(&start); - break; - case ATK_TEXT_BOUNDARY_WORD_START: - if (!gtk_text_iter_starts_word (&start)) - gtk_text_iter_backward_word_start (&start); - end = start; - gtk_text_iter_backward_word_start(&start); - break; - case ATK_TEXT_BOUNDARY_WORD_END: - if (gtk_text_iter_inside_word (&start) && - !gtk_text_iter_starts_word (&start)) - gtk_text_iter_backward_word_start (&start); - while (!gtk_text_iter_ends_word (&start)) - { - if (!gtk_text_iter_backward_char (&start)) - break; - } - end = start; - gtk_text_iter_backward_word_start(&start); - while (!gtk_text_iter_ends_word (&start)) - { - if (!gtk_text_iter_backward_char (&start)) - break; - } - break; - case ATK_TEXT_BOUNDARY_SENTENCE_START: - if (!gtk_text_iter_starts_sentence (&start)) - gtk_text_iter_backward_sentence_start (&start); - end = start; - gtk_text_iter_backward_sentence_start (&start); - break; - case ATK_TEXT_BOUNDARY_SENTENCE_END: - if (gtk_text_iter_inside_sentence (&start) && - !gtk_text_iter_starts_sentence (&start)) - gtk_text_iter_backward_sentence_start (&start); - while (!gtk_text_iter_ends_sentence (&start)) - { - if (!gtk_text_iter_backward_char (&start)) - break; - } - end = start; - gtk_text_iter_backward_sentence_start (&start); - while (!gtk_text_iter_ends_sentence (&start)) - { - if (!gtk_text_iter_backward_char (&start)) - break; - } - break; - case ATK_TEXT_BOUNDARY_LINE_START: - case ATK_TEXT_BOUNDARY_LINE_END: -#if 0 - icon_view = GTK_ICON_VIEW (item->widget); - /* FIXME we probably have to use GailTextCell to salvage this */ - gtk_icon_view_update_item_text (icon_view, item->item); - get_pango_text_offsets (icon_view->priv->layout, - buffer, - 0, - boundary_type, - offset, - start_offset, - end_offset, - &start, - &end); -#endif - break; - } - - *start_offset = gtk_text_iter_get_offset (&start); - *end_offset = gtk_text_iter_get_offset (&end); - - return gtk_text_buffer_get_text (buffer, &start, &end, FALSE); -} - -static gchar* -gtk_icon_view_item_accessible_text_get_text_at_offset (AtkText *text, - gint offset, - AtkTextBoundary boundary_type, - gint *start_offset, - gint *end_offset) -{ - GtkIconViewItemAccessible *item; - GtkTextIter start, end; - GtkTextBuffer *buffer; -#if 0 - GtkIconView *icon_view; -#endif - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (text); - - if (!GTK_IS_ICON_VIEW (item->widget)) - return NULL; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return NULL; - - buffer = item->text_buffer; - - if (!gtk_text_buffer_get_char_count (buffer)) - { - *start_offset = 0; - *end_offset = 0; - return g_strdup (""); - } - gtk_text_buffer_get_iter_at_offset (buffer, &start, offset); - - end = start; - - switch (boundary_type) - { - case ATK_TEXT_BOUNDARY_CHAR: - gtk_text_iter_forward_char (&end); - break; - case ATK_TEXT_BOUNDARY_WORD_START: - if (!gtk_text_iter_starts_word (&start)) - gtk_text_iter_backward_word_start (&start); - if (gtk_text_iter_inside_word (&end)) - gtk_text_iter_forward_word_end (&end); - while (!gtk_text_iter_starts_word (&end)) - { - if (!gtk_text_iter_forward_char (&end)) - break; - } - break; - case ATK_TEXT_BOUNDARY_WORD_END: - if (gtk_text_iter_inside_word (&start) && - !gtk_text_iter_starts_word (&start)) - gtk_text_iter_backward_word_start (&start); - while (!gtk_text_iter_ends_word (&start)) - { - if (!gtk_text_iter_backward_char (&start)) - break; - } - gtk_text_iter_forward_word_end (&end); - break; - case ATK_TEXT_BOUNDARY_SENTENCE_START: - if (!gtk_text_iter_starts_sentence (&start)) - gtk_text_iter_backward_sentence_start (&start); - if (gtk_text_iter_inside_sentence (&end)) - gtk_text_iter_forward_sentence_end (&end); - while (!gtk_text_iter_starts_sentence (&end)) - { - if (!gtk_text_iter_forward_char (&end)) - break; - } - break; - case ATK_TEXT_BOUNDARY_SENTENCE_END: - if (gtk_text_iter_inside_sentence (&start) && - !gtk_text_iter_starts_sentence (&start)) - gtk_text_iter_backward_sentence_start (&start); - while (!gtk_text_iter_ends_sentence (&start)) - { - if (!gtk_text_iter_backward_char (&start)) - break; - } - gtk_text_iter_forward_sentence_end (&end); - break; - case ATK_TEXT_BOUNDARY_LINE_START: - case ATK_TEXT_BOUNDARY_LINE_END: -#if 0 - icon_view = GTK_ICON_VIEW (item->widget); - /* FIXME we probably have to use GailTextCell to salvage this */ - gtk_icon_view_update_item_text (icon_view, item->item); - get_pango_text_offsets (icon_view->priv->layout, - buffer, - 1, - boundary_type, - offset, - start_offset, - end_offset, - &start, - &end); -#endif - break; - } - - - *start_offset = gtk_text_iter_get_offset (&start); - *end_offset = gtk_text_iter_get_offset (&end); - - return gtk_text_buffer_get_text (buffer, &start, &end, FALSE); -} - -static gchar* -gtk_icon_view_item_accessible_text_get_text_after_offset (AtkText *text, - gint offset, - AtkTextBoundary boundary_type, - gint *start_offset, - gint *end_offset) -{ - GtkIconViewItemAccessible *item; - GtkTextIter start, end; - GtkTextBuffer *buffer; -#if 0 - GtkIconView *icon_view; -#endif - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (text); - - if (!GTK_IS_ICON_VIEW (item->widget)) - return NULL; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return NULL; - - buffer = item->text_buffer; - - if (!gtk_text_buffer_get_char_count (buffer)) - { - *start_offset = 0; - *end_offset = 0; - return g_strdup (""); - } - gtk_text_buffer_get_iter_at_offset (buffer, &start, offset); - - end = start; - - switch (boundary_type) - { - case ATK_TEXT_BOUNDARY_CHAR: - gtk_text_iter_forward_char(&start); - gtk_text_iter_forward_chars(&end, 2); - break; - case ATK_TEXT_BOUNDARY_WORD_START: - if (gtk_text_iter_inside_word (&end)) - gtk_text_iter_forward_word_end (&end); - while (!gtk_text_iter_starts_word (&end)) - { - if (!gtk_text_iter_forward_char (&end)) - break; - } - start = end; - if (!gtk_text_iter_is_end (&end)) - { - gtk_text_iter_forward_word_end (&end); - while (!gtk_text_iter_starts_word (&end)) - { - if (!gtk_text_iter_forward_char (&end)) - break; - } - } - break; - case ATK_TEXT_BOUNDARY_WORD_END: - gtk_text_iter_forward_word_end (&end); - start = end; - if (!gtk_text_iter_is_end (&end)) - gtk_text_iter_forward_word_end (&end); - break; - case ATK_TEXT_BOUNDARY_SENTENCE_START: - if (gtk_text_iter_inside_sentence (&end)) - gtk_text_iter_forward_sentence_end (&end); - while (!gtk_text_iter_starts_sentence (&end)) - { - if (!gtk_text_iter_forward_char (&end)) - break; - } - start = end; - if (!gtk_text_iter_is_end (&end)) - { - gtk_text_iter_forward_sentence_end (&end); - while (!gtk_text_iter_starts_sentence (&end)) - { - if (!gtk_text_iter_forward_char (&end)) - break; - } - } - break; - case ATK_TEXT_BOUNDARY_SENTENCE_END: - gtk_text_iter_forward_sentence_end (&end); - start = end; - if (!gtk_text_iter_is_end (&end)) - gtk_text_iter_forward_sentence_end (&end); - break; - case ATK_TEXT_BOUNDARY_LINE_START: - case ATK_TEXT_BOUNDARY_LINE_END: -#if 0 - icon_view = GTK_ICON_VIEW (item->widget); - /* FIXME we probably have to use GailTextCell to salvage this */ - gtk_icon_view_update_item_text (icon_view, item->item); - get_pango_text_offsets (icon_view->priv->layout, - buffer, - 2, - boundary_type, - offset, - start_offset, - end_offset, - &start, - &end); -#endif - break; - } - *start_offset = gtk_text_iter_get_offset (&start); - *end_offset = gtk_text_iter_get_offset (&end); - - return gtk_text_buffer_get_text (buffer, &start, &end, FALSE); -} - -static gint -gtk_icon_view_item_accessible_text_get_character_count (AtkText *text) -{ - GtkIconViewItemAccessible *item; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (text); - - if (!GTK_IS_ICON_VIEW (item->widget)) - return 0; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return 0; - - return gtk_text_buffer_get_char_count (item->text_buffer); -} - -static void -gtk_icon_view_item_accessible_text_get_character_extents (AtkText *text, - gint offset, - gint *x, - gint *y, - gint *width, - gint *height, - AtkCoordType coord_type) -{ - GtkIconViewItemAccessible *item; -#if 0 - GtkIconView *icon_view; - PangoRectangle char_rect; - const gchar *item_text; - gint index; -#endif - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (text); - - if (!GTK_IS_ICON_VIEW (item->widget)) - return; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return; - -#if 0 - icon_view = GTK_ICON_VIEW (item->widget); - /* FIXME we probably have to use GailTextCell to salvage this */ - gtk_icon_view_update_item_text (icon_view, item->item); - item_text = pango_layout_get_text (icon_view->priv->layout); - index = g_utf8_offset_to_pointer (item_text, offset) - item_text; - pango_layout_index_to_pos (icon_view->priv->layout, index, &char_rect); - - atk_component_get_position (ATK_COMPONENT (text), x, y, coord_type); - *x += item->item->layout_x - item->item->x + char_rect.x / PANGO_SCALE; - /* Look at gtk_icon_view_paint_item() to see where the text is. */ - *x -= ((item->item->width - item->item->layout_width) / 2) + (MAX (item->item->pixbuf_width, icon_view->priv->item_width) - item->item->width) / 2, - *y += item->item->layout_y - item->item->y + char_rect.y / PANGO_SCALE; - *width = char_rect.width / PANGO_SCALE; - *height = char_rect.height / PANGO_SCALE; -#endif -} - -static gint -gtk_icon_view_item_accessible_text_get_offset_at_point (AtkText *text, - gint x, - gint y, - AtkCoordType coord_type) -{ - GtkIconViewItemAccessible *item; - gint offset = 0; -#if 0 - GtkIconView *icon_view; - const gchar *item_text; - gint index; - gint l_x, l_y; -#endif - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (text); - - if (!GTK_IS_ICON_VIEW (item->widget)) - return -1; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return -1; - -#if 0 - icon_view = GTK_ICON_VIEW (item->widget); - /* FIXME we probably have to use GailTextCell to salvage this */ - gtk_icon_view_update_item_text (icon_view, item->item); - atk_component_get_position (ATK_COMPONENT (text), &l_x, &l_y, coord_type); - x -= l_x + item->item->layout_x - item->item->x; - x += ((item->item->width - item->item->layout_width) / 2) + (MAX (item->item->pixbuf_width, icon_view->priv->item_width) - item->item->width) / 2, - y -= l_y + item->item->layout_y - item->item->y; - item_text = pango_layout_get_text (icon_view->priv->layout); - if (!pango_layout_xy_to_index (icon_view->priv->layout, - x * PANGO_SCALE, - y * PANGO_SCALE, - &index, NULL)) - { - if (x < 0 || y < 0) - index = 0; - else - index = -1; - } - if (index == -1) - offset = g_utf8_strlen (item_text, -1); - else - offset = g_utf8_pointer_to_offset (item_text, item_text + index); -#endif - return offset; -} - -static void -atk_text_item_interface_init (AtkTextIface *iface) -{ - iface->get_text = gtk_icon_view_item_accessible_text_get_text; - iface->get_character_at_offset = gtk_icon_view_item_accessible_text_get_character_at_offset; - iface->get_text_before_offset = gtk_icon_view_item_accessible_text_get_text_before_offset; - iface->get_text_at_offset = gtk_icon_view_item_accessible_text_get_text_at_offset; - iface->get_text_after_offset = gtk_icon_view_item_accessible_text_get_text_after_offset; - iface->get_character_count = gtk_icon_view_item_accessible_text_get_character_count; - iface->get_character_extents = gtk_icon_view_item_accessible_text_get_character_extents; - iface->get_offset_at_point = gtk_icon_view_item_accessible_text_get_offset_at_point; -} - -static void -gtk_icon_view_item_accessible_get_extents (AtkComponent *component, - gint *x, - gint *y, - gint *width, - gint *height, - AtkCoordType coord_type) -{ - GtkIconViewItemAccessible *item; - AtkObject *parent_obj; - gint l_x, l_y; - - g_return_if_fail (GTK_IS_ICON_VIEW_ITEM_ACCESSIBLE (component)); - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (component); - if (!GTK_IS_WIDGET (item->widget)) - return; - - if (atk_state_set_contains_state (item->state_set, ATK_STATE_DEFUNCT)) - return; - - *width = item->item->cell_area.width; - *height = item->item->cell_area.height; - if (gtk_icon_view_item_accessible_is_showing (item)) - { - parent_obj = gtk_widget_get_accessible (item->widget); - atk_component_get_position (ATK_COMPONENT (parent_obj), &l_x, &l_y, coord_type); - *x = l_x + item->item->cell_area.x; - *y = l_y + item->item->cell_area.y; - } - else - { - *x = G_MININT; - *y = G_MININT; - } -} - -static gboolean -gtk_icon_view_item_accessible_grab_focus (AtkComponent *component) -{ - GtkIconViewItemAccessible *item; - GtkWidget *toplevel; - - g_return_val_if_fail (GTK_IS_ICON_VIEW_ITEM_ACCESSIBLE (component), FALSE); - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (component); - if (!GTK_IS_WIDGET (item->widget)) - return FALSE; - - gtk_widget_grab_focus (item->widget); - gtk_icon_view_set_cursor_item (GTK_ICON_VIEW (item->widget), item->item, NULL); - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->widget)); - if (gtk_widget_is_toplevel (toplevel)) - gtk_window_present (GTK_WINDOW (toplevel)); - - return TRUE; -} - -static void -atk_component_item_interface_init (AtkComponentIface *iface) -{ - iface->get_extents = gtk_icon_view_item_accessible_get_extents; - iface->grab_focus = gtk_icon_view_item_accessible_grab_focus; -} - -static gboolean -gtk_icon_view_item_accessible_add_state (GtkIconViewItemAccessible *item, - AtkStateType state_type, - gboolean emit_signal) -{ - gboolean rc; - - rc = atk_state_set_add_state (item->state_set, state_type); - /* - * The signal should only be generated if the value changed, - * not when the item is set up. So states that are set - * initially should pass FALSE as the emit_signal argument. - */ - - if (emit_signal) - { - atk_object_notify_state_change (ATK_OBJECT (item), state_type, TRUE); - /* If state_type is ATK_STATE_VISIBLE, additional notification */ - if (state_type == ATK_STATE_VISIBLE) - g_signal_emit_by_name (item, "visible-data-changed"); - } - - return rc; -} - -static gboolean -gtk_icon_view_item_accessible_remove_state (GtkIconViewItemAccessible *item, - AtkStateType state_type, - gboolean emit_signal) -{ - if (atk_state_set_contains_state (item->state_set, state_type)) - { - gboolean rc; - - rc = atk_state_set_remove_state (item->state_set, state_type); - /* - * The signal should only be generated if the value changed, - * not when the item is set up. So states that are set - * initially should pass FALSE as the emit_signal argument. - */ - - if (emit_signal) - { - atk_object_notify_state_change (ATK_OBJECT (item), state_type, FALSE); - /* If state_type is ATK_STATE_VISIBLE, additional notification */ - if (state_type == ATK_STATE_VISIBLE) - g_signal_emit_by_name (item, "visible-data-changed"); - } - - return rc; - } - else - return FALSE; -} - -static gboolean -gtk_icon_view_item_accessible_is_showing (GtkIconViewItemAccessible *item) -{ - GtkAllocation allocation; - GtkIconView *icon_view; - GdkRectangle visible_rect; - gboolean is_showing; - - /* - * An item is considered "SHOWING" if any part of the item is in the - * visible rectangle. - */ - - if (!GTK_IS_ICON_VIEW (item->widget)) - return FALSE; - - if (item->item == NULL) - return FALSE; - - gtk_widget_get_allocation (item->widget, &allocation); - - icon_view = GTK_ICON_VIEW (item->widget); - visible_rect.x = 0; - if (icon_view->priv->hadjustment) - visible_rect.x += gtk_adjustment_get_value (icon_view->priv->hadjustment); - visible_rect.y = 0; - if (icon_view->priv->hadjustment) - visible_rect.y += gtk_adjustment_get_value (icon_view->priv->vadjustment); - visible_rect.width = allocation.width; - visible_rect.height = allocation.height; - - if (((item->item->cell_area.x + item->item->cell_area.width) < visible_rect.x) || - ((item->item->cell_area.y + item->item->cell_area.height) < (visible_rect.y)) || - (item->item->cell_area.x > (visible_rect.x + visible_rect.width)) || - (item->item->cell_area.y > (visible_rect.y + visible_rect.height))) - is_showing = FALSE; - else - is_showing = TRUE; - - return is_showing; -} - -static gboolean -gtk_icon_view_item_accessible_set_visibility (GtkIconViewItemAccessible *item, - gboolean emit_signal) -{ - if (gtk_icon_view_item_accessible_is_showing (item)) - return gtk_icon_view_item_accessible_add_state (item, ATK_STATE_SHOWING, - emit_signal); - else - return gtk_icon_view_item_accessible_remove_state (item, ATK_STATE_SHOWING, - emit_signal); -} - -static void -gtk_icon_view_item_accessible_object_init (GtkIconViewItemAccessible *item) -{ - gint i; - - item->state_set = atk_state_set_new (); - - atk_state_set_add_state (item->state_set, ATK_STATE_ENABLED); - atk_state_set_add_state (item->state_set, ATK_STATE_FOCUSABLE); - atk_state_set_add_state (item->state_set, ATK_STATE_SENSITIVE); - atk_state_set_add_state (item->state_set, ATK_STATE_SELECTABLE); - atk_state_set_add_state (item->state_set, ATK_STATE_VISIBLE); - - for (i = 0; i < LAST_ACTION; i++) - item->action_descriptions[i] = NULL; - - item->image_description = NULL; - - item->action_idle_handler = 0; -} - -static void -gtk_icon_view_item_accessible_finalize (GObject *object) -{ - GtkIconViewItemAccessible *item; - gint i; - - g_return_if_fail (GTK_IS_ICON_VIEW_ITEM_ACCESSIBLE (object)); - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (object); - - if (item->widget) - g_object_remove_weak_pointer (G_OBJECT (item->widget), (gpointer) &item->widget); - - if (item->state_set) - g_object_unref (item->state_set); - - if (item->text_buffer) - g_object_unref (item->text_buffer); - - for (i = 0; i < LAST_ACTION; i++) - g_free (item->action_descriptions[i]); - - g_free (item->image_description); - - if (item->action_idle_handler) - { - g_source_remove (item->action_idle_handler); - item->action_idle_handler = 0; - } - - G_OBJECT_CLASS (accessible_item_parent_class)->finalize (object); -} - -static G_CONST_RETURN gchar* -gtk_icon_view_item_accessible_get_name (AtkObject *obj) -{ - if (obj->name) - return obj->name; - else - { - GtkIconViewItemAccessible *item; - GtkTextIter start_iter; - GtkTextIter end_iter; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (obj); - - gtk_text_buffer_get_start_iter (item->text_buffer, &start_iter); - gtk_text_buffer_get_end_iter (item->text_buffer, &end_iter); - - return gtk_text_buffer_get_text (item->text_buffer, &start_iter, &end_iter, FALSE); - } -} - -static AtkObject* -gtk_icon_view_item_accessible_get_parent (AtkObject *obj) -{ - GtkIconViewItemAccessible *item; - - g_return_val_if_fail (GTK_IS_ICON_VIEW_ITEM_ACCESSIBLE (obj), NULL); - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (obj); - - if (item->widget) - return gtk_widget_get_accessible (item->widget); - else - return NULL; -} - -static gint -gtk_icon_view_item_accessible_get_index_in_parent (AtkObject *obj) -{ - GtkIconViewItemAccessible *item; - - g_return_val_if_fail (GTK_IS_ICON_VIEW_ITEM_ACCESSIBLE (obj), 0); - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (obj); - - return item->item->index; -} - -static AtkStateSet * -gtk_icon_view_item_accessible_ref_state_set (AtkObject *obj) -{ - GtkIconViewItemAccessible *item; - GtkIconView *icon_view; - - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (obj); - g_return_val_if_fail (item->state_set, NULL); - - if (!item->widget) - return NULL; - - icon_view = GTK_ICON_VIEW (item->widget); - if (icon_view->priv->cursor_item == item->item) - atk_state_set_add_state (item->state_set, ATK_STATE_FOCUSED); - else - atk_state_set_remove_state (item->state_set, ATK_STATE_FOCUSED); - if (item->item->selected) - atk_state_set_add_state (item->state_set, ATK_STATE_SELECTED); - else - atk_state_set_remove_state (item->state_set, ATK_STATE_SELECTED); - - return g_object_ref (item->state_set); -} - -static void -gtk_icon_view_item_accessible_class_init (AtkObjectClass *klass) -{ - GObjectClass *gobject_class; - - accessible_item_parent_class = g_type_class_peek_parent (klass); - - gobject_class = (GObjectClass *)klass; - - gobject_class->finalize = gtk_icon_view_item_accessible_finalize; - - klass->get_index_in_parent = gtk_icon_view_item_accessible_get_index_in_parent; - klass->get_name = gtk_icon_view_item_accessible_get_name; - klass->get_parent = gtk_icon_view_item_accessible_get_parent; - klass->ref_state_set = gtk_icon_view_item_accessible_ref_state_set; -} - -static GType -gtk_icon_view_item_accessible_get_type (void) -{ - static GType type = 0; - - if (!type) - { - const GTypeInfo tinfo = - { - sizeof (GtkIconViewItemAccessibleClass), - (GBaseInitFunc) NULL, /* base init */ - (GBaseFinalizeFunc) NULL, /* base finalize */ - (GClassInitFunc) gtk_icon_view_item_accessible_class_init, /* class init */ - (GClassFinalizeFunc) NULL, /* class finalize */ - NULL, /* class data */ - sizeof (GtkIconViewItemAccessible), /* instance size */ - 0, /* nb preallocs */ - (GInstanceInitFunc) gtk_icon_view_item_accessible_object_init, /* instance init */ - NULL /* value table */ - }; - - const GInterfaceInfo atk_component_info = - { - (GInterfaceInitFunc) atk_component_item_interface_init, - (GInterfaceFinalizeFunc) NULL, - NULL - }; - const GInterfaceInfo atk_action_info = - { - (GInterfaceInitFunc) atk_action_item_interface_init, - (GInterfaceFinalizeFunc) NULL, - NULL - }; - const GInterfaceInfo atk_image_info = - { - (GInterfaceInitFunc) atk_image_item_interface_init, - (GInterfaceFinalizeFunc) NULL, - NULL - }; - const GInterfaceInfo atk_text_info = - { - (GInterfaceInitFunc) atk_text_item_interface_init, - (GInterfaceFinalizeFunc) NULL, - NULL - }; - - type = g_type_register_static (ATK_TYPE_OBJECT, - I_("GtkIconViewItemAccessible"), &tinfo, 0); - g_type_add_interface_static (type, ATK_TYPE_COMPONENT, - &atk_component_info); - g_type_add_interface_static (type, ATK_TYPE_ACTION, - &atk_action_info); - g_type_add_interface_static (type, ATK_TYPE_IMAGE, - &atk_image_info); - g_type_add_interface_static (type, ATK_TYPE_TEXT, - &atk_text_info); - } - - return type; -} - -#define GTK_TYPE_ICON_VIEW_ACCESSIBLE (gtk_icon_view_accessible_get_type ()) -#define GTK_ICON_VIEW_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_ICON_VIEW_ACCESSIBLE, GtkIconViewAccessible)) -#define GTK_IS_ICON_VIEW_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_ICON_VIEW_ACCESSIBLE)) - -static GType gtk_icon_view_accessible_get_type (void); - -typedef struct -{ - AtkObject parent; -} GtkIconViewAccessible; - -typedef struct -{ - AtkObject *item; - gint index; -} GtkIconViewItemAccessibleInfo; - -typedef struct -{ - GList *items; - - GtkAdjustment *old_hadj; - GtkAdjustment *old_vadj; - - GtkTreeModel *model; - -} GtkIconViewAccessiblePrivate; - -static GtkIconViewAccessiblePrivate * -gtk_icon_view_accessible_get_priv (AtkObject *accessible) -{ - return g_object_get_qdata (G_OBJECT (accessible), - accessible_private_data_quark); -} - -static void -gtk_icon_view_item_accessible_info_new (AtkObject *accessible, - AtkObject *item, - gint index) -{ - GtkIconViewItemAccessibleInfo *info; - GtkIconViewItemAccessibleInfo *tmp_info; - GtkIconViewAccessiblePrivate *priv; - GList *items; - - info = g_new (GtkIconViewItemAccessibleInfo, 1); - info->item = item; - info->index = index; - - priv = gtk_icon_view_accessible_get_priv (accessible); - items = priv->items; - while (items) - { - tmp_info = items->data; - if (tmp_info->index > index) - break; - items = items->next; - } - priv->items = g_list_insert_before (priv->items, items, info); - priv->old_hadj = NULL; - priv->old_vadj = NULL; -} - -static gint -gtk_icon_view_accessible_get_n_children (AtkObject *accessible) -{ - GtkIconView *icon_view; - GtkWidget *widget; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)); - if (!widget) - return 0; - - icon_view = GTK_ICON_VIEW (widget); - - return g_list_length (icon_view->priv->items); -} - -static AtkObject * -gtk_icon_view_accessible_find_child (AtkObject *accessible, - gint index) -{ - GtkIconViewAccessiblePrivate *priv; - GtkIconViewItemAccessibleInfo *info; - GList *items; - - priv = gtk_icon_view_accessible_get_priv (accessible); - items = priv->items; - - while (items) - { - info = items->data; - if (info->index == index) - return info->item; - items = items->next; - } - return NULL; -} - -static AtkObject * -gtk_icon_view_accessible_ref_child (AtkObject *accessible, - gint index) -{ - GtkIconView *icon_view; - GtkWidget *widget; - GList *icons; - AtkObject *obj; - GtkIconViewItemAccessible *a11y_item; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)); - if (!widget) - return NULL; - - icon_view = GTK_ICON_VIEW (widget); - icons = g_list_nth (icon_view->priv->items, index); - obj = NULL; - if (icons) - { - GtkIconViewItem *item = icons->data; - - g_return_val_if_fail (item->index == index, NULL); - obj = gtk_icon_view_accessible_find_child (accessible, index); - if (!obj) - { - gchar *text; - - obj = g_object_new (gtk_icon_view_item_accessible_get_type (), NULL); - gtk_icon_view_item_accessible_info_new (accessible, - obj, - index); - obj->role = ATK_ROLE_ICON; - a11y_item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (obj); - a11y_item->item = item; - a11y_item->widget = widget; - a11y_item->text_buffer = gtk_text_buffer_new (NULL); - - text = get_text (icon_view, item); - if (text) - { - gtk_text_buffer_set_text (a11y_item->text_buffer, text, -1); - g_free (text); - } - - gtk_icon_view_item_accessible_set_visibility (a11y_item, FALSE); - g_object_add_weak_pointer (G_OBJECT (widget), (gpointer) &(a11y_item->widget)); - } - g_object_ref (obj); - } - return obj; -} - -static void -gtk_icon_view_accessible_traverse_items (GtkIconViewAccessible *view, - GList *list) -{ - GtkIconViewAccessiblePrivate *priv; - GtkIconViewItemAccessibleInfo *info; - GtkIconViewItemAccessible *item; - GList *items; - - priv = gtk_icon_view_accessible_get_priv (ATK_OBJECT (view)); - if (priv->items) - { - GtkWidget *widget; - gboolean act_on_item; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (view)); - if (widget == NULL) - return; - - items = priv->items; - - act_on_item = (list == NULL); - - while (items) - { - - info = (GtkIconViewItemAccessibleInfo *)items->data; - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (info->item); - - if (act_on_item == FALSE && list == items) - act_on_item = TRUE; - - if (act_on_item) - gtk_icon_view_item_accessible_set_visibility (item, TRUE); - - items = items->next; - } - } -} - -static void -gtk_icon_view_accessible_adjustment_changed (GtkAdjustment *adjustment, - GtkIconViewAccessible *view) -{ - gtk_icon_view_accessible_traverse_items (view, NULL); -} - -static void -gtk_icon_view_accessible_set_adjustment (AtkObject *accessible, - GtkOrientation orientation, - GtkAdjustment *adjustment) -{ - GtkIconViewAccessiblePrivate *priv; - GtkAdjustment **old_adj_ptr; - - priv = gtk_icon_view_accessible_get_priv (accessible); - - /* Adjustments are set for the first time in constructor and priv is not - * initialized at that time, so skip this first setting. */ - if (!priv) - return; - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (priv->old_hadj == adjustment) - return; - - old_adj_ptr = &priv->old_hadj; - } - else - { - if (priv->old_vadj == adjustment) - return; - - old_adj_ptr = &priv->old_vadj; - } - - /* Disconnect signal handlers */ - if (*old_adj_ptr) - { - g_object_remove_weak_pointer (G_OBJECT (*old_adj_ptr), - (gpointer *)&priv->old_hadj); - g_signal_handlers_disconnect_by_func (*old_adj_ptr, - gtk_icon_view_accessible_adjustment_changed, - accessible); - } - - /* Connect signal */ - *old_adj_ptr = adjustment; - g_object_add_weak_pointer (G_OBJECT (adjustment), (gpointer *)old_adj_ptr); - g_signal_connect (adjustment, "value-changed", - G_CALLBACK (gtk_icon_view_accessible_adjustment_changed), - accessible); -} - -static void -gtk_icon_view_accessible_model_row_changed (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - AtkObject *atk_obj; - gint index; - GtkWidget *widget; - GtkIconView *icon_view; - GtkIconViewItem *item; - GtkIconViewAccessible *a11y_view; - GtkIconViewItemAccessible *a11y_item; - const gchar *name; - gchar *text; - - atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data)); - a11y_view = GTK_ICON_VIEW_ACCESSIBLE (atk_obj); - index = gtk_tree_path_get_indices(path)[0]; - a11y_item = GTK_ICON_VIEW_ITEM_ACCESSIBLE ( - gtk_icon_view_accessible_find_child (atk_obj, index)); - - if (a11y_item) - { - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_obj)); - icon_view = GTK_ICON_VIEW (widget); - item = a11y_item->item; - - name = gtk_icon_view_item_accessible_get_name (ATK_OBJECT (a11y_item)); - - if (!name || strcmp (name, "") == 0) - { - text = get_text (icon_view, item); - if (text) - { - gtk_text_buffer_set_text (a11y_item->text_buffer, text, -1); - g_free (text); - } - } - } - - g_signal_emit_by_name (atk_obj, "visible-data-changed"); - - return; -} - -static void -gtk_icon_view_accessible_model_row_inserted (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - GtkIconViewAccessiblePrivate *priv; - GtkIconViewItemAccessibleInfo *info; - GtkIconViewAccessible *view; - GtkIconViewItemAccessible *item; - GList *items; - GList *tmp_list; - AtkObject *atk_obj; - gint index; - - index = gtk_tree_path_get_indices(path)[0]; - atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data)); - view = GTK_ICON_VIEW_ACCESSIBLE (atk_obj); - priv = gtk_icon_view_accessible_get_priv (atk_obj); - - items = priv->items; - tmp_list = NULL; - while (items) - { - info = items->data; - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (info->item); - if (info->index != item->item->index) - { - if (info->index < index) - g_warning ("Unexpected index value on insertion %d %d", index, info->index); - - if (tmp_list == NULL) - tmp_list = items; - - info->index = item->item->index; - } - - items = items->next; - } - gtk_icon_view_accessible_traverse_items (view, tmp_list); - g_signal_emit_by_name (atk_obj, "children-changed::add", - index, NULL, NULL); - return; -} - -static void -gtk_icon_view_accessible_model_row_deleted (GtkTreeModel *tree_model, - GtkTreePath *path, - gpointer user_data) -{ - GtkIconViewAccessiblePrivate *priv; - GtkIconViewItemAccessibleInfo *info; - GtkIconViewAccessible *view; - GtkIconViewItemAccessible *item; - GList *items; - GList *tmp_list; - GList *deleted_item; - AtkObject *atk_obj; - gint index; - - index = gtk_tree_path_get_indices(path)[0]; - atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data)); - view = GTK_ICON_VIEW_ACCESSIBLE (atk_obj); - priv = gtk_icon_view_accessible_get_priv (atk_obj); - - items = priv->items; - tmp_list = NULL; - deleted_item = NULL; - info = NULL; - while (items) - { - info = items->data; - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (info->item); - if (info->index == index) - { - deleted_item = items; - } - if (info->index != item->item->index) - { - if (tmp_list == NULL) - tmp_list = items; - - info->index = item->item->index; - } - - items = items->next; - } - gtk_icon_view_accessible_traverse_items (view, tmp_list); - if (deleted_item) - { - info = deleted_item->data; - gtk_icon_view_item_accessible_add_state (GTK_ICON_VIEW_ITEM_ACCESSIBLE (info->item), ATK_STATE_DEFUNCT, TRUE); - g_signal_emit_by_name (atk_obj, "children-changed::remove", - index, NULL, NULL); - priv->items = g_list_remove_link (priv->items, deleted_item); - g_free (info); - } - - return; -} - -static gint -gtk_icon_view_accessible_item_compare (GtkIconViewItemAccessibleInfo *i1, - GtkIconViewItemAccessibleInfo *i2) -{ - return i1->index - i2->index; -} - -static void -gtk_icon_view_accessible_model_rows_reordered (GtkTreeModel *tree_model, - GtkTreePath *path, - GtkTreeIter *iter, - gint *new_order, - gpointer user_data) -{ - GtkIconViewAccessiblePrivate *priv; - GtkIconViewItemAccessibleInfo *info; - GtkIconView *icon_view; - GtkIconViewItemAccessible *item; - GList *items; - AtkObject *atk_obj; - gint *order; - gint length, i; - - atk_obj = gtk_widget_get_accessible (GTK_WIDGET (user_data)); - icon_view = GTK_ICON_VIEW (user_data); - priv = gtk_icon_view_accessible_get_priv (atk_obj); - - length = gtk_tree_model_iter_n_children (tree_model, NULL); - - order = g_new (gint, length); - for (i = 0; i < length; i++) - order [new_order[i]] = i; - - items = priv->items; - while (items) - { - info = items->data; - item = GTK_ICON_VIEW_ITEM_ACCESSIBLE (info->item); - info->index = order[info->index]; - item->item = g_list_nth_data (icon_view->priv->items, info->index); - items = items->next; - } - g_free (order); - priv->items = g_list_sort (priv->items, - (GCompareFunc)gtk_icon_view_accessible_item_compare); - - return; -} - -static void -gtk_icon_view_accessible_disconnect_model_signals (GtkTreeModel *model, - GtkWidget *widget) -{ - GObject *obj; - - obj = G_OBJECT (model); - g_signal_handlers_disconnect_by_func (obj, (gpointer) gtk_icon_view_accessible_model_row_changed, widget); - g_signal_handlers_disconnect_by_func (obj, (gpointer) gtk_icon_view_accessible_model_row_inserted, widget); - g_signal_handlers_disconnect_by_func (obj, (gpointer) gtk_icon_view_accessible_model_row_deleted, widget); - g_signal_handlers_disconnect_by_func (obj, (gpointer) gtk_icon_view_accessible_model_rows_reordered, widget); -} - -static void -gtk_icon_view_accessible_connect_model_signals (GtkIconView *icon_view) -{ - GObject *obj; - - obj = G_OBJECT (icon_view->priv->model); - g_signal_connect_data (obj, "row-changed", - (GCallback) gtk_icon_view_accessible_model_row_changed, - icon_view, NULL, 0); - g_signal_connect_data (obj, "row-inserted", - (GCallback) gtk_icon_view_accessible_model_row_inserted, - icon_view, NULL, G_CONNECT_AFTER); - g_signal_connect_data (obj, "row-deleted", - (GCallback) gtk_icon_view_accessible_model_row_deleted, - icon_view, NULL, G_CONNECT_AFTER); - g_signal_connect_data (obj, "rows-reordered", - (GCallback) gtk_icon_view_accessible_model_rows_reordered, - icon_view, NULL, G_CONNECT_AFTER); -} - -static void -gtk_icon_view_accessible_clear_cache (GtkIconViewAccessiblePrivate *priv) -{ - GtkIconViewItemAccessibleInfo *info; - GList *items; - - items = priv->items; - while (items) - { - info = (GtkIconViewItemAccessibleInfo *) items->data; - g_object_unref (info->item); - g_free (items->data); - items = items->next; - } - g_list_free (priv->items); - priv->items = NULL; -} - -static void -gtk_icon_view_accessible_notify_gtk (GObject *obj, - GParamSpec *pspec) -{ - GtkIconView *icon_view; - GtkWidget *widget; - AtkObject *atk_obj; - GtkIconViewAccessiblePrivate *priv; - - if (strcmp (pspec->name, "model") == 0) - { - widget = GTK_WIDGET (obj); - atk_obj = gtk_widget_get_accessible (widget); - priv = gtk_icon_view_accessible_get_priv (atk_obj); - if (priv->model) - { - g_object_remove_weak_pointer (G_OBJECT (priv->model), - (gpointer *)&priv->model); - gtk_icon_view_accessible_disconnect_model_signals (priv->model, widget); - } - gtk_icon_view_accessible_clear_cache (priv); - - icon_view = GTK_ICON_VIEW (obj); - priv->model = icon_view->priv->model; - /* If there is no model the GtkIconView is probably being destroyed */ - if (priv->model) - { - g_object_add_weak_pointer (G_OBJECT (priv->model), (gpointer *)&priv->model); - gtk_icon_view_accessible_connect_model_signals (icon_view); - } - } - - return; -} - -static void -gtk_icon_view_accessible_initialize (AtkObject *accessible, - gpointer data) -{ - GtkIconViewAccessiblePrivate *priv; - GtkIconView *icon_view; - - if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize) - ATK_OBJECT_CLASS (accessible_parent_class)->initialize (accessible, data); - - priv = g_new0 (GtkIconViewAccessiblePrivate, 1); - g_object_set_qdata (G_OBJECT (accessible), - accessible_private_data_quark, - priv); - - icon_view = GTK_ICON_VIEW (data); - if (icon_view->priv->hadjustment) - gtk_icon_view_accessible_set_adjustment (accessible, - GTK_ORIENTATION_HORIZONTAL, - icon_view->priv->hadjustment); - if (icon_view->priv->vadjustment) - gtk_icon_view_accessible_set_adjustment (accessible, - GTK_ORIENTATION_VERTICAL, - icon_view->priv->vadjustment); - g_signal_connect (data, - "notify", - G_CALLBACK (gtk_icon_view_accessible_notify_gtk), - NULL); - - priv->model = icon_view->priv->model; - if (priv->model) - { - g_object_add_weak_pointer (G_OBJECT (priv->model), (gpointer *)&priv->model); - gtk_icon_view_accessible_connect_model_signals (icon_view); - } - - accessible->role = ATK_ROLE_LAYERED_PANE; -} - -static void -gtk_icon_view_accessible_finalize (GObject *object) -{ - GtkIconViewAccessiblePrivate *priv; - - priv = gtk_icon_view_accessible_get_priv (ATK_OBJECT (object)); - gtk_icon_view_accessible_clear_cache (priv); - - g_free (priv); - - G_OBJECT_CLASS (accessible_parent_class)->finalize (object); -} - -static void -gtk_icon_view_accessible_destroyed (GtkWidget *widget, - GtkAccessible *accessible) -{ - AtkObject *atk_obj; - GtkIconViewAccessiblePrivate *priv; - - atk_obj = ATK_OBJECT (accessible); - priv = gtk_icon_view_accessible_get_priv (atk_obj); - if (priv->old_hadj) - { - g_object_remove_weak_pointer (G_OBJECT (priv->old_hadj), - (gpointer *)&priv->old_hadj); - - g_signal_handlers_disconnect_by_func (priv->old_hadj, - (gpointer) gtk_icon_view_accessible_adjustment_changed, - accessible); - priv->old_hadj = NULL; - } - if (priv->old_vadj) - { - g_object_remove_weak_pointer (G_OBJECT (priv->old_vadj), - (gpointer *)&priv->old_vadj); - - g_signal_handlers_disconnect_by_func (priv->old_vadj, - (gpointer) gtk_icon_view_accessible_adjustment_changed, - accessible); - priv->old_vadj = NULL; - } -} - -static void -gtk_icon_view_accessible_connect_widget_destroyed (GtkAccessible *accessible) -{ - GtkWidget *widget; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)); - if (widget) - { - g_signal_connect_after (widget, - "destroy", - G_CALLBACK (gtk_icon_view_accessible_destroyed), - accessible); - } - GTK_ACCESSIBLE_CLASS (accessible_parent_class)->connect_widget_destroyed (accessible); -} - -static void -gtk_icon_view_accessible_class_init (AtkObjectClass *klass) -{ - GObjectClass *gobject_class; - GtkAccessibleClass *accessible_class; - - accessible_parent_class = g_type_class_peek_parent (klass); - - gobject_class = (GObjectClass *)klass; - accessible_class = (GtkAccessibleClass *)klass; - - gobject_class->finalize = gtk_icon_view_accessible_finalize; - - klass->get_n_children = gtk_icon_view_accessible_get_n_children; - klass->ref_child = gtk_icon_view_accessible_ref_child; - klass->initialize = gtk_icon_view_accessible_initialize; - - accessible_class->connect_widget_destroyed = gtk_icon_view_accessible_connect_widget_destroyed; - - accessible_private_data_quark = g_quark_from_static_string ("icon_view-accessible-private-data"); -} - -static AtkObject* -gtk_icon_view_accessible_ref_accessible_at_point (AtkComponent *component, - gint x, - gint y, - AtkCoordType coord_type) -{ - GtkWidget *widget; - GtkIconView *icon_view; - GtkIconViewItem *item; - gint x_pos, y_pos; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component)); - if (widget == NULL) - /* State is defunct */ - return NULL; - - icon_view = GTK_ICON_VIEW (widget); - atk_component_get_extents (component, &x_pos, &y_pos, NULL, NULL, coord_type); - item = gtk_icon_view_get_item_at_coords (icon_view, x - x_pos, y - y_pos, TRUE, NULL); - if (item) - return gtk_icon_view_accessible_ref_child (ATK_OBJECT (component), item->index); - - return NULL; -} - -static void -atk_component_interface_init (AtkComponentIface *iface) -{ - iface->ref_accessible_at_point = gtk_icon_view_accessible_ref_accessible_at_point; -} - -static gboolean -gtk_icon_view_accessible_add_selection (AtkSelection *selection, - gint i) -{ - GtkWidget *widget; - GtkIconView *icon_view; - GtkIconViewItem *item; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection)); - if (widget == NULL) - return FALSE; - - icon_view = GTK_ICON_VIEW (widget); - - item = g_list_nth_data (icon_view->priv->items, i); - - if (!item) - return FALSE; - - gtk_icon_view_select_item (icon_view, item); - - return TRUE; -} - -static gboolean -gtk_icon_view_accessible_clear_selection (AtkSelection *selection) -{ - GtkWidget *widget; - GtkIconView *icon_view; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection)); - if (widget == NULL) - return FALSE; - - icon_view = GTK_ICON_VIEW (widget); - gtk_icon_view_unselect_all (icon_view); - - return TRUE; -} - -static AtkObject* -gtk_icon_view_accessible_ref_selection (AtkSelection *selection, - gint i) -{ - GList *l; - GtkWidget *widget; - GtkIconView *icon_view; - GtkIconViewItem *item; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection)); - if (widget == NULL) - return NULL; - - icon_view = GTK_ICON_VIEW (widget); - - l = icon_view->priv->items; - while (l) - { - item = l->data; - if (item->selected) - { - if (i == 0) - return atk_object_ref_accessible_child (gtk_widget_get_accessible (widget), item->index); - else - i--; - } - l = l->next; - } - - return NULL; -} - -static gint -gtk_icon_view_accessible_get_selection_count (AtkSelection *selection) -{ - GtkWidget *widget; - GtkIconView *icon_view; - GtkIconViewItem *item; - GList *l; - gint count; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection)); - if (widget == NULL) - return 0; - - icon_view = GTK_ICON_VIEW (widget); - - l = icon_view->priv->items; - count = 0; - while (l) - { - item = l->data; - - if (item->selected) - count++; - - l = l->next; - } - - return count; -} - -static gboolean -gtk_icon_view_accessible_is_child_selected (AtkSelection *selection, - gint i) -{ - GtkWidget *widget; - GtkIconView *icon_view; - GtkIconViewItem *item; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection)); - if (widget == NULL) - return FALSE; - - icon_view = GTK_ICON_VIEW (widget); - - item = g_list_nth_data (icon_view->priv->items, i); - if (!item) - return FALSE; - - return item->selected; -} - -static gboolean -gtk_icon_view_accessible_remove_selection (AtkSelection *selection, - gint i) -{ - GtkWidget *widget; - GtkIconView *icon_view; - GtkIconViewItem *item; - GList *l; - gint count; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection)); - if (widget == NULL) - return FALSE; - - icon_view = GTK_ICON_VIEW (widget); - l = icon_view->priv->items; - count = 0; - while (l) - { - item = l->data; - if (item->selected) - { - if (count == i) - { - gtk_icon_view_unselect_item (icon_view, item); - return TRUE; - } - count++; - } - l = l->next; - } - - return FALSE; -} - -static gboolean -gtk_icon_view_accessible_select_all_selection (AtkSelection *selection) -{ - GtkWidget *widget; - GtkIconView *icon_view; - - widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (selection)); - if (widget == NULL) - return FALSE; - - icon_view = GTK_ICON_VIEW (widget); - gtk_icon_view_select_all (icon_view); - return TRUE; -} - -static void -gtk_icon_view_accessible_selection_interface_init (AtkSelectionIface *iface) -{ - iface->add_selection = gtk_icon_view_accessible_add_selection; - iface->clear_selection = gtk_icon_view_accessible_clear_selection; - iface->ref_selection = gtk_icon_view_accessible_ref_selection; - iface->get_selection_count = gtk_icon_view_accessible_get_selection_count; - iface->is_child_selected = gtk_icon_view_accessible_is_child_selected; - iface->remove_selection = gtk_icon_view_accessible_remove_selection; - iface->select_all_selection = gtk_icon_view_accessible_select_all_selection; -} - -static GType -gtk_icon_view_accessible_get_type (void) -{ - static GType type = 0; - - if (!type) - { - GTypeInfo tinfo = - { - 0, /* class size */ - (GBaseInitFunc) NULL, /* base init */ - (GBaseFinalizeFunc) NULL, /* base finalize */ - (GClassInitFunc) gtk_icon_view_accessible_class_init, - (GClassFinalizeFunc) NULL, /* class finalize */ - NULL, /* class data */ - 0, /* instance size */ - 0, /* nb preallocs */ - (GInstanceInitFunc) NULL, /* instance init */ - NULL /* value table */ - }; - const GInterfaceInfo atk_component_info = - { - (GInterfaceInitFunc) atk_component_interface_init, - (GInterfaceFinalizeFunc) NULL, - NULL - }; - const GInterfaceInfo atk_selection_info = - { - (GInterfaceInitFunc) gtk_icon_view_accessible_selection_interface_init, - (GInterfaceFinalizeFunc) NULL, - NULL - }; - - /* - * Figure out the size of the class and instance - * we are deriving from - */ - AtkObjectFactory *factory; - GType derived_type; - GTypeQuery query; - GType derived_atk_type; - - derived_type = g_type_parent (GTK_TYPE_ICON_VIEW); - factory = atk_registry_get_factory (atk_get_default_registry (), - derived_type); - derived_atk_type = atk_object_factory_get_accessible_type (factory); - g_type_query (derived_atk_type, &query); - tinfo.class_size = query.class_size; - tinfo.instance_size = query.instance_size; - - type = g_type_register_static (derived_atk_type, - I_("GtkIconViewAccessible"), - &tinfo, 0); - g_type_add_interface_static (type, ATK_TYPE_COMPONENT, - &atk_component_info); - g_type_add_interface_static (type, ATK_TYPE_SELECTION, - &atk_selection_info); - } - return type; -} - -static AtkObject * -gtk_icon_view_accessible_new (GObject *obj) -{ - AtkObject *accessible; - - g_return_val_if_fail (GTK_IS_WIDGET (obj), NULL); - - accessible = g_object_new (gtk_icon_view_accessible_get_type (), NULL); - atk_object_initialize (accessible, obj); - - return accessible; -} - -static GType -gtk_icon_view_accessible_factory_get_accessible_type (void) -{ - return gtk_icon_view_accessible_get_type (); -} - -static AtkObject* -gtk_icon_view_accessible_factory_create_accessible (GObject *obj) -{ - return gtk_icon_view_accessible_new (obj); -} - -static void -gtk_icon_view_accessible_factory_class_init (AtkObjectFactoryClass *klass) -{ - klass->create_accessible = gtk_icon_view_accessible_factory_create_accessible; - klass->get_accessible_type = gtk_icon_view_accessible_factory_get_accessible_type; -} - -static GType -gtk_icon_view_accessible_factory_get_type (void) -{ - static GType type = 0; - - if (!type) - { - const GTypeInfo tinfo = - { - sizeof (AtkObjectFactoryClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_icon_view_accessible_factory_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (AtkObjectFactory), - 0, /* n_preallocs */ - NULL, NULL - }; - - type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY, - I_("GtkIconViewAccessibleFactory"), - &tinfo, 0); - } - return type; -} - - -static AtkObject * -gtk_icon_view_get_accessible (GtkWidget *widget) -{ - static gboolean first_time = TRUE; - - if (first_time) - { - AtkObjectFactory *factory; - AtkRegistry *registry; - GType derived_type; - GType derived_atk_type; - - /* - * Figure out whether accessibility is enabled by looking at the - * type of the accessible object which would be created for - * the parent type of GtkIconView. - */ - derived_type = g_type_parent (GTK_TYPE_ICON_VIEW); + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), FALSE); - registry = atk_get_default_registry (); - factory = atk_registry_get_factory (registry, - derived_type); - derived_atk_type = atk_object_factory_get_accessible_type (factory); - if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE)) - atk_registry_set_factory_type (registry, - GTK_TYPE_ICON_VIEW, - gtk_icon_view_accessible_factory_get_type ()); - first_time = FALSE; - } - return GTK_WIDGET_CLASS (gtk_icon_view_parent_class)->get_accessible (widget); + return icon_view->priv->activate_on_single_click; } static gboolean gtk_icon_view_buildable_custom_tag_start (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *tagname, - GMarkupParser *parser, - gpointer *data) + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) { if (parent_buildable_iface->custom_tag_start (buildable, builder, child, - tagname, parser, data)) + tagname, parser, data)) return TRUE; return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child, - tagname, parser, data); + tagname, parser, data); } static void gtk_icon_view_buildable_custom_tag_end (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *child, - const gchar *tagname, - gpointer *data) -{ - if (!_gtk_cell_layout_buildable_custom_tag_end (buildable, builder, - child, tagname, data)) - parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname, - data); + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *data) +{ + if (!_gtk_cell_layout_buildable_custom_tag_end (buildable, builder, + child, tagname, data)) + parent_buildable_iface->custom_tag_end (buildable, builder, + child, tagname, data); }