gtkcellareabox.c 76.1 KB
Newer Older
1
/* gtkcellareabox.c
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * Copyright (C) 2010 Openismus GmbH
 *
 * Authors:
 *      Tristan Van Berkom <tristanvb@openismus.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Javier Jardón's avatar
Javier Jardón committed
19
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 21
 */

22 23 24

/**
 * SECTION:gtkcellareabox
Matthias Clasen's avatar
Matthias Clasen committed
25 26
 * @Short_Description: A cell area that renders GtkCellRenderers
 *     into a row or a column
27 28
 * @Title: GtkCellAreaBox
 *
Matthias Clasen's avatar
Matthias Clasen committed
29 30
 * The #GtkCellAreaBox renders cell renderers into a row or a column
 * depending on its #GtkOrientation.
31
 *
32
 * GtkCellAreaBox uses a notion of packing. Packing
Matthias Clasen's avatar
Matthias Clasen committed
33
 * refers to adding cell renderers with reference to a particular position
34
 * in a #GtkCellAreaBox. There are two reference positions: the
35
 * start and the end of the box.
Matthias Clasen's avatar
Matthias Clasen committed
36 37 38 39 40
 * When the #GtkCellAreaBox is oriented in the %GTK_ORIENTATION_VERTICAL
 * orientation, the start is defined as the top of the box and the end is
 * defined as the bottom. In the %GTK_ORIENTATION_HORIZONTAL orientation
 * start is defined as the left side and the end is defined as the right
 * side.
41
 *
Matthias Clasen's avatar
Matthias Clasen committed
42
 * Alignments of #GtkCellRenderers rendered in adjacent rows can be
43
 * configured by configuring the #GtkCellAreaBox align child cell property
Matthias Clasen's avatar
Matthias Clasen committed
44 45
 * with gtk_cell_area_cell_set_property() or by specifying the "align"
 * argument to gtk_cell_area_box_pack_start() and gtk_cell_area_box_pack_end().
46 47
 */

48 49
#include "config.h"
#include "gtkintl.h"
50
#include "gtkorientable.h"
51
#include "gtkcelllayout.h"
52
#include "gtkcellareabox.h"
53
#include "gtkcellareaboxcontextprivate.h"
54
#include "gtktypebuiltins.h"
55 56
#include "gtkprivate.h"

57 58

/* GObjectClass */
59 60 61
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,
Matthias Clasen's avatar
Matthias Clasen committed
62 63 64
                                                                   guint                 prop_id,
                                                                   const GValue         *value,
                                                                   GParamSpec           *pspec);
65
static void      gtk_cell_area_box_get_property                   (GObject              *object,
Matthias Clasen's avatar
Matthias Clasen committed
66 67 68
                                                                   guint                 prop_id,
                                                                   GValue               *value,
                                                                   GParamSpec           *pspec);
69 70

/* GtkCellAreaClass */
71
static void      gtk_cell_area_box_add                            (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
72
                                                                   GtkCellRenderer      *renderer);
73
static void      gtk_cell_area_box_remove                         (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
74
                                                                   GtkCellRenderer      *renderer);
75
static void      gtk_cell_area_box_foreach                        (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
76 77
                                                                   GtkCellCallback       callback,
                                                                   gpointer              callback_data);
78
static void      gtk_cell_area_box_foreach_alloc                  (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
79 80 81 82 83 84
                                                                   GtkCellAreaContext   *context,
                                                                   GtkWidget            *widget,
                                                                   const GdkRectangle   *cell_area,
                                                                   const GdkRectangle   *background_area,
                                                                   GtkCellAllocCallback  callback,
                                                                   gpointer              callback_data);
85 86 87 88 89
static void      gtk_cell_area_box_apply_attributes               (GtkCellArea          *area,
								   GtkTreeModel         *tree_model,
								   GtkTreeIter          *iter,
								   gboolean              is_expander,
								   gboolean              is_expanded);
90
static void      gtk_cell_area_box_set_cell_property              (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
91 92 93 94
                                                                   GtkCellRenderer      *renderer,
                                                                   guint                 prop_id,
                                                                   const GValue         *value,
                                                                   GParamSpec           *pspec);
95
static void      gtk_cell_area_box_get_cell_property              (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
96 97 98 99
                                                                   GtkCellRenderer      *renderer,
                                                                   guint                 prop_id,
                                                                   GValue               *value,
                                                                   GParamSpec           *pspec);
100
static GtkCellAreaContext *gtk_cell_area_box_create_context       (GtkCellArea          *area);
101
static GtkCellAreaContext *gtk_cell_area_box_copy_context         (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
102
                                                                   GtkCellAreaContext   *context);
103 104
static GtkSizeRequestMode  gtk_cell_area_box_get_request_mode     (GtkCellArea          *area);
static void      gtk_cell_area_box_get_preferred_width            (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
105 106 107 108
                                                                   GtkCellAreaContext   *context,
                                                                   GtkWidget            *widget,
                                                                   gint                 *minimum_width,
                                                                   gint                 *natural_width);
109
static void      gtk_cell_area_box_get_preferred_height           (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
110 111 112 113
                                                                   GtkCellAreaContext   *context,
                                                                   GtkWidget            *widget,
                                                                   gint                 *minimum_height,
                                                                   gint                 *natural_height);
114
static void      gtk_cell_area_box_get_preferred_height_for_width (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
115 116 117 118 119
                                                                   GtkCellAreaContext   *context,
                                                                   GtkWidget            *widget,
                                                                   gint                  width,
                                                                   gint                 *minimum_height,
                                                                   gint                 *natural_height);
120
static void      gtk_cell_area_box_get_preferred_width_for_height (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
121 122 123 124 125
                                                                   GtkCellAreaContext   *context,
                                                                   GtkWidget            *widget,
                                                                   gint                  height,
                                                                   gint                 *minimum_width,
                                                                   gint                 *natural_width);
126
static gboolean  gtk_cell_area_box_focus                          (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
127
                                                                   GtkDirectionType      direction);
128

129 130 131
/* GtkCellLayoutIface */
static void      gtk_cell_area_box_cell_layout_init               (GtkCellLayoutIface *iface);
static void      gtk_cell_area_box_layout_pack_start              (GtkCellLayout      *cell_layout,
Matthias Clasen's avatar
Matthias Clasen committed
132 133
                                                                   GtkCellRenderer    *renderer,
                                                                   gboolean            expand);
