/* GObjectClass */
-static void gtk_cell_area_box_finalize (GObject *object);
-static void gtk_cell_area_box_dispose (GObject *object);
-static void gtk_cell_area_box_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec);
-static void gtk_cell_area_box_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec);
+static void gtk_cell_area_box_finalize (GObject *object);
+static void gtk_cell_area_box_dispose (GObject *object);
+static void gtk_cell_area_box_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_cell_area_box_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
/* GtkCellAreaClass */
-static void gtk_cell_area_box_add (GtkCellArea *area,
- GtkCellRenderer *renderer);
-static void gtk_cell_area_box_remove (GtkCellArea *area,
- GtkCellRenderer *renderer);
-static void gtk_cell_area_box_forall (GtkCellArea *area,
- GtkCellCallback callback,
- gpointer callback_data);
-static gint gtk_cell_area_box_event (GtkCellArea *area,
- GtkCellAreaIter *iter,
- GtkWidget *widget,
- GdkEvent *event,
- const GdkRectangle *cell_area);
-static void gtk_cell_area_box_render (GtkCellArea *area,
- GtkCellAreaIter *iter,
- GtkWidget *widget,
- cairo_t *cr,
- const GdkRectangle *cell_area);
-
-static GtkCellAreaIter *gtk_cell_area_box_create_iter (GtkCellArea *area);
-static GtkSizeRequestMode gtk_cell_area_box_get_request_mode (GtkCellArea *area);
-static void gtk_cell_area_box_get_preferred_width (GtkCellArea *area,
- GtkCellAreaIter *iter,
- GtkWidget *widget,
- gint *minimum_width,
- gint *natural_width);
-static void gtk_cell_area_box_get_preferred_height (GtkCellArea *area,
- GtkCellAreaIter *iter,
- GtkWidget *widget,
- gint *minimum_height,
- gint *natural_height);
-static void gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area,
- GtkCellAreaIter *iter,
- GtkWidget *widget,
- gint width,
- gint *minimum_height,
- gint *natural_height);
-static void gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area,
- GtkCellAreaIter *iter,
- GtkWidget *widget,
- gint height,
- gint *minimum_width,
- gint *natural_width);
+static void gtk_cell_area_box_add (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+static void gtk_cell_area_box_remove (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+static void gtk_cell_area_box_forall (GtkCellArea *area,
+ GtkCellCallback callback,
+ gpointer callback_data);
+static void gtk_cell_area_box_get_cell_allocation (GtkCellArea *area,
+ GtkCellAreaIter *iter,
+ GtkWidget *widget,
+ GtkCellRenderer *renderer,
+ const GdkRectangle *cell_area,
+ GdkRectangle *allocation);
+static gint gtk_cell_area_box_event (GtkCellArea *area,
+ GtkCellAreaIter *iter,
+ GtkWidget *widget,
+ GdkEvent *event,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags);
+static void gtk_cell_area_box_render (GtkCellArea *area,
+ GtkCellAreaIter *iter,
+ GtkWidget *widget,
+ cairo_t *cr,
+ const GdkRectangle *background_area,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean paint_focus);
+static void gtk_cell_area_box_set_cell_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_cell_area_box_get_cell_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static GtkCellAreaIter *gtk_cell_area_box_create_iter (GtkCellArea *area);
+static GtkSizeRequestMode gtk_cell_area_box_get_request_mode (GtkCellArea *area);
+static void gtk_cell_area_box_get_preferred_width (GtkCellArea *area,
+ GtkCellAreaIter *iter,
+ GtkWidget *widget,
+ gint *minimum_width,
+ gint *natural_width);
+static void gtk_cell_area_box_get_preferred_height (GtkCellArea *area,
+ GtkCellAreaIter *iter,
+ GtkWidget *widget,
+ gint *minimum_height,
+ gint *natural_height);
+static void gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea *area,
+ GtkCellAreaIter *iter,
+ GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height);
+static void gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea *area,
+ GtkCellAreaIter *iter,
+ GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width);
+static gboolean gtk_cell_area_box_focus (GtkCellArea *area,
+ GtkDirectionType direction);
/* GtkCellLayoutIface */
static void gtk_cell_area_box_cell_layout_init (GtkCellLayoutIface *iface);
static void cell_info_free (CellInfo *info);
static gint cell_info_find (CellInfo *info,
GtkCellRenderer *renderer);
-static CellGroup *cell_group_new (guint id);
-static void cell_group_free (CellGroup *group);
+
static AllocatedCell *allocated_cell_new (GtkCellRenderer *renderer,
gint position,
gint size);
static void allocated_cell_free (AllocatedCell *cell);
static GList *list_consecutive_cells (GtkCellAreaBox *box);
-static GList *construct_cell_groups (GtkCellAreaBox *box);
static gint count_expand_groups (GtkCellAreaBox *box);
static void iter_weak_notify (GtkCellAreaBox *box,
GtkCellAreaBoxIter *dead_iter);
static void init_iter_groups (GtkCellAreaBox *box);
static void init_iter_group (GtkCellAreaBox *box,
GtkCellAreaBoxIter *iter);
-static void get_renderer_size (GtkCellRenderer *renderer,
- GtkOrientation orientation,
- GtkWidget *widget,
- gint for_size,
- gint *minimum_size,
- gint *natural_size);
static GSList *get_allocated_cells (GtkCellAreaBox *box,
GtkCellAreaBoxIter *iter,
GtkWidget *widget);
GtkOrientation orientation;
GList *cells;
- GList *groups;
+ GArray *groups;
GSList *iters;
gint spacing;
};
-
-
enum {
PROP_0,
PROP_ORIENTATION,
PROP_SPACING
};
+enum {
+ CELL_PROP_0,
+ CELL_PROP_EXPAND,
+ CELL_PROP_ALIGN,
+ CELL_PROP_PACK_TYPE
+};
+
G_DEFINE_TYPE_WITH_CODE (GtkCellAreaBox, gtk_cell_area_box, GTK_TYPE_CELL_AREA,
G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
gtk_cell_area_box_cell_layout_init)
priv = box->priv;
priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+ priv->groups = g_array_new (FALSE, TRUE, sizeof (CellGroup));
priv->cells = NULL;
- priv->groups = NULL;
priv->iters = NULL;
priv->spacing = 0;
}
object_class->get_property = gtk_cell_area_box_get_property;
/* GtkCellAreaClass */
- area_class->add = gtk_cell_area_box_add;
- area_class->remove = gtk_cell_area_box_remove;
- area_class->forall = gtk_cell_area_box_forall;
- area_class->event = gtk_cell_area_box_event;
- area_class->render = gtk_cell_area_box_render;
+ area_class->add = gtk_cell_area_box_add;
+ area_class->remove = gtk_cell_area_box_remove;
+ area_class->forall = gtk_cell_area_box_forall;
+ area_class->get_cell_allocation = gtk_cell_area_box_get_cell_allocation;
+ area_class->event = gtk_cell_area_box_event;
+ 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;
area_class->create_iter = gtk_cell_area_box_create_iter;
area_class->get_request_mode = gtk_cell_area_box_get_request_mode;
area_class->get_preferred_height_for_width = gtk_cell_area_box_get_preferred_height_for_width;
area_class->get_preferred_width_for_height = gtk_cell_area_box_get_preferred_width_for_height;
+ area_class->focus = gtk_cell_area_box_focus;
+
+ /* Properties */
g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
g_object_class_install_property (object_class,
0,
GTK_PARAM_READWRITE));
+ /* Cell Properties */
+ gtk_cell_area_class_install_cell_property (area_class,
+ CELL_PROP_EXPAND,
+ g_param_spec_boolean
+ ("expand",
+ P_("Expand"),
+ P_("Whether the cell expands"),
+ FALSE,
+ GTK_PARAM_READWRITE));
+
+ gtk_cell_area_class_install_cell_property (area_class,
+ CELL_PROP_ALIGN,
+ g_param_spec_boolean
+ ("align",
+ P_("Align"),
+ P_("Whether cell should align with adjacent rows"),
+ TRUE,
+ GTK_PARAM_READWRITE));
+
+ gtk_cell_area_class_install_cell_property (area_class,
+ CELL_PROP_PACK_TYPE,
+ g_param_spec_enum
+ ("pack-type",
+ P_("Pack Type"),
+ P_("A GtkPackType indicating whether the cell is packed with "
+ "reference to the start or end of the cell area"),
+ GTK_TYPE_PACK_TYPE, GTK_PACK_START,
+ GTK_PARAM_READWRITE));
+
g_type_class_add_private (object_class, sizeof (GtkCellAreaBoxPrivate));
}
return (info->renderer == renderer) ? 0 : -1;
}
-static CellGroup *
-cell_group_new (guint id)
-{
- CellGroup *group = g_slice_new0 (CellGroup);
-
- group->id = id;
-
- return group;
-}
-
-static void
-cell_group_free (CellGroup *group)
-{
- g_list_free (group->cells);
- g_slice_free (CellGroup, group);
-}
-
static AllocatedCell *
allocated_cell_new (GtkCellRenderer *renderer,
gint position,
return consecutive_cells;
}
-static GList *
-construct_cell_groups (GtkCellAreaBox *box)
+static void
+cell_groups_clear (GtkCellAreaBox *box)
{
- GtkCellAreaBoxPrivate *priv = box->priv;
- CellGroup *group;
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ gint i;
+
+ for (i = 0; i < priv->groups->len; i++)
+ {
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
+
+ g_list_free (group->cells);
+ }
+
+ g_array_set_size (priv->groups, 0);
+}
+
+static void
+cell_groups_rebuild (GtkCellAreaBox *box)
+{
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ CellGroup group = { 0, };
+ CellGroup *group_ptr;
GList *cells, *l;
- GList *groups = NULL;
guint id = 0;
+ cell_groups_clear (box);
+
if (!priv->cells)
- return NULL;
+ return;
+
+ cells = list_consecutive_cells (box);
- cells = list_consecutive_cells (box);
- group = cell_group_new (id++);
- groups = g_list_prepend (groups, group);
+ /* First group is implied */
+ g_array_append_val (priv->groups, group);
+ group_ptr = &g_array_index (priv->groups, CellGroup, id);
for (l = cells; l; l = l->next)
{
/* A new group starts with any aligned cell, the first group is implied */
if (info->align && l != cells)
{
- group = cell_group_new (id++);
- groups = g_list_prepend (groups, group);
+ memset (&group, 0x0, sizeof (CellGroup));
+ group.id = ++id;
+
+ g_array_append_val (priv->groups, group);
+ group_ptr = &g_array_index (priv->groups, CellGroup, id);
}
- group->cells = g_list_prepend (group->cells, info);
- group->n_cells++;
+ group_ptr->cells = g_list_prepend (group_ptr->cells, info);
+ group_ptr->n_cells++;
/* A group expands if it contains any expand cells */
if (info->expand)
- group->expand_cells ++;
+ group_ptr->expand_cells++;
}
g_list_free (cells);
- for (l = cells; l; l = l->next)
+ for (id = 0; id < priv->groups->len; id++)
{
- group = l->data;
- group->cells = g_list_reverse (group->cells);
+ group_ptr = &g_array_index (priv->groups, CellGroup, id);
+
+ group_ptr->cells = g_list_reverse (group_ptr->cells);
}
- return g_list_reverse (groups);
+ /* Iters need to be updated with the new grouping information */
+ init_iter_groups (box);
+}
+
+static gint
+count_visible_cells (CellGroup *group,
+ gint *expand_cells)
+{
+ GList *l;
+ gint visible_cells = 0;
+ gint n_expand_cells = 0;
+
+ for (l = group->cells; l; l = l->next)
+ {
+ CellInfo *info = l->data;
+
+ if (gtk_cell_renderer_get_visible (info->renderer))
+ {
+ visible_cells++;
+
+ if (info->expand)
+ n_expand_cells++;
+ }
+ }
+
+ if (expand_cells)
+ *expand_cells = n_expand_cells;
+
+ return visible_cells;
}
static gint
count_expand_groups (GtkCellAreaBox *box)
{
GtkCellAreaBoxPrivate *priv = box->priv;
- GList *l;
+ gint i;
gint expand_groups = 0;
- for (l = priv->groups; l; l = l->next)
+ for (i = 0; i < priv->groups->len; i++)
{
- CellGroup *group = l->data;
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
if (group->expand_cells > 0)
expand_groups++;
GtkCellAreaBoxIter *iter)
{
GtkCellAreaBoxPrivate *priv = box->priv;
- gint n_groups, *expand_groups, i;
- GList *l;
+ gint *expand_groups, i;
- n_groups = g_list_length (priv->groups);
- expand_groups = g_new (gboolean, n_groups);
+ expand_groups = g_new (gboolean, priv->groups->len);
- for (i = 0, l = priv->groups; l; l = l->next, i++)
+ for (i = 0; i < priv->groups->len; i++)
{
- CellGroup *group = l->data;
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
expand_groups[i] = (group->expand_cells > 0);
}
- gtk_cell_area_box_init_groups (iter, n_groups, expand_groups);
+ /* This call implies flushing the request info */
+ gtk_cell_area_box_init_groups (iter, priv->groups->len, expand_groups);
g_free (expand_groups);
}
GtkWidget *widget)
{
const GtkCellAreaBoxAllocation *group_allocs;
+ GtkCellArea *area = GTK_CELL_AREA (box);
GtkCellAreaBoxPrivate *priv = box->priv;
- GList *group_list, *cell_list;
+ GList *cell_list;
GSList *allocated_cells = NULL;
gint i, j, n_allocs;
return NULL;
}
- for (i = 0, group_list = priv->groups; group_list; i++, group_list = group_list->next)
+ for (i = 0; i < n_allocs; i++)
{
- CellGroup *group = group_list->data;
+ /* We dont always allocate all groups, sometimes the requested group has only invisible
+ * cells for every row, hence the usage of group_allocs[i].group_idx here
+ */
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, group_allocs[i].group_idx);
/* Exception for single cell groups */
if (group->n_cells == 1)
}
else
{
- GtkRequestedSize *sizes = g_new (GtkRequestedSize, group->n_cells);
- gint avail_size = group_allocs[i].size;
- gint position = group_allocs[i].position;
+ GtkRequestedSize *sizes;
+ gint avail_size, position;
+ gint visible_cells, expand_cells;
gint extra_size, extra_extra;
- for (j = 0, cell_list = group->cells; cell_list; j++, cell_list = cell_list->next)
+ visible_cells = count_visible_cells (group, &expand_cells);
+
+ /* If this row has no visible cells in this group, just
+ * skip the allocation */
+ if (visible_cells == 0)
+ continue;
+
+ /* Offset the allocation to the group position and allocate into
+ * the group's available size */
+ position = group_allocs[i].position;
+ avail_size = group_allocs[i].size;
+
+ sizes = g_new (GtkRequestedSize, visible_cells);
+
+ for (j = 0, cell_list = group->cells; cell_list; cell_list = cell_list->next)
{
CellInfo *info = cell_list->data;
- get_renderer_size (info->renderer,
- priv->orientation,
- widget, -1,
- &sizes[j].minimum_size,
- &sizes[j].natural_size);
+ if (!gtk_cell_renderer_get_visible (info->renderer))
+ continue;
+
+ gtk_cell_area_request_renderer (area, info->renderer,
+ priv->orientation,
+ widget, -1,
+ &sizes[j].minimum_size,
+ &sizes[j].natural_size);
+
+ sizes[j].data = info;
+ avail_size -= sizes[j].minimum_size;
- avail_size -= sizes[j].minimum_size;
+ j++;
}
/* Distribute cells naturally within the group */
- avail_size -= (group->n_cells - 1) * priv->spacing;
- avail_size = gtk_distribute_natural_allocation (avail_size, group->n_cells, sizes);
+ avail_size -= (visible_cells - 1) * priv->spacing;
+ avail_size = gtk_distribute_natural_allocation (avail_size, visible_cells, sizes);
/* Calculate/distribute expand for cells */
- if (group->expand_cells > 0)
+ if (expand_cells > 0)
{
- extra_size = avail_size / group->expand_cells;
- extra_extra = avail_size % group->expand_cells;
+ extra_size = avail_size / expand_cells;
+ extra_extra = avail_size % expand_cells;
}
else
extra_size = extra_extra = 0;
- /* Create the allocated cells */
- for (j = 0, cell_list = group->cells; cell_list; j++, cell_list = cell_list->next)
+ /* Create the allocated cells (loop only over visible cells here) */
+ for (j = 0; j < visible_cells; j++)
{
- CellInfo *info = cell_list->data;
+ CellInfo *info = sizes[j].data;
AllocatedCell *cell;
if (info->expand)
}
}
+ /* Note it might not be important to reverse the list here at all,
+ * we have the correct positions, no need to allocate from left to right */
return g_slist_reverse (allocated_cells);
}
GtkCellAreaBoxPrivate *priv = box->priv;
GSList *l;
+ /* Unref/free the iter list */
for (l = priv->iters; l; l = l->next)
g_object_weak_unref (G_OBJECT (l->data), (GWeakNotify)iter_weak_notify, box);
g_slist_free (priv->iters);
+ priv->iters = NULL;
+ /* Free the cell grouping info */
+ cell_groups_clear (box);
+ g_array_free (priv->groups, TRUE);
+
G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->finalize (object);
}
priv->cells = g_list_delete_link (priv->cells, node);
/* Reconstruct cell groups */
- g_list_foreach (priv->groups, (GFunc)cell_group_free, NULL);
- g_list_free (priv->groups);
- priv->groups = construct_cell_groups (box);
-
- /* Reinitialize groups on iters */
- init_iter_groups (box);
+ cell_groups_rebuild (box);
}
else
g_warning ("Trying to remove a cell renderer that is not present GtkCellAreaBox");
}
}
+static void
+gtk_cell_area_box_get_cell_allocation (GtkCellArea *area,
+ GtkCellAreaIter *iter,
+ GtkWidget *widget,
+ GtkCellRenderer *renderer,
+ const GdkRectangle *cell_area,
+ GdkRectangle *allocation)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GtkCellAreaBoxIter *box_iter = GTK_CELL_AREA_BOX_ITER (iter);
+ GSList *allocated_cells, *l;
+
+ *allocation = *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_iter, widget);
+
+ for (l = allocated_cells; l; l = l->next)
+ {
+ AllocatedCell *cell = l->data;
+
+ if (cell->renderer == renderer)
+ {
+ 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;
+ }
+ }
+
+ 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,
- GtkCellAreaIter *iter,
- GtkWidget *widget,
- GdkEvent *event,
- const GdkRectangle *cell_area)
+gtk_cell_area_box_event (GtkCellArea *area,
+ GtkCellAreaIter *iter,
+ 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, iter, 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;
+ GtkCellAreaBoxIter *box_iter = GTK_CELL_AREA_BOX_ITER (iter);
+ GSList *allocated_cells, *l;
+ GdkRectangle cell_background, inner_area;
+ GtkAllocation allocation;
+ gint event_x, event_y;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ /* 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_iter, widget);
+
+ 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, &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;
+ if (gtk_cell_renderer_can_focus (cell->renderer))
+ event_renderer = cell->renderer;
+ else
+ {
+ GtkCellRenderer *focus_renderer;
+
+ /* A renderer can have focus siblings but that renderer might not be
+ * focusable for every row... so we go on to check can_focus here. */
+ focus_renderer = gtk_cell_area_get_focus_from_sibling (area, cell->renderer);
+
+ if (focus_renderer && gtk_cell_renderer_can_focus (focus_renderer))
+ event_renderer = focus_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, 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 0;
+ return retval;
}
static void
-gtk_cell_area_box_render (GtkCellArea *area,
- GtkCellAreaIter *iter,
- GtkWidget *widget,
- cairo_t *cr,
- const GdkRectangle *cell_area)
+gtk_cell_area_box_render (GtkCellArea *area,
+ GtkCellAreaIter *iter,
+ GtkWidget *widget,
+ cairo_t *cr,
+ const GdkRectangle *background_area,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags,
+ gboolean paint_focus)
{
GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
GtkCellAreaBoxIter *box_iter = GTK_CELL_AREA_BOX_ITER (iter);
GSList *allocated_cells, *l;
+ GdkRectangle cell_background, inner_area;
+ GtkCellRenderer *focus_cell = NULL;
+ GdkRectangle focus_rect = { 0, };
+ gboolean first_focus_cell = TRUE;
+
+ if (flags & GTK_CELL_RENDERER_FOCUSED)
+ {
+ focus_cell = gtk_cell_area_get_focus_cell (area);
+ flags &= ~GTK_CELL_RENDERER_FOCUSED;
+ }
+
+ cell_background = *cell_area;
/* Get a list of cells with allocation sizes decided regardless
* of alignments and pack order etc. */
for (l = allocated_cells; l; l = l->next)
{
- AllocatedCell *cell = l->data;
+ AllocatedCell *cell = l->data;
+ GtkCellRendererState cell_fields = 0;
+ 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, &cell_background, &inner_area);
+
+ /* Here after getting the inner area of the cell background,
+ * add portions of the background area to the cell background */
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ if (l == allocated_cells)
+ {
+ cell_background.width += cell_background.x - background_area->x;
+ cell_background.x = background_area->x;
+ }
+ if (l->next == NULL)
+ cell_background.width =
+ background_area->width - (cell_background.x - background_area->x);
+
+ cell_background.y = background_area->y;
+ cell_background.height = background_area->height;
+ }
+ else
+ {
+ if (l == allocated_cells)
+ {
+ cell_background.height += cell_background.y - background_area->y;
+ cell_background.y = background_area->y;
+ }
+
+ if (l->next == NULL)
+ cell_background.height =
+ background_area->height - (cell_background.y - background_area->y);
+
+ cell_background.x = background_area->x;
+ cell_background.width = background_area->width;
+ }
+
+ if (focus_cell &&
+ (cell->renderer == focus_cell ||
+ gtk_cell_area_is_focus_sibling (area, focus_cell, cell->renderer)))
+ {
+ cell_fields |= GTK_CELL_RENDERER_FOCUSED;
+
+ if (paint_focus)
+ {
+ GdkRectangle cell_focus;
+ gint opposite_size, x_offset, y_offset;
+
+ cell_focus = inner_area;
+
+ /* Trim up the focus size */
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ gtk_cell_renderer_get_preferred_height_for_width (cell->renderer, widget,
+ cell_focus.width,
+ NULL, &opposite_size);
+
+ cell_focus.height = MIN (opposite_size, cell_focus.height);
+ }
+ else
+ {
+ gtk_cell_renderer_get_preferred_width_for_height (cell->renderer, widget,
+ cell_focus.height,
+ NULL, &opposite_size);
+
+ cell_focus.width = MIN (opposite_size, cell_focus.width);
+ }
+
+ /* offset the cell position */
+ _gtk_cell_renderer_calc_offset (cell->renderer, &inner_area, GTK_TEXT_DIR_LTR,
+ cell_focus.width, cell_focus.height,
+ &x_offset, &y_offset);
+
+ cell_focus.x += x_offset;
+ cell_focus.y += y_offset;
+
+ /* Accumulate the focus rectangle for all focus siblings */
+ if (first_focus_cell)
+ {
+ focus_rect = cell_focus;
+ first_focus_cell = FALSE;
+ }
+ else
+ gdk_rectangle_union (&focus_rect, &cell_focus, &focus_rect);
+ }
+ }
+
+ /* We have to do some per-cell considerations for the 'flags'
+ * for focus handling */
+ gtk_cell_renderer_render (cell->renderer, cr, widget,
+ &cell_background, &inner_area,
+ flags | cell_fields);
}
+ if (paint_focus && focus_rect.width != 0 && focus_rect.height != 0)
+ {
+ GtkStateType renderer_state =
+ flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
+ (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
+ (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL));
+
+ gtk_paint_focus (gtk_widget_get_style (widget), cr,
+ renderer_state, widget,
+ gtk_cell_area_get_style_detail (area),
+ focus_rect.x, focus_rect.y,
+ focus_rect.width, focus_rect.height);
+ }
+
+
g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL);
g_slist_free (allocated_cells);
}
+static void
+gtk_cell_area_box_set_cell_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GList *node;
+ CellInfo *info;
+ gboolean rebuild = FALSE;
+ gboolean val;
+ GtkPackType pack_type;
+
+ node = g_list_find_custom (priv->cells, renderer,
+ (GCompareFunc)cell_info_find);
+ if (!node)
+ return;
+
+ info = node->data;
+
+ switch (prop_id)
+ {
+ case CELL_PROP_EXPAND:
+ val = g_value_get_boolean (value);
+
+ if (info->expand != val)
+ {
+ info->expand = val;
+ rebuild = TRUE;
+ }
+ break;
+
+ case CELL_PROP_ALIGN:
+ val = g_value_get_boolean (value);
+
+ if (info->align != val)
+ {
+ info->align = val;
+ rebuild = TRUE;
+ }
+ break;
+
+ case CELL_PROP_PACK_TYPE:
+ pack_type = g_value_get_enum (value);
+
+ if (info->pack != pack_type)
+ {
+ info->pack = pack_type;
+ rebuild = TRUE;
+ }
+ break;
+ default:
+ GTK_CELL_AREA_WARN_INVALID_CHILD_PROPERTY_ID (area, prop_id, pspec);
+ break;
+ }
+
+ /* Groups need to be rebuilt */
+ if (rebuild)
+ cell_groups_rebuild (box);
+}
+
+static void
+gtk_cell_area_box_get_cell_property (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ GList *node;
+ CellInfo *info;
+
+ node = g_list_find_custom (priv->cells, renderer,
+ (GCompareFunc)cell_info_find);
+ if (!node)
+ return;
+
+ info = node->data;
+
+ switch (prop_id)
+ {
+ case CELL_PROP_EXPAND:
+ g_value_set_boolean (value, info->expand);
+ break;
+
+ case CELL_PROP_ALIGN:
+ g_value_set_boolean (value, info->align);
+ break;
+
+ case CELL_PROP_PACK_TYPE:
+ g_value_set_enum (value, info->pack);
+ break;
+ default:
+ GTK_CELL_AREA_WARN_INVALID_CHILD_PROPERTY_ID (area, prop_id, pspec);
+ break;
+ }
+}
+
+
static GtkCellAreaIter *
gtk_cell_area_box_create_iter (GtkCellArea *area)
{
GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
GtkCellAreaBoxPrivate *priv = box->priv;
GtkCellAreaIter *iter =
- (GtkCellAreaIter *)g_object_new (GTK_TYPE_CELL_AREA_BOX_ITER, NULL);
+ (GtkCellAreaIter *)g_object_new (GTK_TYPE_CELL_AREA_BOX_ITER,
+ "area", area, NULL);
priv->iters = g_slist_prepend (priv->iters, iter);
GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
}
-static void
-get_renderer_size (GtkCellRenderer *renderer,
- GtkOrientation orientation,
- GtkWidget *widget,
- gint for_size,
- gint *minimum_size,
- gint *natural_size)
-{
- if (orientation == GTK_ORIENTATION_HORIZONTAL)
- {
- if (for_size < 0)
- gtk_cell_renderer_get_preferred_width (renderer, widget, minimum_size, natural_size);
- else
- gtk_cell_renderer_get_preferred_width_for_height (renderer, widget, for_size,
- minimum_size, natural_size);
- }
- else /* GTK_ORIENTATION_VERTICAL */
- {
- if (for_size < 0)
- gtk_cell_renderer_get_preferred_height (renderer, widget, minimum_size, natural_size);
- else
- gtk_cell_renderer_get_preferred_height_for_width (renderer, widget, for_size,
- minimum_size, natural_size);
- }
-}
-
static void
compute_size (GtkCellAreaBox *box,
GtkOrientation orientation,
gint *natural_size)
{
GtkCellAreaBoxPrivate *priv = box->priv;
- CellGroup *group;
- CellInfo *info;
- GList *cell_list, *group_list;
+ GtkCellArea *area = GTK_CELL_AREA (box);
+ GList *list;
+ gint i;
gint min_size = 0;
gint nat_size = 0;
- for (group_list = priv->groups; group_list; group_list = group_list->next)
+ for (i = 0; i < priv->groups->len; i++)
{
- gint group_min_size = 0;
- gint group_nat_size = 0;
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
+ gint group_min_size = 0;
+ gint group_nat_size = 0;
- group = group_list->data;
-
- for (cell_list = group->cells; cell_list; cell_list = cell_list->next)
+ for (list = group->cells; list; list = list->next)
{
- gint renderer_min_size, renderer_nat_size;
-
- info = cell_list->data;
+ CellInfo *info = list->data;
+ gint renderer_min_size, renderer_nat_size;
+
+ if (!gtk_cell_renderer_get_visible (info->renderer))
+ continue;
- get_renderer_size (info->renderer, orientation, widget, for_size,
- &renderer_min_size, &renderer_nat_size);
+ gtk_cell_area_request_renderer (area, info->renderer, orientation, widget, for_size,
+ &renderer_min_size, &renderer_nat_size);
if (orientation == priv->orientation)
{
}
GtkRequestedSize *
-get_group_sizes (CellGroup *group,
+get_group_sizes (GtkCellArea *area,
+ CellGroup *group,
GtkOrientation orientation,
GtkWidget *widget,
gint *n_sizes)
GList *l;
gint i;
- *n_sizes = g_list_length (group->cells);
+ *n_sizes = count_visible_cells (group, NULL);
sizes = g_new (GtkRequestedSize, *n_sizes);
- for (l = group->cells, i = 0; l; l = l->next, i++)
+ for (l = group->cells, i = 0; l; l = l->next)
{
CellInfo *info = l->data;
+ if (!gtk_cell_renderer_get_visible (info->renderer))
+ continue;
+
sizes[i].data = info;
- get_renderer_size (info->renderer,
- orientation, widget, -1,
- &sizes[i].minimum_size,
- &sizes[i].natural_size);
+ gtk_cell_area_request_renderer (area, info->renderer,
+ orientation, widget, -1,
+ &sizes[i].minimum_size,
+ &sizes[i].natural_size);
+
+ i++;
}
return sizes;
gint *natural_size)
{
GtkCellAreaBoxPrivate *priv = box->priv;
+ GtkCellArea *area = GTK_CELL_AREA (box);
/* Exception for single cell groups */
if (group->n_cells == 1)
{
CellInfo *info = group->cells->data;
- get_renderer_size (info->renderer,
- OPPOSITE_ORIENTATION (priv->orientation),
- widget, for_size, minimum_size, natural_size);
+ gtk_cell_area_request_renderer (area, info->renderer,
+ OPPOSITE_ORIENTATION (priv->orientation),
+ widget, for_size, minimum_size, natural_size);
}
else
{
gint extra_size, extra_extra;
gint min_size = 0, nat_size = 0;
- orientation_sizes = get_group_sizes (group, priv->orientation, widget, &n_sizes);
+ orientation_sizes = get_group_sizes (area, group, priv->orientation, widget, &n_sizes);
/* First naturally allocate the cells in the group into the for_size */
avail_size -= (n_sizes - 1) * priv->spacing;
}
}
- get_renderer_size (info->renderer,
- OPPOSITE_ORIENTATION (priv->orientation),
- widget,
- orientation_sizes[i].minimum_size,
- &cell_min, &cell_nat);
+ gtk_cell_area_request_renderer (area, info->renderer,
+ OPPOSITE_ORIENTATION (priv->orientation),
+ widget,
+ orientation_sizes[i].minimum_size,
+ &cell_min, &cell_nat);
min_size = MAX (min_size, cell_min);
nat_size = MAX (nat_size, cell_nat);
{
GtkCellAreaBoxPrivate *priv = box->priv;
CellGroup *group;
- GList *group_list;
GtkRequestedSize *orientation_sizes;
gint n_groups, n_expand_groups, i;
gint avail_size = for_size;
* and push the height-for-width for each group accordingly while accumulating
* the overall height-for-width for this row.
*/
- for (group_list = priv->groups; group_list; group_list = group_list->next)
+ for (i = 0; i < n_groups; i++)
{
gint group_min, group_nat;
-
- group = group_list->data;
+ gint group_idx = GPOINTER_TO_INT (orientation_sizes[i].data);
+
+ group = &g_array_index (priv->groups, CellGroup, group_idx);
if (group->expand_cells > 0)
{
- orientation_sizes[group->id].minimum_size += extra_size;
+ orientation_sizes[i].minimum_size += extra_size;
if (extra_extra)
{
- orientation_sizes[group->id].minimum_size++;
+ orientation_sizes[i].minimum_size++;
extra_extra--;
}
}
/* Now we have the allocation for the group, request it's height-for-width */
compute_group_size_for_opposing_orientation (box, group, widget,
- orientation_sizes[group->id].minimum_size,
+ orientation_sizes[i].minimum_size,
&group_min, &group_nat);
min_size = MAX (min_size, group_min);
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
{
- gtk_cell_area_box_iter_push_group_height_for_width (iter, group->id, for_size,
+ gtk_cell_area_box_iter_push_group_height_for_width (iter, group_idx, for_size,
group_min, group_nat);
}
else
{
- gtk_cell_area_box_iter_push_group_width_for_height (iter, group->id, for_size,
+ gtk_cell_area_box_iter_push_group_width_for_height (iter, group_idx, for_size,
group_min, group_nat);
}
}
*natural_width = nat_width;
}
+static gboolean
+gtk_cell_area_box_focus (GtkCellArea *area,
+ GtkDirectionType direction)
+{
+ GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
+ GtkCellAreaBoxPrivate *priv = box->priv;
+ gint cycle = FOCUS_NONE;
+ gboolean cycled_focus = FALSE;
+ GtkCellRenderer *focus_cell;
+
+ focus_cell = gtk_cell_area_get_focus_cell (area);
+
+ switch (direction)
+ {
+ case GTK_DIR_TAB_FORWARD:
+ cycle = FOCUS_NEXT;
+ break;
+ case GTK_DIR_TAB_BACKWARD:
+ cycle = FOCUS_PREV;
+ break;
+ case GTK_DIR_UP:
+ if (priv->orientation == GTK_ORIENTATION_VERTICAL || !focus_cell)
+ cycle = FOCUS_PREV;
+ break;
+ case GTK_DIR_DOWN:
+ if (priv->orientation == GTK_ORIENTATION_VERTICAL || !focus_cell)
+ cycle = FOCUS_NEXT;
+ break;
+ case GTK_DIR_LEFT:
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !focus_cell)
+ cycle = FOCUS_PREV;
+ break;
+ case GTK_DIR_RIGHT:
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL || !focus_cell)
+ cycle = FOCUS_NEXT;
+ break;
+ default:
+ break;
+ }
+
+ if (cycle != FOCUS_NONE)
+ {
+ gboolean found_cell = FALSE;
+ GList *list;
+ gint i;
+
+ /* If there is no focused cell, focus on the first (or last) one in the list */
+ if (!focus_cell)
+ found_cell = TRUE;
+
+ for (i = (cycle == FOCUS_NEXT) ? 0 : priv->groups->len -1;
+ cycled_focus == FALSE && i >= 0 && i < priv->groups->len;
+ i = (cycle == FOCUS_NEXT) ? i + 1 : i - 1)
+ {
+ CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
+
+ for (list = (cycle == FOCUS_NEXT) ? g_list_first (group->cells) : g_list_last (group->cells);
+ list; list = (cycle == FOCUS_NEXT) ? list->next : list->prev)
+ {
+ CellInfo *info = list->data;
+
+ if (info->renderer == focus_cell)
+ found_cell = TRUE;
+ else if (found_cell)
+ {
+ if (gtk_cell_renderer_can_focus (info->renderer))
+ {
+ gtk_cell_area_set_focus_cell (area, info->renderer);
+
+ cycled_focus = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (!cycled_focus)
+ gtk_cell_area_set_focus_cell (area, NULL);
+
+ return cycled_focus;
+}
+
/*************************************************************
* GtkCellLayoutIface *
priv->cells = g_list_delete_link (priv->cells, node);
priv->cells = g_list_insert (priv->cells, info, position);
- /* Reconstruct cell groups */
- g_list_foreach (priv->groups, (GFunc)cell_group_free, NULL);
- g_list_free (priv->groups);
- priv->groups = construct_cell_groups (box);
-
- /* Reinitialize groups on iters */
- init_iter_groups (box);
+ cell_groups_rebuild (box);
}
}
priv->cells = g_list_append (priv->cells, info);
- /* Reconstruct cell groups */
- g_list_foreach (priv->groups, (GFunc)cell_group_free, NULL);
- g_list_free (priv->groups);
- priv->groups = construct_cell_groups (box);
-
- /* Reinitialize groups on iters */
- init_iter_groups (box);
+ cell_groups_rebuild (box);
}
void
priv->cells = g_list_append (priv->cells, info);
- /* Reconstruct cell groups */
- g_list_foreach (priv->groups, (GFunc)cell_group_free, NULL);
- g_list_free (priv->groups);
- priv->groups = construct_cell_groups (box);
-
- /* Reinitialize groups on iters */
- init_iter_groups (box);
+ cell_groups_rebuild (box);
}
gint