diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt index 15a692d12e..555a043ba2 100644 --- a/docs/reference/gtk/gtk3-sections.txt +++ b/docs/reference/gtk/gtk3-sections.txt @@ -4387,16 +4387,19 @@ gtk_cell_layout_get_type GtkCellArea GtkCellAreaClass GtkCellCallback +GtkCellAllocCallback GTK_CELL_AREA_WARN_INVALID_CELL_PROPERTY_ID gtk_cell_area_add gtk_cell_area_remove gtk_cell_area_has_renderer gtk_cell_area_foreach -gtk_cell_area_get_cell_allocation +gtk_cell_area_foreach_alloc gtk_cell_area_event gtk_cell_area_render gtk_cell_area_set_style_detail gtk_cell_area_get_style_detail +gtk_cell_area_get_cell_allocation +gtk_cell_area_get_cell_at_position gtk_cell_area_create_context gtk_cell_area_get_request_mode gtk_cell_area_get_preferred_width diff --git a/gtk/gtkcellarea.c b/gtk/gtkcellarea.c index a601962a63..166546bc45 100644 --- a/gtk/gtkcellarea.c +++ b/gtk/gtkcellarea.c @@ -424,6 +424,20 @@ typedef struct { gboolean has_renderer; } HasRendererCheck; +/* Used in foreach loop to get a cell's allocation */ +typedef struct { + GtkCellRenderer *renderer; + GdkRectangle allocation; +} RendererAllocationData; + +/* Used in foreach loop to get a cell by position */ +typedef struct { + gint x; + gint y; + GtkCellRenderer *renderer; + GdkRectangle cell_area; +} CellByPositionData; + /* Attribute/Cell metadata */ typedef struct { const gchar *attribute; @@ -919,6 +933,7 @@ gtk_cell_area_real_event (GtkCellArea *area, GtkCellRendererState flags) { GtkCellAreaPrivate *priv = area->priv; + gboolean retval = FALSE; if (event->type == GDK_KEY_PRESS && (flags & GTK_CELL_RENDERER_FOCUSED) != 0) { @@ -928,11 +943,63 @@ gtk_cell_area_real_event (GtkCellArea *area, if (priv->edited_cell && (key_event->keyval == GDK_KEY_Escape)) { gtk_cell_area_stop_editing (area, TRUE); - return TRUE; + retval = TRUE; + } + } + else if (event->type == GDK_BUTTON_PRESS) + { + GdkEventButton *button_event = (GdkEventButton *)event; + + if (button_event->button == 1) + { + GtkCellRenderer *renderer; + GtkCellRenderer *focus_renderer; + GdkRectangle alloc_area, inner_area; + gint event_x, event_y; + + /* We may need some semantics to tell us the offset of the event + * window we are handling events for (i.e. GtkTreeView has a bin_window) */ + event_x = button_event->x; + event_y = button_event->y; + + renderer = + gtk_cell_area_get_cell_at_position (area, context, widget, + cell_area, event_x, event_y, + &alloc_area); + + if (renderer) + { + focus_renderer = gtk_cell_area_get_focus_from_sibling (area, renderer); + if (!focus_renderer) + focus_renderer = renderer; + + /* If we're already editing, cancel it and set focus */ + if (gtk_cell_area_get_edited_cell (area)) + { + /* XXX Was it really canceled in this case ? */ + gtk_cell_area_stop_editing (area, TRUE); + gtk_cell_area_set_focus_cell (area, focus_renderer); + retval = TRUE; + } + else + { + /* If we are activating via a focus sibling, + * we need to fetch the right cell area for the real event renderer */ + if (focus_renderer != renderer) + gtk_cell_area_get_cell_allocation (area, context, widget, focus_renderer, + cell_area, &alloc_area); + + gtk_cell_area_inner_cell_area (area, widget, &alloc_area, &inner_area); + + gtk_cell_area_set_focus_cell (area, focus_renderer); + retval = gtk_cell_area_activate_cell (area, widget, focus_renderer, + event, &inner_area, flags); + } + } } } - return FALSE; + return retval; } static void @@ -1401,43 +1468,41 @@ gtk_cell_area_foreach (GtkCellArea *area, } /** - * gtk_cell_area_get_cell_allocation: + * gtk_cell_area_foreach_alloc: * @area: a #GtkCellArea - * @context: the #GtkCellAreaContext used to hold sizes for @area. - * @widget: the #GtkWidget that @area is rendering on - * @renderer: the #GtkCellRenderer to get the allocation for - * @cell_area: the whole allocated area for @area in @widget - * for this row - * @allocation: (out): where to store the allocation for @renderer + * @context: the #GtkCellAreaContext for this row of data. + * @widget: the #GtkWidget that @area is rendering to + * @cell_area: the @widget relative coordinates and size for @area + * @callback: the #GtkCellAllocCallback to call + * @callback_data: user provided data pointer * - * Derives the allocation of @renderer inside @area if @area - * were to be renderered in @cell_area. + * Calls @callback for every #GtkCellRenderer in @area with the + * allocated rectangle inside @cell_area. * * Since: 3.0 */ void -gtk_cell_area_get_cell_allocation (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GtkCellRenderer *renderer, - const GdkRectangle *cell_area, - GdkRectangle *allocation) +gtk_cell_area_foreach_alloc (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + GtkCellAllocCallback callback, + gpointer callback_data) { GtkCellAreaClass *class; g_return_if_fail (GTK_IS_CELL_AREA (area)); g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); g_return_if_fail (cell_area != NULL); - g_return_if_fail (allocation != NULL); + g_return_if_fail (callback != NULL); class = GTK_CELL_AREA_GET_CLASS (area); - if (class->get_cell_allocation) - class->get_cell_allocation (area, context, widget, renderer, cell_area, allocation); + if (class->foreach_alloc) + class->foreach_alloc (area, context, widget, cell_area, callback, callback_data); else - g_warning ("GtkCellAreaClass::get_cell_allocation not implemented for `%s'", + g_warning ("GtkCellAreaClass::foreach_alloc not implemented for `%s'", g_type_name (G_TYPE_FROM_INSTANCE (area))); } @@ -1576,6 +1641,117 @@ gtk_cell_area_get_style_detail (GtkCellArea *area) return priv->style_detail; } +static gboolean +get_cell_allocation (GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + RendererAllocationData *data) +{ + if (data->renderer == renderer) + data->allocation = *cell_area; + + return (data->renderer == renderer); +} + +/** + * gtk_cell_area_get_cell_allocation: + * @area: a #GtkCellArea + * @context: the #GtkCellAreaContext used to hold sizes for @area. + * @widget: the #GtkWidget that @area is rendering on + * @renderer: the #GtkCellRenderer to get the allocation for + * @cell_area: the whole allocated area for @area in @widget + * for this row + * @allocation: (out): where to store the allocation for @renderer + * + * Derives the allocation of @renderer inside @area if @area + * were to be renderered in @cell_area. + * + * Since: 3.0 + */ +void +gtk_cell_area_get_cell_allocation (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + GdkRectangle *allocation) +{ + RendererAllocationData data = { renderer, { 0, } }; + + g_return_if_fail (GTK_IS_CELL_AREA (area)); + g_return_if_fail (GTK_IS_CELL_AREA_CONTEXT (context)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (cell_area != NULL); + g_return_if_fail (allocation != NULL); + + gtk_cell_area_foreach_alloc (area, context, widget, cell_area, + (GtkCellAllocCallback)get_cell_allocation, &data); + + *allocation = data.allocation; +} + +static gboolean +get_cell_by_position (GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + CellByPositionData *data) +{ + if (data->x >= cell_area->x && data->x < cell_area->x + cell_area->width && + data->y >= cell_area->y && data->y < cell_area->y + cell_area->height) + { + data->renderer = renderer; + data->cell_area = *cell_area; + } + + return (data->renderer != NULL); +} + +/** + * gtk_cell_area_get_cell_at_position: + * @area: a #GtkCellArea + * @context: the #GtkCellAreaContext used to hold sizes for @area. + * @widget: the #GtkWidget that @area is rendering on + * @cell_area: the whole allocated area for @area in @widget + * for this row + * @x: the x position + * @y: the y position + * @alloc_area: (out) (allow-none): where to store the inner allocated area of the + * returned cell renderer, or %NULL. + * + * Gets the #GtkCellRenderer at @x and @y coordinates inside @area and optionally + * returns the full cell allocation for it inside @cell_area. + * + * Returns: the #GtkCellRenderer at @x and @y. + * + * Since: 3.0 + */ +GtkCellRenderer * +gtk_cell_area_get_cell_at_position (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + gint x, + gint y, + GdkRectangle *alloc_area) +{ + CellByPositionData data = { x, y, NULL, { 0, } }; + + g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL); + g_return_val_if_fail (GTK_IS_CELL_AREA_CONTEXT (context), NULL); + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + g_return_val_if_fail (cell_area != NULL, NULL); + g_return_val_if_fail (x >= cell_area->x && x <= cell_area->x + cell_area->width, NULL); + g_return_val_if_fail (y >= cell_area->y && y <= cell_area->y + cell_area->height, NULL); + + gtk_cell_area_foreach_alloc (area, context, widget, cell_area, + (GtkCellAllocCallback)get_cell_by_position, &data); + + if (alloc_area) + *alloc_area = data.cell_area; + + return data.renderer; +} + + /************************************************************* * API: Geometry * *************************************************************/ diff --git a/gtk/gtkcellarea.h b/gtk/gtkcellarea.h index aa0560c20c..83c174188f 100644 --- a/gtk/gtkcellarea.h +++ b/gtk/gtkcellarea.h @@ -72,6 +72,22 @@ typedef struct _GtkCellAreaContext GtkCellAreaContext; typedef gboolean (*GtkCellCallback) (GtkCellRenderer *renderer, gpointer data); +/** + * GtkCellAllocCallback: + * @renderer: the cell renderer to operate on + * @area: the area allocated to @renderer inside the rectangle provided to gtk_cell_area_foreach_alloc(). + * @data: user-supplied data + * + * The type of the callback functions used for iterating over + * the cell renderers and their allocated areas inside a #GtkCellArea, + * see gtk_cell_area_foreach_alloc(). + * + * Return value: %TRUE to stop iterating over cells. + */ +typedef gboolean (*GtkCellAllocCallback) (GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + gpointer data); + struct _GtkCellArea { @@ -88,8 +104,8 @@ struct _GtkCellArea * @remove: removes a #GtkCellRenderer from the area. * @foreach: Calls the #GtkCellCallback function on every #GtkCellRenderer in the area * with the provided user data until the callback returns %TRUE. - * @get_cell_allocation: Gets the position (relative to the passed @cell_area rectangle) - * and size of a #GtkCellRenderer. + * @foreach_alloc: Calls the #GtkCellAllocCallback function on every #GtkCellRenderer in the area + * with the allocated area for the cell and the provided user data until the callback returns %TRUE. * @event: Handle an event in the area, this is generally used to activate a cell * at the event location for button events but can also be used to generically pass * events to #GtkWidgets drawn onto the area. @@ -154,12 +170,12 @@ struct _GtkCellAreaClass void (* foreach) (GtkCellArea *area, GtkCellCallback callback, gpointer callback_data); - void (* get_cell_allocation) (GtkCellArea *area, + void (* foreach_alloc) (GtkCellArea *area, GtkCellAreaContext *context, GtkWidget *widget, - GtkCellRenderer *renderer, const GdkRectangle *cell_area, - GdkRectangle *allocation); + GtkCellAllocCallback callback, + gpointer callback_data); gint (* event) (GtkCellArea *area, GtkCellAreaContext *context, GtkWidget *widget, @@ -253,12 +269,12 @@ gboolean gtk_cell_area_has_renderer (GtkCellArea void gtk_cell_area_foreach (GtkCellArea *area, GtkCellCallback callback, gpointer callback_data); -void gtk_cell_area_get_cell_allocation (GtkCellArea *area, +void gtk_cell_area_foreach_alloc (GtkCellArea *area, GtkCellAreaContext *context, GtkWidget *widget, - GtkCellRenderer *renderer, const GdkRectangle *cell_area, - GdkRectangle *allocation); + GtkCellAllocCallback callback, + gpointer callback_data); gint gtk_cell_area_event (GtkCellArea *area, GtkCellAreaContext *context, GtkWidget *widget, @@ -277,6 +293,22 @@ void gtk_cell_area_set_style_detail (GtkCellArea const gchar *detail); G_CONST_RETURN gchar *gtk_cell_area_get_style_detail (GtkCellArea *area); + +void gtk_cell_area_get_cell_allocation (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + GdkRectangle *allocation); +GtkCellRenderer *gtk_cell_area_get_cell_at_position (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + gint x, + gint y, + GdkRectangle *alloc_area); + + /* Geometry */ GtkCellAreaContext *gtk_cell_area_create_context (GtkCellArea *area); GtkSizeRequestMode gtk_cell_area_get_request_mode (GtkCellArea *area); diff --git a/gtk/gtkcellareabox.c b/gtk/gtkcellareabox.c index f60cc9a659..62db9b5aec 100644 --- a/gtk/gtkcellareabox.c +++ b/gtk/gtkcellareabox.c @@ -74,18 +74,12 @@ static void gtk_cell_area_box_remove (GtkCellArea static void gtk_cell_area_box_foreach (GtkCellArea *area, GtkCellCallback callback, gpointer callback_data); -static void gtk_cell_area_box_get_cell_allocation (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GtkCellRenderer *renderer, - const GdkRectangle *cell_area, - GdkRectangle *allocation); -static gint gtk_cell_area_box_event (GtkCellArea *area, +static void gtk_cell_area_box_foreach_alloc (GtkCellArea *area, GtkCellAreaContext *context, GtkWidget *widget, - GdkEvent *event, const GdkRectangle *cell_area, - GtkCellRendererState flags); + GtkCellAllocCallback callback, + gpointer callback_data); static void gtk_cell_area_box_render (GtkCellArea *area, GtkCellAreaContext *context, GtkWidget *widget, @@ -267,8 +261,7 @@ gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class) area_class->add = gtk_cell_area_box_add; area_class->remove = gtk_cell_area_box_remove; area_class->foreach = gtk_cell_area_box_foreach; - area_class->get_cell_allocation = gtk_cell_area_box_get_cell_allocation; - area_class->event = gtk_cell_area_box_event; + area_class->foreach_alloc = gtk_cell_area_box_foreach_alloc; area_class->render = gtk_cell_area_box_render; area_class->set_cell_property = gtk_cell_area_box_set_cell_property; area_class->get_cell_property = gtk_cell_area_box_get_cell_property; @@ -1040,19 +1033,20 @@ gtk_cell_area_box_foreach (GtkCellArea *area, } static void -gtk_cell_area_box_get_cell_allocation (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GtkCellRenderer *renderer, - const GdkRectangle *cell_area, - GdkRectangle *allocation) +gtk_cell_area_box_foreach_alloc (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + const GdkRectangle *cell_area, + GtkCellAllocCallback callback, + gpointer callback_data) { GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); GtkCellAreaBoxPrivate *priv = box->priv; GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); GSList *allocated_cells, *l; + GdkRectangle allocation; - *allocation = *cell_area; + allocation = *cell_area; /* Get a list of cells with allocation sizes decided regardless * of alignments and pack order etc. */ @@ -1063,145 +1057,25 @@ gtk_cell_area_box_get_cell_allocation (GtkCellArea *area, { AllocatedCell *cell = l->data; - if (cell->renderer == renderer) + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) { - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - allocation->x = cell_area->x + cell->position; - allocation->width = cell->size; - } - else - { - allocation->y = cell_area->y + cell->position; - allocation->height = cell->size; - } - - break; + allocation.x = cell_area->x + cell->position; + allocation.width = cell->size; } + else + { + allocation.y = cell_area->y + cell->position; + allocation.height = cell->size; + } + + if (callback (cell->renderer, &allocation, callback_data)) + break; } g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL); g_slist_free (allocated_cells); } -enum { - FOCUS_NONE, - FOCUS_PREV, - FOCUS_NEXT -}; - -static gint -gtk_cell_area_box_event (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - GdkEvent *event, - const GdkRectangle *cell_area, - GtkCellRendererState flags) -{ - gint retval; - - /* First let the parent class handle activation of cells via keystrokes */ - retval = - GTK_CELL_AREA_CLASS (gtk_cell_area_box_parent_class)->event (area, context, widget, - event, cell_area, flags); - - if (retval) - return retval; - - /* Also detect mouse events, for mouse events we need to allocate the renderers - * and find which renderer needs to be activated. - */ - if (event->type == GDK_BUTTON_PRESS) - { - GdkEventButton *button_event = (GdkEventButton *)event; - - if (button_event->button == 1) - { - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = box->priv; - GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); - GSList *allocated_cells, *l; - GdkRectangle cell_background, inner_area; - gint event_x, event_y; - - /* We may need some semantics to tell us the offset of the event - * window we are handling events for (i.e. GtkTreeView has a bin_window) */ - event_x = button_event->x; - event_y = button_event->y; - - cell_background = *cell_area; - - /* Get a list of cells with allocation sizes decided regardless - * of alignments and pack order etc. */ - allocated_cells = get_allocated_cells (box, box_context, widget, - cell_area->width, cell_area->height); - - for (l = allocated_cells; l; l = l->next) - { - AllocatedCell *cell = l->data; - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - cell_background.x = cell_area->x + cell->position; - cell_background.width = cell->size; - } - else - { - cell_background.y = cell_area->y + cell->position; - cell_background.height = cell->size; - } - - /* Remove margins from the background area to produce the cell area - */ - gtk_cell_area_inner_cell_area (area, widget, &cell_background, &inner_area); - - if (event_x >= inner_area.x && event_x <= inner_area.x + inner_area.width && - event_y >= inner_area.y && event_y <= inner_area.y + inner_area.height) - { - GtkCellRenderer *event_renderer = NULL; - GtkCellRenderer *focus_renderer; - - focus_renderer = gtk_cell_area_get_focus_from_sibling (area, cell->renderer); - if (focus_renderer) - event_renderer = focus_renderer; - else - event_renderer = cell->renderer; - - event_renderer = cell->renderer; - - if (event_renderer) - { - if (gtk_cell_area_get_edited_cell (area)) - { - /* XXX Was it really canceled in this case ? */ - gtk_cell_area_stop_editing (area, TRUE); - gtk_cell_area_set_focus_cell (area, event_renderer); - retval = TRUE; - } - else - { - /* If we are activating via a focus sibling, we need to fix the - * cell area */ - if (event_renderer != cell->renderer) - gtk_cell_area_inner_cell_area (area, widget, cell_area, &cell_background); - - gtk_cell_area_set_focus_cell (area, event_renderer); - - retval = gtk_cell_area_activate_cell (area, widget, event_renderer, - event, &cell_background, flags); - } - break; - } - } - } - g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL); - g_slist_free (allocated_cells); - } - } - - return retval; -} - static void gtk_cell_area_box_render (GtkCellArea *area, GtkCellAreaContext *context, @@ -1959,6 +1833,12 @@ gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area, *natural_width = nat_width; } +enum { + FOCUS_NONE, + FOCUS_PREV, + FOCUS_NEXT +}; + static gboolean gtk_cell_area_box_focus (GtkCellArea *area, GtkDirectionType direction)