134
static void      gtk_cell_area_box_layout_pack_end                (GtkCellLayout      *cell_layout,
Matthias Clasen's avatar
Matthias Clasen committed
135 136
                                                                   GtkCellRenderer    *renderer,
                                                                   gboolean            expand);
137
static void      gtk_cell_area_box_layout_reorder                 (GtkCellLayout      *cell_layout,
Matthias Clasen's avatar
Matthias Clasen committed
138 139
                                                                   GtkCellRenderer    *renderer,
                                                                   gint                position);
140
static void      gtk_cell_area_box_focus_changed                  (GtkCellArea        *area,
Matthias Clasen's avatar
Matthias Clasen committed
141 142
                                                                   GParamSpec         *pspec,
                                                                   GtkCellAreaBox     *box);
143 144


145
/* CellInfo/CellGroup metadata handling and convenience functions */
146 147 148
typedef struct {
  GtkCellRenderer *renderer;

149
  guint            expand : 1; /* Whether the cell expands */
Matthias Clasen's avatar
Matthias Clasen committed
150 151
  guint            pack   : 1; /* Whether it is packed from the start or end */
  guint            align  : 1; /* Whether to align its position with adjacent rows */
152
  guint            fixed  : 1; /* Whether to require the same size for all rows */
153 154
} CellInfo;

155 156 157
typedef struct {
  GList *cells;

158 159 160
  guint  id           : 8;
  guint  n_cells      : 8;
  guint  expand_cells : 8;
161 162
  guint  align        : 1;
  guint  visible      : 1;
163 164
} CellGroup;

165 166 167 168 169 170 171
typedef struct {
  GtkCellRenderer *renderer;

  gint             position;
  gint             size;
} AllocatedCell;

Matthias Clasen's avatar
Matthias Clasen committed
172 173 174
static CellInfo      *cell_info_new          (GtkCellRenderer       *renderer,
                                              GtkPackType            pack,
                                              gboolean               expand,
175 176
                                              gboolean               align,
					      gboolean               fixed);
177 178
static void           cell_info_free         (CellInfo              *info);
static gint           cell_info_find         (CellInfo              *info,
Matthias Clasen's avatar
Matthias Clasen committed
179
                                              GtkCellRenderer       *renderer);
180 181

static AllocatedCell *allocated_cell_new     (GtkCellRenderer       *renderer,
Matthias Clasen's avatar
Matthias Clasen committed
182 183
                                              gint                   position,
                                              gint                   size);
184 185 186 187
static void           allocated_cell_free    (AllocatedCell         *cell);
static GList         *list_consecutive_cells (GtkCellAreaBox        *box);
static gint           count_expand_groups    (GtkCellAreaBox        *box);
static void           context_weak_notify    (GtkCellAreaBox        *box,
Matthias Clasen's avatar
Matthias Clasen committed
188
                                              GtkCellAreaBoxContext *dead_context);
189
static void           reset_contexts         (GtkCellAreaBox        *box);
190 191
static void           init_context_groups    (GtkCellAreaBox        *box);
static void           init_context_group     (GtkCellAreaBox        *box,
Matthias Clasen's avatar
Matthias Clasen committed
192
                                              GtkCellAreaBoxContext *context);
193
static GSList        *get_allocated_cells    (GtkCellAreaBox        *box,
Matthias Clasen's avatar
Matthias Clasen committed
194 195 196 197
                                              GtkCellAreaBoxContext *context,
                                              GtkWidget             *widget,
                                              gint                   width,
                                              gint                   height);
198

199 200 201

struct _GtkCellAreaBoxPrivate
{
202 203
  /* We hold on to the previously focused cell when navigating
   * up and down in a horizontal box (or left and right on a vertical one)
Matthias Clasen's avatar
Matthias Clasen committed
204 205
   * this way we always re-enter the last focused cell.
   */
206 207
  GtkCellRenderer *last_focus_cell;
  gulong           focus_cell_id;
208

209 210
  GList           *cells;
  GArray          *groups;
211

212 213
  GSList          *contexts;

214
  GtkOrientation   orientation;
215
  gint             spacing;
216 217

  /* We hold on to the rtl state from a widget we are requested for
Matthias Clasen's avatar
Matthias Clasen committed
218 219
   * so that we can navigate focus correctly
   */
220
  gboolean         rtl;
221 222 223 224
};

enum {
  PROP_0,
225
  PROP_ORIENTATION,
226
  PROP_SPACING
227 228
};

229 230 231 232
enum {
  CELL_PROP_0,
  CELL_PROP_EXPAND,
  CELL_PROP_ALIGN,
233
  CELL_PROP_FIXED_SIZE,
234 235 236
  CELL_PROP_PACK_TYPE
};

237
G_DEFINE_TYPE_WITH_CODE (GtkCellAreaBox, gtk_cell_area_box, GTK_TYPE_CELL_AREA,
238
                         G_ADD_PRIVATE (GtkCellAreaBox)
Matthias Clasen's avatar
Matthias Clasen committed
239 240
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
                                                gtk_cell_area_box_cell_layout_init)
241
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
242 243 244 245 246 247

static void
gtk_cell_area_box_init (GtkCellAreaBox *box)
{
  GtkCellAreaBoxPrivate *priv;

248
  box->priv = gtk_cell_area_box_get_instance_private (box);
249 250 251
  priv = box->priv;

  priv->orientation = GTK_ORIENTATION_HORIZONTAL;
252
  priv->groups      = g_array_new (FALSE, TRUE, sizeof (CellGroup));
253
  priv->cells       = NULL;
254
  priv->contexts    = NULL;
255
  priv->spacing     = 0;
256
  priv->rtl         = FALSE;
257 258

  /* Watch whenever focus is given to a cell, even if it's not with keynav,
Matthias Clasen's avatar
Matthias Clasen committed
259 260 261 262 263
   * this way we remember upon entry of the area where focus was last time
   * around
   */
  priv->focus_cell_id = g_signal_connect (box, "notify::focus-cell",
                                          G_CALLBACK (gtk_cell_area_box_focus_changed), box);
264 265
}

Matthias Clasen's avatar
Matthias Clasen committed
266
static void
267 268 269 270 271 272 273 274 275 276 277 278
gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class)
{
  GObjectClass     *object_class = G_OBJECT_CLASS (class);
  GtkCellAreaClass *area_class   = GTK_CELL_AREA_CLASS (class);

  /* GObjectClass */
  object_class->finalize     = gtk_cell_area_box_finalize;
  object_class->dispose      = gtk_cell_area_box_dispose;
  object_class->set_property = gtk_cell_area_box_set_property;
  object_class->get_property = gtk_cell_area_box_get_property;

  /* GtkCellAreaClass */
279 280
  area_class->add                 = gtk_cell_area_box_add;
  area_class->remove              = gtk_cell_area_box_remove;
281
  area_class->foreach             = gtk_cell_area_box_foreach;
282
  area_class->foreach_alloc       = gtk_cell_area_box_foreach_alloc;
283
  area_class->apply_attributes    = gtk_cell_area_box_apply_attributes;
284 285
  area_class->set_cell_property   = gtk_cell_area_box_set_cell_property;
  area_class->get_cell_property   = gtk_cell_area_box_get_cell_property;
Matthias Clasen's avatar
Matthias Clasen committed
286

287
  area_class->create_context                 = gtk_cell_area_box_create_context;
288
  area_class->copy_context                   = gtk_cell_area_box_copy_context;
289 290 291 292 293 294
  area_class->get_request_mode               = gtk_cell_area_box_get_request_mode;
  area_class->get_preferred_width            = gtk_cell_area_box_get_preferred_width;
  area_class->get_preferred_height           = gtk_cell_area_box_get_preferred_height;
  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;

295
  area_class->focus = gtk_cell_area_box_focus;
296

297
  /* Properties */
298 299
  g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");

300 301 302 303 304 305 306
  /**
   * GtkCellAreaBox:spacing:
   *
   * The amount of space to reserve between cells.
   *
   * Since: 3.0
   */
307 308 309
  g_object_class_install_property (object_class,
                                   PROP_SPACING,
                                   g_param_spec_int ("spacing",
Matthias Clasen's avatar
Matthias Clasen committed
310 311 312 313 314 315
                                                     P_("Spacing"),
                                                     P_("Space which is inserted between cells"),
                                                     0,
                                                     G_MAXINT,
                                                     0,
                                                     GTK_PARAM_READWRITE));
316

317
  /* Cell Properties */
318 319 320
  /**
   * GtkCellAreaBox:expand:
   *
Matthias Clasen's avatar
Matthias Clasen committed
321 322
   * Whether the cell renderer should receive extra space
   * when the area receives more than its natural size.
323 324 325
   *
   * Since: 3.0
   */
326
  gtk_cell_area_class_install_cell_property (area_class,
Matthias Clasen's avatar
Matthias Clasen committed
327 328 329 330 331 332 333 334
                                             CELL_PROP_EXPAND,
                                             g_param_spec_boolean
                                             ("expand",
                                              P_("Expand"),
                                              P_("Whether the cell expands"),
                                              FALSE,
                                              GTK_PARAM_READWRITE));

335 336 337 338 339 340 341
  /**
   * GtkCellAreaBox:align:
   *
   * Whether the cell renderer should be aligned in adjacent rows.
   *
   * Since: 3.0
   */
342
  gtk_cell_area_class_install_cell_property (area_class,
Matthias Clasen's avatar
Matthias Clasen committed
343 344 345 346 347
                                             CELL_PROP_ALIGN,
                                             g_param_spec_boolean
                                             ("align",
                                              P_("Align"),
                                              P_("Whether cell should align with adjacent rows"),
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
                                              FALSE,
                                              GTK_PARAM_READWRITE));

  /**
   * GtkCellAreaBox:fixed-size:
   *
   * Whether the cell renderer should require the same size
   * for all rows for which it was requested.
   *
   * Since: 3.0
   */
  gtk_cell_area_class_install_cell_property (area_class,
                                             CELL_PROP_FIXED_SIZE,
                                             g_param_spec_boolean
                                             ("fixed-size",
                                              P_("Fixed Size"),
                                              P_("Whether cells should be the same size in all rows"),
Matthias Clasen's avatar
Matthias Clasen committed
365 366
                                              TRUE,
                                              GTK_PARAM_READWRITE));
367

368 369 370
  /**
   * GtkCellAreaBox:pack-type:
   *
Matthias Clasen's avatar
Matthias Clasen committed
371 372
   * A GtkPackType indicating whether the cell renderer is packed
   * with reference to the start or end of the area.
373 374 375
   *
   * Since: 3.0
   */
376
  gtk_cell_area_class_install_cell_property (area_class,
Matthias Clasen's avatar
Matthias Clasen committed
377 378 379 380 381 382 383 384
                                             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));
385 386 387
}


388
/*************************************************************
389
 *    CellInfo/CellGroup basics and convenience functions    *
390 391
 *************************************************************/
static CellInfo *
Matthias Clasen's avatar
Matthias Clasen committed
392 393 394
cell_info_new  (GtkCellRenderer *renderer,
                GtkPackType      pack,
                gboolean         expand,
395 396
                gboolean         align,
		gboolean         fixed)
397 398
{
  CellInfo *info = g_slice_new (CellInfo);
Matthias Clasen's avatar
Matthias Clasen committed
399

400 401
  info->renderer = g_object_ref_sink (renderer);
  info->pack     = pack;
402 403
  info->expand   = expand;
  info->align    = align;
404
  info->fixed    = fixed;
405 406 407 408 409 410 411 412 413 414 415 416 417 418

  return info;
}

static void
cell_info_free (CellInfo *info)
{
  g_object_unref (info->renderer);

  g_slice_free (CellInfo, info);
}

static gint
cell_info_find (CellInfo        *info,
Matthias Clasen's avatar
Matthias Clasen committed
419
                GtkCellRenderer *renderer)
420 421 422 423
{
  return (info->renderer == renderer) ? 0 : -1;
}

424 425
static AllocatedCell *
allocated_cell_new (GtkCellRenderer *renderer,
Matthias Clasen's avatar
Matthias Clasen committed
426 427
                    gint             position,
                    gint             size)
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
{
  AllocatedCell *cell = g_slice_new (AllocatedCell);

  cell->renderer = renderer;
  cell->position = position;
  cell->size     = size;

  return cell;
}

static void
allocated_cell_free (AllocatedCell *cell)
{
  g_slice_free (AllocatedCell, cell);
}

444 445 446 447 448 449 450
static GList *
list_consecutive_cells (GtkCellAreaBox *box)
{
  GtkCellAreaBoxPrivate *priv = box->priv;
  GList                 *l, *consecutive_cells = NULL, *pack_end_cells = NULL;
  CellInfo              *info;

Matthias Clasen's avatar
Matthias Clasen committed
451 452
  /* List cells in consecutive order taking their
   * PACK_START/PACK_END options into account
453 454 455 456
   */
  for (l = priv->cells; l; l = l->next)
    {
      info = l->data;
Matthias Clasen's avatar
Matthias Clasen committed
457

458
      if (info->pack == GTK_PACK_START)
Matthias Clasen's avatar
Matthias Clasen committed
459
        consecutive_cells = g_list_prepend (consecutive_cells, info);
460 461 462 463 464
    }

  for (l = priv->cells; l; l = l->next)
    {
      info = l->data;
Matthias Clasen's avatar
Matthias Clasen committed
465

466
      if (info->pack == GTK_PACK_END)
Matthias Clasen's avatar
Matthias Clasen committed
467
        pack_end_cells = g_list_prepend (pack_end_cells, info);
468 469 470 471 472 473 474 475
    }

  consecutive_cells = g_list_reverse (consecutive_cells);
  consecutive_cells = g_list_concat (consecutive_cells, pack_end_cells);

  return consecutive_cells;
}

476
static void
Matthias Clasen's avatar
Matthias Clasen committed
477
cell_groups_clear (GtkCellAreaBox *box)
478
{
Matthias Clasen's avatar
Matthias Clasen committed
479
  GtkCellAreaBoxPrivate *priv = box->priv;
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
  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)
{
Matthias Clasen's avatar
Matthias Clasen committed
495
  GtkCellAreaBoxPrivate *priv = box->priv;
496
  CellGroup              group = { 0, };
497
  CellGroup             *group_ptr;
498 499
  GList                 *cells, *l;
  guint                  id = 0;
500
  gboolean               last_cell_fixed = FALSE;
501

502 503
  cell_groups_clear (box);

504
  if (!priv->cells)
505
    return;
506

507 508 509 510
  cells = list_consecutive_cells (box);

  /* First group is implied */
  g_array_append_val (priv->groups, group);
511
  group_ptr = &g_array_index (priv->groups, CellGroup, id);
512 513 514 515 516

  for (l = cells; l; l = l->next)
    {
      CellInfo *info = l->data;

517 518 519 520
      /* A new group starts with any aligned cell, or
       * at the beginning and end of a fixed size cell. 
       * the first group is implied */
      if ((info->align || info->fixed || last_cell_fixed) && l != cells)
Matthias Clasen's avatar
Matthias Clasen committed
521 522 523
        {
          memset (&group, 0x0, sizeof (CellGroup));
          group.id = ++id;
524

Matthias Clasen's avatar
Matthias Clasen committed
525 526 527
          g_array_append_val (priv->groups, group);
          group_ptr = &g_array_index (priv->groups, CellGroup, id);
        }
528

529 530
      group_ptr->cells = g_list_prepend (group_ptr->cells, info);
      group_ptr->n_cells++;
531

532 533 534 535 536
      /* Not every group is aligned, some are floating
       * fixed size cells */
      if (info->align)
	group_ptr->align = TRUE;

537 538
      /* A group expands if it contains any expand cells */
      if (info->expand)
Matthias Clasen's avatar
Matthias Clasen committed
539
        group_ptr->expand_cells++;
540 541

      last_cell_fixed = info->fixed;
542 543 544 545
    }

  g_list_free (cells);

546
  for (id = 0; id < priv->groups->len; id++)
547
    {
548
      group_ptr = &g_array_index (priv->groups, CellGroup, id);
549 550

      group_ptr->cells = g_list_reverse (group_ptr->cells);
551 552
    }

553 554
  /* Contexts need to be updated with the new grouping information */
  init_context_groups (box);
555 556 557
}

static gint
Matthias Clasen's avatar
Matthias Clasen committed
558 559
count_visible_cells (CellGroup *group,
                     gint      *expand_cells)
560 561 562 563 564 565 566 567 568 569
{
  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))
Matthias Clasen's avatar
Matthias Clasen committed
570 571
        {
          visible_cells++;
572

Matthias Clasen's avatar
Matthias Clasen committed
573 574 575
          if (info->expand)
            n_expand_cells++;
        }
576 577 578 579 580 581
    }

  if (expand_cells)
    *expand_cells = n_expand_cells;

  return visible_cells;
582 583 584 585 586 587
}

static gint
count_expand_groups (GtkCellAreaBox  *box)
{
  GtkCellAreaBoxPrivate *priv = box->priv;
588
  gint                   i;
589 590
  gint                   expand_groups = 0;

591
  for (i = 0; i < priv->groups->len; i++)
592
    {
593
      CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
594

595
      if (group->expand_cells > 0)
Matthias Clasen's avatar
Matthias Clasen committed
596
        expand_groups++;
597 598 599 600 601
    }

  return expand_groups;
}

Matthias Clasen's avatar
Matthias Clasen committed
602
static void
603
context_weak_notify (GtkCellAreaBox        *box,
Matthias Clasen's avatar
Matthias Clasen committed
604
                     GtkCellAreaBoxContext *dead_context)
605 606 607
{
  GtkCellAreaBoxPrivate *priv = box->priv;

608
  priv->contexts = g_slist_remove (priv->contexts, dead_context);
609 610 611
}

static void
612
init_context_group (GtkCellAreaBox        *box,
Matthias Clasen's avatar
Matthias Clasen committed
613
                    GtkCellAreaBoxContext *context)
614 615
{
  GtkCellAreaBoxPrivate *priv = box->priv;
616
  gint                  *expand_groups, *align_groups, i;
617

618
  expand_groups = g_new (gboolean, priv->groups->len);
619
  align_groups  = g_new (gboolean, priv->groups->len);
620

621
  for (i = 0; i < priv->groups->len; i++)
622
    {
623
      CellGroup *group = &g_array_index (priv->groups, CellGroup, i);
624

625
      expand_groups[i] = (group->expand_cells > 0);
626
      align_groups[i]  = group->align;
627
    }
628

629 630
  /* This call implies resetting the request info */
  _gtk_cell_area_box_init_groups (context, priv->groups->len, expand_groups, align_groups);
631
  g_free (expand_groups);
632
  g_free (align_groups);
633
}
634

635
static void
636
init_context_groups (GtkCellAreaBox *box)
637
{
638
  GtkCellAreaBoxPrivate *priv = box->priv;
639
  GSList                *l;
640

Matthias Clasen's avatar
Matthias Clasen committed
641 642
  /* When the box's groups are reconstructed,
   * contexts need to be reinitialized.
643
   */
644
  for (l = priv->contexts; l; l = l->next)
645
    {
646
      GtkCellAreaBoxContext *context = l->data;
647

648
      init_context_group (box, context);
649
    }
650
}
651

652
static void
653
reset_contexts (GtkCellAreaBox *box)
654 655 656
{
  GtkCellAreaBoxPrivate *priv = box->priv;
  GSList                *l;
657

658
  /* When the box layout changes, contexts need to
659
   * be reset and sizes for the box get requested again
660
   */
661
  for (l = priv->contexts; l; l = l->next)
662
    {
663
      GtkCellAreaContext *context = l->data;
664

665
      gtk_cell_area_context_reset (context);
666
    }
667 668
}

669 670 671 672 673 674 675
/* Fall back on a completely unaligned dynamic allocation of cells
 * when not allocated for the said orientation, alignment of cells
 * is not done when each area gets a different size in the orientation
 * of the box.
 */
static GSList *
allocate_cells_manually (GtkCellAreaBox        *box,
Matthias Clasen's avatar
Matthias Clasen committed
676 677 678
                         GtkWidget             *widget,
                         gint                   width,
                         gint                   height)
679 680 681 682 683 684 685
{
  GtkCellAreaBoxPrivate    *priv = box->priv;
  GList                    *cells, *l;
  GSList                   *allocated_cells = NULL;
  GtkRequestedSize         *sizes;
  gint                      i;
  gint                      nvisible = 0, nexpand = 0, group_expand;
686
  gint                      avail_size, extra_size, extra_extra, full_size;
687
  gint                      position = 0, for_size;
688
  gboolean                  rtl;
689 690 691 692

  if (!priv->cells)
    return NULL;

Matthias Clasen's avatar
Matthias Clasen committed
693 694 695
  /* For vertical oriented boxes, we just let the cell renderers
   * realign themselves for rtl
   */
696
  rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
Matthias Clasen's avatar
Matthias Clasen committed
697
         gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
698

699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
  cells = list_consecutive_cells (box);

  /* Count the visible and expand cells */
  for (i = 0; i < priv->groups->len; i++)
    {
      CellGroup *group = &g_array_index (priv->groups, CellGroup, i);

      nvisible += count_visible_cells (group, &group_expand);
      nexpand  += group_expand;
    }

  if (nvisible <= 0)
    {
      g_list_free (cells);
      return NULL;
    }

  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
717
    {
718 719
      full_size = avail_size = width;
      for_size  = height;
720
    }
721
  else
722
    {
723 724
      full_size = avail_size = height;
      for_size  = width;
725
    }
726 727 728 729 730 731 732 733

  /* Go ahead and collect the requests on the fly */
  sizes = g_new0 (GtkRequestedSize, nvisible);
  for (l = cells, i = 0; l; l = l->next)
    {
      CellInfo *info = l->data;

      if (!gtk_cell_renderer_get_visible (info->renderer))
Matthias Clasen's avatar
Matthias Clasen committed
734
        continue;
735

736
      gtk_cell_area_request_renderer (GTK_CELL_AREA (box), info->renderer,
Matthias Clasen's avatar
Matthias Clasen committed
737 738 739 740
                                      priv->orientation,
                                      widget, for_size,
                                      &sizes[i].minimum_size,
                                      &sizes[i].natural_size);
741 742 743 744 745 746 747 748 749 750

      avail_size -= sizes[i].minimum_size;

      sizes[i].data = info;

      i++;
    }

  /* Naturally distribute the allocation */
  avail_size -= (nvisible - 1) * priv->spacing;
751 752 753 754
  if (avail_size > 0)
    avail_size = gtk_distribute_natural_allocation (avail_size, nvisible, sizes);
  else
    avail_size = 0;
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771

  /* Calculate/distribute expand for cells */
  if (nexpand > 0)
    {
      extra_size  = avail_size / nexpand;
      extra_extra = avail_size % nexpand;
    }
  else
    extra_size = extra_extra = 0;

  /* Create the allocated cells */
  for (i = 0; i < nvisible; i++)
    {
      CellInfo      *info = sizes[i].data;
      AllocatedCell *cell;

      if (info->expand)
Matthias Clasen's avatar
Matthias Clasen committed
772 773 774 775 776 777 778 779 780
        {
          sizes[i].minimum_size += extra_size;
          if (extra_extra)
            {
              sizes[i].minimum_size++;
              extra_extra--;
            }
        }

781
      if (rtl)
Matthias Clasen's avatar
Matthias Clasen committed
782 783 784
        cell = allocated_cell_new (info->renderer,
                                   full_size - (position + sizes[i].minimum_size),
                                   sizes[i].minimum_size);
785
      else
Matthias Clasen's avatar
Matthias Clasen committed
786
        cell = allocated_cell_new (info->renderer, position, sizes[i].minimum_size);
787 788

      allocated_cells = g_slist_prepend (allocated_cells, cell);
Matthias Clasen's avatar
Matthias Clasen committed
789

790 791 792 793 794 795 796 797
      position += sizes[i].minimum_size;
      position += priv->spacing;
    }

  g_free (sizes);
  g_list_free (cells);

  /* Note it might not be important to reverse the list here at all,
Matthias Clasen's avatar
Matthias Clasen committed
798 799
   * we have the correct positions, no need to allocate from left to right
   */
800 801 802
  return g_slist_reverse (allocated_cells);
}

803 804 805 806 807
/* Returns an allocation for each cell in the orientation of the box,
 * used in ->render()/->event() implementations to get a straight-forward
 * list of allocated cells to operate on.
 */
static GSList *
808
get_allocated_cells (GtkCellAreaBox        *box,
Matthias Clasen's avatar
Matthias Clasen committed
809 810 811 812
                     GtkCellAreaBoxContext *context,
                     GtkWidget             *widget,
                     gint                   width,
                     gint                   height)
813
{
814 815 816 817 818
  GtkCellAreaBoxAllocation *group_allocs;
  GtkCellArea              *area = GTK_CELL_AREA (box);
  GtkCellAreaBoxPrivate    *priv = box->priv;
  GList                    *cell_list;
  GSList                   *allocated_cells = NULL;
819
  gint                      i, j, n_allocs, position;
820 821
  gint                      for_size, full_size;
  gboolean                  rtl;
822

823
  group_allocs = _gtk_cell_area_box_context_get_orientation_allocs (context, &n_allocs);
824
  if (!group_allocs)
825
    return allocate_cells_manually (box, widget, width, height);
826

827
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
828 829 830 831
    {
      full_size = width;
      for_size  = height;
    }
832
  else
833 834 835 836 837
    {
      full_size = height;
      for_size  = width;
    }

Matthias Clasen's avatar
Matthias Clasen committed
838 839 840
  /* For vertical oriented boxes, we just let the cell renderers
   * realign themselves for rtl
   */
841
  rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
Matthias Clasen's avatar
Matthias Clasen committed
842
         gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
843

844
  for (position = 0, i = 0; i < n_allocs; i++)
845
    {
Matthias Clasen's avatar
Matthias Clasen committed
846 847 848
      /* 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
849 850
       */
      CellGroup *group = &g_array_index (priv->groups, CellGroup, group_allocs[i].group_idx);
851 852 853

      /* Exception for single cell groups */
      if (group->n_cells == 1)
Matthias Clasen's avatar
Matthias Clasen committed
854 855 856
        {
          CellInfo      *info = group->cells->data;
          AllocatedCell *cell;
857 858
	  gint           cell_position, cell_size;

859 860 861
	  if (!gtk_cell_renderer_get_visible (info->renderer))
	    continue;

862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
	  /* If were not aligned, place the cell after the last cell */
	  if (info->align)
	    position = cell_position = group_allocs[i].position;
	  else
	    cell_position = position;

	  /* If not a fixed size, use only the requested size for this row */
	  if (info->fixed)
	    cell_size = group_allocs[i].size;
	  else
	    {
	      gint dummy;
              gtk_cell_area_request_renderer (area, info->renderer,
                                              priv->orientation,
                                              widget, for_size,
                                              &dummy,
                                              &cell_size);
	      cell_size = MIN (cell_size, group_allocs[i].size);
	    }
Matthias Clasen's avatar
Matthias Clasen committed
881 882 883

          if (rtl)
            cell = allocated_cell_new (info->renderer,
884
                                       full_size - (cell_position + cell_size), cell_size);
Matthias Clasen's avatar
Matthias Clasen committed
885
          else
886 887 888 889
            cell = allocated_cell_new (info->renderer, cell_position, cell_size);

	  position += cell_size;
          position += priv->spacing;
Matthias Clasen's avatar
Matthias Clasen committed
890 891 892

          allocated_cells = g_slist_prepend (allocated_cells, cell);
        }
893
      else
Matthias Clasen's avatar
Matthias Clasen committed
894 895
        {
          GtkRequestedSize *sizes;
896
          gint              avail_size, cell_position;
Matthias Clasen's avatar
Matthias Clasen committed
897 898
          gint              visible_cells, expand_cells;
          gint              extra_size, extra_extra;
899

Matthias Clasen's avatar
Matthias Clasen committed
900
          visible_cells = count_visible_cells (group, &expand_cells);
901

Matthias Clasen's avatar
Matthias Clasen committed
902 903 904 905 906
          /* If this row has no visible cells in this group, just
           * skip the allocation
           */
          if (visible_cells == 0)
            continue;
907

908 909 910 911 912 913 914 915 916 917 918 919 920
	  /* If were not aligned, place the cell after the last cell 
	   * and eat up the extra space
	   */
	  if (group->align)
	    {
	      avail_size = group_allocs[i].size;
	      position   = cell_position = group_allocs[i].position;
	    }
	  else
	    {
	      avail_size    = group_allocs[i].size + (group_allocs[i].position - position);
	      cell_position = position;
	    }
921

Matthias Clasen's avatar
Matthias Clasen committed
922
          sizes = g_new (GtkRequestedSize, visible_cells);
923

Matthias Clasen's avatar
Matthias Clasen committed
924 925 926
          for (j = 0, cell_list = group->cells; cell_list; cell_list = cell_list->next)
            {
              CellInfo *info = cell_list->data;
927

Matthias Clasen's avatar
Matthias Clasen committed
928 929
              if (!gtk_cell_renderer_get_visible (info->renderer))
                continue;
930

Matthias Clasen's avatar
Matthias Clasen committed
931 932 933 934 935
              gtk_cell_area_request_renderer (area, info->renderer,
                                              priv->orientation,
                                              widget, for_size,
                                              &sizes[j].minimum_size,
                                              &sizes[j].natural_size);
936

Matthias Clasen's avatar
Matthias Clasen committed
937 938
              sizes[j].data = info;
              avail_size   -= sizes[j].minimum_size;
939

Matthias Clasen's avatar
Matthias Clasen committed
940 941
              j++;
            }
942

Matthias Clasen's avatar
Matthias Clasen committed
943 944
          /* Distribute cells naturally within the group */
          avail_size -= (visible_cells - 1) * priv->spacing;
945 946 947 948
          if (avail_size > 0)
            avail_size = gtk_distribute_natural_allocation (avail_size, visible_cells, sizes);
          else
            avail_size = 0;
949

Matthias Clasen's avatar
Matthias Clasen committed
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
          /* Calculate/distribute expand for cells */
          if (expand_cells > 0)
            {
              extra_size  = avail_size / expand_cells;
              extra_extra = avail_size % expand_cells;
            }
          else
            extra_size = extra_extra = 0;

          /* Create the allocated cells (loop only over visible cells here) */
          for (j = 0; j < visible_cells; j++)
            {
              CellInfo      *info = sizes[j].data;
              AllocatedCell *cell;

              if (info->expand)
                {
                  sizes[j].minimum_size += extra_size;
                  if (extra_extra)
                    {
                      sizes[j].minimum_size++;
                      extra_extra--;
                    }
                }

              if (rtl)
                cell = allocated_cell_new (info->renderer,
977
                                           full_size - (cell_position + sizes[j].minimum_size),
Matthias Clasen's avatar
Matthias Clasen committed
978 979
                                           sizes[j].minimum_size);
              else
980
                cell = allocated_cell_new (info->renderer, cell_position, sizes[j].minimum_size);
Matthias Clasen's avatar
Matthias Clasen committed
981 982 983

              allocated_cells = g_slist_prepend (allocated_cells, cell);

984 985
              cell_position += sizes[j].minimum_size;
              cell_position += priv->spacing;
Matthias Clasen's avatar
Matthias Clasen committed
986 987 988
            }

          g_free (sizes);
989 990

	  position = cell_position;
Matthias Clasen's avatar
Matthias Clasen committed
991
        }
992 993
    }

994 995
  g_free (group_allocs);

996
  /* Note it might not be important to reverse the list here at all,
Matthias Clasen's avatar
Matthias Clasen committed
997 998
   * we have the correct positions, no need to allocate from left to right
   */
999 1000 1001
  return g_slist_reverse (allocated_cells);
}

1002 1003 1004

static void
gtk_cell_area_box_focus_changed (GtkCellArea        *area,
Matthias Clasen's avatar
Matthias Clasen committed
1005 1006
                                 GParamSpec         *pspec,
                                 GtkCellAreaBox     *box)
1007 1008 1009 1010 1011
{
  if (gtk_cell_area_get_focus_cell (area))
    box->priv->last_focus_cell = gtk_cell_area_get_focus_cell (area);
}

1012 1013 1014 1015 1016 1017
/*************************************************************
 *                      GObjectClass                         *
 *************************************************************/
static void
gtk_cell_area_box_finalize (GObject *object)
{
1018 1019 1020 1021
  GtkCellAreaBox        *box = GTK_CELL_AREA_BOX (object);
  GtkCellAreaBoxPrivate *priv = box->priv;
  GSList                *l;

1022 1023 1024
  /* Unref/free the context list */
  for (l = priv->contexts; l; l = l->next)
    g_object_weak_unref (G_OBJECT (l->data), (GWeakNotify)context_weak_notify, box);
1025

1026 1027
  g_slist_free (priv->contexts);
  priv->contexts = NULL;
1028

1029 1030 1031
  /* Free the cell grouping info */
  cell_groups_clear (box);
  g_array_free (priv->groups, TRUE);
Matthias Clasen's avatar
Matthias Clasen committed
1032

1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
  G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->finalize (object);
}

static void
gtk_cell_area_box_dispose (GObject *object)
{
  G_OBJECT_CLASS (gtk_cell_area_box_parent_class)->dispose (object);
}

static void
gtk_cell_area_box_set_property (GObject       *object,
Matthias Clasen's avatar
Matthias Clasen committed
1044 1045 1046
                                guint          prop_id,
                                const GValue  *value,
                                GParamSpec    *pspec)
1047
{
1048
  GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
1049

1050 1051
  switch (prop_id)
    {
1052 1053 1054 1055
    case PROP_ORIENTATION:
      box->priv->orientation = g_value_get_enum (value);

      /* Notify that size needs to be requested again */
1056
      reset_contexts (box);
1057

1058
      break;
1059 1060 1061 1062 1063 1064 1065
    case PROP_SPACING:
      gtk_cell_area_box_set_spacing (box, g_value_get_int (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
1066 1067 1068 1069
}

static void
gtk_cell_area_box_get_property (GObject     *object,
Matthias Clasen's avatar
Matthias Clasen committed
1070 1071 1072
                                guint        prop_id,
                                GValue      *value,
                                GParamSpec  *pspec)
1073
{
1074
  GtkCellAreaBox *box = GTK_CELL_AREA_BOX (object);
1075

1076 1077
  switch (prop_id)
    {
1078 1079 1080
    case PROP_ORIENTATION:
      g_value_set_enum (value, box->priv->orientation);
      break;
1081 1082 1083 1084 1085 1086 1087
    case PROP_SPACING:
      g_value_set_int (value, gtk_cell_area_box_get_spacing (box));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
1088 1089 1090 1091 1092
}

/*************************************************************
 *                    GtkCellAreaClass                       *
 *************************************************************/
Matthias Clasen's avatar
Matthias Clasen committed
1093
static void
1094
gtk_cell_area_box_add (GtkCellArea        *area,
Matthias Clasen's avatar
Matthias Clasen committed
1095
                       GtkCellRenderer    *renderer)
1096
{
1097
  gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area),
1098
                                renderer, FALSE, FALSE, TRUE);
1099 1100 1101 1102
}

static void
gtk_cell_area_box_remove (GtkCellArea        *area,
Matthias Clasen's avatar
Matthias Clasen committed
1103
                          GtkCellRenderer    *renderer)
1104
{
1105 1106 1107 1108
  GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
  GtkCellAreaBoxPrivate *priv = box->priv;
  GList                 *node;

1109 1110 1111
  if (priv->last_focus_cell == renderer)
    priv->last_focus_cell = NULL;

Matthias Clasen's avatar
Matthias Clasen committed
1112 1113
  node = g_list_find_custom (priv->cells, renderer,
                             (GCompareFunc)cell_info_find);
1114 1115 1116 1117

  if (node)
    {
      CellInfo *info = node->data;
1118

1119 1120 1121
      cell_info_free (info);

      priv->cells = g_list_delete_link (priv->cells, node);
1122

1123
      /* Reconstruct cell groups */
1124
      cell_groups_rebuild (box);
1125 1126 1127
    }
  else
    g_warning ("Trying to remove a cell renderer that is not present GtkCellAreaBox");
1128 1129 1130
}

static void
1131
gtk_cell_area_box_foreach (GtkCellArea        *area,
Matthias Clasen's avatar
Matthias Clasen committed
1132 1133
                           GtkCellCallback     callback,
                           gpointer            callback_data)
1134
{
1135 1136 1137 1138 1139 1140 1141
  GtkCellAreaBox        *box  = GTK_CELL_AREA_BOX (area);
  GtkCellAreaBoxPrivate *priv = box->priv;
  GList                 *list;

  for (list = priv->cells; list; list = list->next)
    {
      CellInfo *info = list->data;
1142

1143
      if (callback (info->renderer, callback_data))
Matthias Clasen's avatar
Matthias Clasen committed
1144
        break;
1145
    }
1146 1147
}

1148
static void
1149
gtk_cell_area_box_foreach_alloc (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
1150 1151 1152 1153 1154 1155
                                 GtkCellAreaContext   *context,
                                 GtkWidget            *widget,
                                 const GdkRectangle   *cell_area,
                                 const GdkRectangle   *background_area,
                                 GtkCellAllocCallback  callback,
                                 gpointer              callback_data)
1156 1157 1158
{
  GtkCellAreaBox        *box      = GTK_CELL_AREA_BOX (area);
  GtkCellAreaBoxPrivate *priv     = box->priv;
1159
  GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context);
1160
  GSList                *allocated_cells, *l;
1161
  GdkRectangle           cell_alloc, cell_background;
1162 1163 1164
  gboolean               rtl;

  rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL &&
Matthias Clasen's avatar
Matthias Clasen committed
1165
         gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1166

1167
  cell_alloc = *cell_area;
1168 1169

  /* Get a list of cells with allocation sizes decided regardless
Matthias Clasen's avatar
Matthias Clasen committed
1170 1171 1172 1173
   * of alignments and pack order etc.
   */
  allocated_cells = get_allocated_cells (box, box_context, widget,
                                         cell_area->width, cell_area->height);
1174 1175 1176

  for (l = allocated_cells; l; l = l->next)
    {
1177
      AllocatedCell *cell = l->data;
1178

1179
      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
Matthias Clasen's avatar
Matthias Clasen committed
1180 1181 1182 1183
        {
          cell_alloc.x     = cell_area->x + cell->position;
          cell_alloc.width = cell->size;
        }
1184
      else
Matthias Clasen's avatar
Matthias Clasen committed
1185 1186 1187 1188 1189 1190 1191 1192
        {
          cell_alloc.y      = cell_area->y + cell->position;
          cell_alloc.height = cell->size;
        }

      /* Stop iterating over cells if they flow out of the render
       * area, this can happen because the render area can actually
       * be smaller than the requested area (treeview columns can
1193
       * be user resizable and can be resized to be smaller than
Matthias Clasen's avatar
Matthias Clasen committed
1194 1195
       * the actual requested area).
       */
1196
      if (cell_alloc.x > cell_area->x + cell_area->width ||
Matthias Clasen's avatar
Matthias Clasen committed
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207
          cell_alloc.x + cell_alloc.width < cell_area->x ||
          cell_alloc.y > cell_area->y + cell_area->height)
        break;

      /* Special case for the last cell (or first cell in rtl)...
       * let the last cell consume the remaining space in the area
       * (the last cell is allowed to consume the remaining space if
       * the space given for rendering is actually larger than allocation,
       * this can happen in the expander GtkTreeViewColumn where only the
       * deepest depth column receives the allocation... shallow columns
       * receive more width). */
1208
      if (!l->next)
Matthias Clasen's avatar
Matthias Clasen committed
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
        {
          if (rtl)
            {
              /* Fill the leading space for the first cell in the area
               * (still last in the list)
               */
              cell_alloc.width = (cell_alloc.x - cell_area->x) + cell_alloc.width;
              cell_alloc.x     = cell_area->x;
            }
          else
            {
              cell_alloc.width  = cell_area->x + cell_area->width  - cell_alloc.x;
              cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y;
            }
        }
1224
      else
Matthias Clasen's avatar
Matthias Clasen committed
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
        {
          /* If the cell we are rendering doesnt fit into the remaining space,
           * clip it so that the underlying renderer has a chance to deal with
           * it (for instance text renderers get a chance to ellipsize).
           */
          if (cell_alloc.x + cell_alloc.width > cell_area->x + cell_area->width)
            cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x;

          if (cell_alloc.y + cell_alloc.height > cell_area->y + cell_area->height)
            cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y;
        }
1236

1237
      /* Add portions of the background_area to the cell_alloc
Matthias Clasen's avatar
Matthias Clasen committed
1238 1239
       * to create the cell_background
       */
1240
      cell_background = cell_alloc;
1241

1242
      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
Matthias Clasen's avatar
Matthias Clasen committed
1243 1244 1245
        {
          if (l == allocated_cells)
            {
1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
              /* Add the depth to the first cell */
              if (rtl)
                {
                  cell_background.width += background_area->width - cell_area->width;
                  cell_background.x      = background_area->x + background_area->width - cell_background.width;
                }
              else
                {
                  cell_background.width += cell_area->x - background_area->x;
                  cell_background.x      = background_area->x;
                }
Matthias Clasen's avatar
Matthias Clasen committed
1257
            }
1258

Matthias Clasen's avatar
Matthias Clasen committed
1259
          if (l->next == NULL)
1260 1261 1262 1263 1264 1265 1266 1267 1268
            {
              /* Grant this cell the remaining space */
              int remain = cell_background.x - background_area->x;

              if (rtl)
                cell_background.x -= remain;
              else
                cell_background.width = background_area->width - remain;
            }
1269

Matthias Clasen's avatar
Matthias Clasen committed
1270 1271 1272
          cell_background.y      = background_area->y;
          cell_background.height = background_area->height;
        }
1273
      else
Matthias Clasen's avatar
Matthias Clasen committed
1274 1275 1276 1277 1278 1279
        {
          if (l == allocated_cells)
            {
              cell_background.height += cell_background.y - background_area->y;
              cell_background.y       = background_area->y;
            }
1280

Matthias Clasen's avatar
Matthias Clasen committed
1281 1282 1283
          if (l->next == NULL)
              cell_background.height =
                background_area->height - (cell_background.y - background_area->y);
1284

Matthias Clasen's avatar
Matthias Clasen committed
1285 1286 1287
          cell_background.x     = background_area->x;
          cell_background.width = background_area->width;
        }
1288

1289
      if (callback (cell->renderer, &cell_alloc, &cell_background, callback_data))
Matthias Clasen's avatar
Matthias Clasen committed
1290
        break;
1291 1292