gtkcellarea.c 127 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* gtkcellarea.c
 *
 * 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
/**
 * SECTION:gtkcellarea
Matthias Clasen's avatar
Matthias Clasen committed
24
 * @Short_Description: An abstract class for laying out GtkCellRenderers
25 26
 * @Title: GtkCellArea
 *
Matthias Clasen's avatar
Matthias Clasen committed
27 28 29 30
 * The #GtkCellArea is an abstract class for #GtkCellLayout widgets
 * (also referred to as "layouting widgets") to interface with an
 * arbitrary number of #GtkCellRenderers and interact with the user
 * for a given #GtkTreeModel row.
31
 *
Matthias Clasen's avatar
Matthias Clasen committed
32
 * The cell area handles events, focus navigation, drawing and
33 34
 * size requests and allocations for a given row of data.
 *
Matthias Clasen's avatar
Matthias Clasen committed
35
 * Usually users dont have to interact with the #GtkCellArea directly
36
 * unless they are implementing a cell-layouting widget themselves.
37
 *
38 39
 * # Requesting area sizes
 *
40
 * As outlined in
41
 * [GtkWidget’s geometry management section][geometry-management],
42
 * GTK+ uses a height-for-width
Matthias Clasen's avatar
Matthias Clasen committed
43
 * geometry management system to compute the sizes of widgets and user
44 45 46
 * interfaces. #GtkCellArea uses the same semantics to calculate the
 * size of an area for an arbitrary number of #GtkTreeModel rows.
 *
47
 * When requesting the size of a cell area one needs to calculate
48
 * the size for a handful of rows, and this will be done differently by
49
 * different layouting widgets. For instance a #GtkTreeViewColumn
50
 * always lines up the areas from top to bottom while a #GtkIconView
51
 * on the other hand might enforce that all areas received the same
Matthias Clasen's avatar
Matthias Clasen committed
52
 * width and wrap the areas around, requesting height for more cell
53 54
 * areas when allocated less width.
 *
55
 * It’s also important for areas to maintain some cell
Matthias Clasen's avatar
Matthias Clasen committed
56
 * alignments with areas rendered for adjacent rows (cells can
William Jon McCann's avatar
William Jon McCann committed
57
 * appear “columnized” inside an area even when the size of
58
 * cells are different in each row). For this reason the #GtkCellArea
59
 * uses a #GtkCellAreaContext object to store the alignments
60 61 62 63 64 65
 * and sizes along the way (as well as the overall largest minimum
 * and natural size for all the rows which have been calculated
 * with the said context).
 *
 * The #GtkCellAreaContext is an opaque object specific to the
 * #GtkCellArea which created it (see gtk_cell_area_create_context()).
66
 * The owning cell-layouting widget can create as many contexts as
67
 * it wishes to calculate sizes of rows which should receive the
Matthias Clasen's avatar
Matthias Clasen committed
68
 * same size in at least one orientation (horizontally or vertically),
69
 * However, it’s important that the same #GtkCellAreaContext which
70 71
 * was used to request the sizes for a given #GtkTreeModel row be
 * used when rendering or processing events for that row.
72 73 74
 *
 * In order to request the width of all the rows at the root level
 * of a #GtkTreeModel one would do the following:
75
 *
Matthias Clasen's avatar
Matthias Clasen committed
76
 * |[<!-- language="C" -->
77 78 79 80 81 82 83 84 85 86 87 88 89
 * GtkTreeIter iter;
 * gint        minimum_width;
 * gint        natural_width;
 *
 * valid = gtk_tree_model_get_iter_first (model, &iter);
 * while (valid)
 *   {
 *     gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
 *     gtk_cell_area_get_preferred_width (area, context, widget, NULL, NULL);
 *
 *     valid = gtk_tree_model_iter_next (model, &iter);
 *   }
 * gtk_cell_area_context_get_preferred_width (context, &minimum_width, &natural_width);
Matthias Clasen's avatar
Matthias Clasen committed
90
 * ]|
91
 *
92
 * Note that in this example it’s not important to observe the
Matthias Clasen's avatar
Matthias Clasen committed
93
 * returned minimum and natural width of the area for each row
94
 * unless the cell-layouting object is actually interested in the
Matthias Clasen's avatar
Matthias Clasen committed
95 96 97 98 99 100 101 102 103
 * widths of individual rows. The overall width is however stored
 * in the accompanying #GtkCellAreaContext object and can be consulted
 * at any time.
 *
 * This can be useful since #GtkCellLayout widgets usually have to
 * support requesting and rendering rows in treemodels with an
 * exceedingly large amount of rows. The #GtkCellLayout widget in
 * that case would calculate the required width of the rows in an
 * idle or timeout source (see g_timeout_add()) and when the widget
Matthias Clasen's avatar
Matthias Clasen committed
104
 * is requested its actual width in #GtkWidgetClass.get_preferred_width()
Matthias Clasen's avatar
Matthias Clasen committed
105 106 107 108 109
 * it can simply consult the width accumulated so far in the
 * #GtkCellAreaContext object.
 *
 * A simple example where rows are rendered from top to bottom and
 * take up the full width of the layouting widget would look like:
110
 *
Matthias Clasen's avatar
Matthias Clasen committed
111
 * |[<!-- language="C" -->
112 113 114 115 116 117 118 119 120 121 122 123
 * static void
 * foo_get_preferred_width (GtkWidget       *widget,
 *                          gint            *minimum_size,
 *                          gint            *natural_size)
 * {
 *   Foo        *foo  = FOO (widget);
 *   FooPrivate *priv = foo->priv;
 *
 *   foo_ensure_at_least_one_handfull_of_rows_have_been_requested (foo);
 *
 *   gtk_cell_area_context_get_preferred_width (priv->context, minimum_size, natural_size);
 * }
Matthias Clasen's avatar
Matthias Clasen committed
124
 * ]|
125
 *
Matthias Clasen's avatar
Matthias Clasen committed
126 127 128 129 130 131 132 133 134
 * In the above example the Foo widget has to make sure that some
 * row sizes have been calculated (the amount of rows that Foo judged
 * was appropriate to request space for in a single timeout iteration)
 * before simply returning the amount of space required by the area via
 * the #GtkCellAreaContext.
 *
 * Requesting the height for width (or width for height) of an area is
 * a similar task except in this case the #GtkCellAreaContext does not
 * store the data (actually, it does not know how much space the layouting
135
 * widget plans to allocate it for every row. It’s up to the layouting
Matthias Clasen's avatar
Matthias Clasen committed
136 137 138 139 140
 * widget to render each row of data with the appropriate height and
 * width which was requested by the #GtkCellArea).
 *
 * In order to request the height for width of all the rows at the
 * root level of a #GtkTreeModel one would do the following:
141
 *
Matthias Clasen's avatar
Matthias Clasen committed
142
 * |[<!-- language="C" -->
143 144 145 146 147 148 149 150 151 152
 * GtkTreeIter iter;
 * gint        minimum_height;
 * gint        natural_height;
 * gint        full_minimum_height = 0;
 * gint        full_natural_height = 0;
 *
 * valid = gtk_tree_model_get_iter_first (model, &iter);
 * while (valid)
 *   {
 *     gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
Matthias Clasen's avatar
Matthias Clasen committed
153
 *     gtk_cell_area_get_preferred_height_for_width (area, context, widget,
154 155 156 157 158 159 160 161 162 163
 *                                                   width, &minimum_height, &natural_height);
 *
 *     if (width_is_for_allocation)
 *        cache_row_height (&iter, minimum_height, natural_height);
 *
 *     full_minimum_height += minimum_height;
 *     full_natural_height += natural_height;
 *
 *     valid = gtk_tree_model_iter_next (model, &iter);
 *   }
Matthias Clasen's avatar
Matthias Clasen committed
164
 * ]|
165
 *
Matthias Clasen's avatar
Matthias Clasen committed
166 167 168 169 170 171 172 173 174
 * Note that in the above example we would need to cache the heights
 * returned for each row so that we would know what sizes to render the
 * areas for each row. However we would only want to really cache the
 * heights if the request is intended for the layouting widgets real
 * allocation.
 *
 * In some cases the layouting widget is requested the height for an
 * arbitrary for_width, this is a special case for layouting widgets
 * who need to request size for tens of thousands  of rows. For this
175
 * case it’s only important that the layouting widget calculate
Matthias Clasen's avatar
Matthias Clasen committed
176 177 178 179
 * one reasonably sized chunk of rows and return that height
 * synchronously. The reasoning here is that any layouting widget is
 * at least capable of synchronously calculating enough height to fill
 * the screen height (or scrolled window height) in response to a single
Matthias Clasen's avatar
Matthias Clasen committed
180
 * call to #GtkWidgetClass.get_preferred_height_for_width(). Returning
Matthias Clasen's avatar
Matthias Clasen committed
181 182
 * a perfect height for width that is larger than the screen area is
 * inconsequential since after the layouting receives an allocation
183
 * from a scrolled window it simply continues to drive the scrollbar
Matthias Clasen's avatar
Matthias Clasen committed
184 185
 * values while more and more height is required for the row heights
 * that are calculated in the background.
186 187
 *
 * # Rendering Areas
Matthias Clasen's avatar
Matthias Clasen committed
188
 *
Matthias Clasen's avatar
Matthias Clasen committed
189 190 191
 * Once area sizes have been aquired at least for the rows in the
 * visible area of the layouting widget they can be rendered at
 * #GtkWidgetClass.draw() time.
192
 *
Matthias Clasen's avatar
Matthias Clasen committed
193 194
 * A crude example of how to render all the rows at the root level
 * runs as follows:
195
 *
Matthias Clasen's avatar
Matthias Clasen committed
196
 * |[<!-- language="C" -->
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
 * GtkAllocation allocation;
 * GdkRectangle  cell_area = { 0, };
 * GtkTreeIter   iter;
 * gint          minimum_width;
 * gint          natural_width;
 *
 * gtk_widget_get_allocation (widget, &allocation);
 * cell_area.width = allocation.width;
 *
 * valid = gtk_tree_model_get_iter_first (model, &iter);
 * while (valid)
 *   {
 *     cell_area.height = get_cached_height_for_row (&iter);
 *
 *     gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
Matthias Clasen's avatar
Matthias Clasen committed
212
 *     gtk_cell_area_render (area, context, widget, cr,
213 214 215 216 217 218
 *                           &cell_area, &cell_area, state_flags, FALSE);
 *
 *     cell_area.y += cell_area.height;
 *
 *     valid = gtk_tree_model_iter_next (model, &iter);
 *   }
Matthias Clasen's avatar
Matthias Clasen committed
219
 * ]|
220
 *
Matthias Clasen's avatar
Matthias Clasen committed
221 222
 * Note that the cached height in this example really depends on how
 * the layouting widget works. The layouting widget might decide to
223
 * give every row its minimum or natural height or, if the model content
Matthias Clasen's avatar
Matthias Clasen committed
224 225
 * is expected to fit inside the layouting widget without scrolling, it
 * would make sense to calculate the allocation for each row at
Matthias Clasen's avatar
Matthias Clasen committed
226
 * #GtkWidget::size-allocate time using gtk_distribute_natural_allocation().
Matthias Clasen's avatar
Matthias Clasen committed
227
 *
228
 * # Handling Events and Driving Keyboard Focus
Matthias Clasen's avatar
Matthias Clasen committed
229
 *
Matthias Clasen's avatar
Matthias Clasen committed
230 231 232 233 234 235 236 237
 * Passing events to the area is as simple as handling events on any
 * normal widget and then passing them to the gtk_cell_area_event()
 * API as they come in. Usually #GtkCellArea is only interested in
 * button events, however some customized derived areas can be implemented
 * who are interested in handling other events. Handling an event can
 * trigger the #GtkCellArea::focus-changed signal to fire; as well as
 * #GtkCellArea::add-editable in the case that an editable cell was
 * clicked and needs to start editing. You can call
238 239
 * gtk_cell_area_stop_editing() at any time to cancel any cell editing
 * that is currently in progress.
240
 *
Matthias Clasen's avatar
Matthias Clasen committed
241 242
 * The #GtkCellArea drives keyboard focus from cell to cell in a way
 * similar to #GtkWidget. For layouting widgets that support giving
243
 * focus to cells it’s important to remember to pass %GTK_CELL_RENDERER_FOCUSED
Matthias Clasen's avatar
Matthias Clasen committed
244 245 246 247 248 249 250 251 252 253 254 255 256
 * to the area functions for the row that has focus and to tell the
 * area to paint the focus at render time.
 *
 * Layouting widgets that accept focus on cells should implement the
 * #GtkWidgetClass.focus() virtual method. The layouting widget is always
 * responsible for knowing where #GtkTreeModel rows are rendered inside
 * the widget, so at #GtkWidgetClass.focus() time the layouting widget
 * should use the #GtkCellArea methods to navigate focus inside the area
 * and then observe the GtkDirectionType to pass the focus to adjacent
 * rows and areas.
 *
 * A basic example of how the #GtkWidgetClass.focus() virtual method
 * should be implemented:
257
 *
Matthias Clasen's avatar
Matthias Clasen committed
258
 * |[<!-- language="C" -->
259
 * static gboolean
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
 * foo_focus (GtkWidget       *widget,
 *            GtkDirectionType direction)
 * {
 *   Foo        *foo  = FOO (widget);
 *   FooPrivate *priv = foo->priv;
 *   gint        focus_row;
 *   gboolean    have_focus = FALSE;
 *
 *   focus_row = priv->focus_row;
 *
 *   if (!gtk_widget_has_focus (widget))
 *     gtk_widget_grab_focus (widget);
 *
 *   valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, priv->focus_row);
 *   while (valid)
 *     {
 *       gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
 *
 *       if (gtk_cell_area_focus (priv->area, direction))
 *         {
 *            priv->focus_row = focus_row;
 *            have_focus = TRUE;
 *            break;
 *         }
 *       else
 *         {
286 287 288 289 290 291 292
 *           if (direction == GTK_DIR_RIGHT ||
 *               direction == GTK_DIR_LEFT)
 *             break;
 *           else if (direction == GTK_DIR_UP ||
 *                    direction == GTK_DIR_TAB_BACKWARD)
 *            {
 *               if (focus_row == 0)
293 294 295 296 297 298 299 300 301 302
 *                 break;
 *               else
 *                {
 *                   focus_row--;
 *                   valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, focus_row);
 *                }
 *             }
 *           else
 *             {
 *               if (focus_row == last_row)
303
 *                 break;
304 305 306 307 308 309 310 311 312 313
 *               else
 *                 {
 *                   focus_row++;
 *                   valid = gtk_tree_model_iter_next (priv->model, &iter);
 *                 }
 *             }
 *         }
 *     }
 *     return have_focus;
 * }
Matthias Clasen's avatar
Matthias Clasen committed
314
 * ]|
315
 *
Matthias Clasen's avatar
Matthias Clasen committed
316 317
 * Note that the layouting widget is responsible for matching the
 * GtkDirectionType values to the way it lays out its cells.
Matthias Clasen's avatar
Matthias Clasen committed
318
 *
319
 * # Cell Properties
Matthias Clasen's avatar
Matthias Clasen committed
320 321 322
 *
 * The #GtkCellArea introduces cell properties for #GtkCellRenderers
 * in very much the same way that #GtkContainer introduces
323
 * [child properties][child-properties]
Matthias Clasen's avatar
Matthias Clasen committed
324 325
 * for #GtkWidgets. This provides some general interfaces for defining
 * the relationship cell areas have with their cells. For instance in a
William Jon McCann's avatar
William Jon McCann committed
326
 * #GtkCellAreaBox a cell might “expand” and receive extra space when
327
 * the area is allocated more than its full natural request, or a cell
William Jon McCann's avatar
William Jon McCann committed
328
 * might be configured to “align” with adjacent rows which were requested
Matthias Clasen's avatar
Matthias Clasen committed
329 330 331 332 333 334
 * and rendered with the same #GtkCellAreaContext.
 *
 * Use gtk_cell_area_class_install_cell_property() to install cell
 * properties for a cell area class and gtk_cell_area_class_find_cell_property()
 * or gtk_cell_area_class_list_cell_properties() to get information about
 * existing cell properties.
335 336
 *
 * To set the value of a cell property, use gtk_cell_area_cell_set_property(),
Matthias Clasen's avatar
Matthias Clasen committed
337 338 339
 * gtk_cell_area_cell_set() or gtk_cell_area_cell_set_valist(). To obtain
 * the value of a cell property, use gtk_cell_area_cell_get_property(),
 * gtk_cell_area_cell_get() or gtk_cell_area_cell_get_valist().
340 341
 */

342 343 344 345 346 347
#include "config.h"

#include <stdarg.h>
#include <string.h>
#include <stdlib.h>

348
#include "gtkintl.h"
349 350
#include "gtkcelllayout.h"
#include "gtkcellarea.h"
351
#include "gtkcellareacontext.h"
352
#include "gtkmarshalers.h"
353
#include "gtkprivate.h"
354
#include "gtkrender.h"
355

356 357 358
#include <gobject/gvaluecollector.h>


359 360 361
/* GObjectClass */
static void      gtk_cell_area_dispose                             (GObject            *object);
static void      gtk_cell_area_finalize                            (GObject            *object);
362
static void      gtk_cell_area_set_property                        (GObject            *object,
Matthias Clasen's avatar
Matthias Clasen committed
363 364 365
                                                                    guint               prop_id,
                                                                    const GValue       *value,
                                                                    GParamSpec         *pspec);
366
static void      gtk_cell_area_get_property                        (GObject            *object,
Matthias Clasen's avatar
Matthias Clasen committed
367 368 369
                                                                    guint               prop_id,
                                                                    GValue             *value,
                                                                    GParamSpec         *pspec);
370

371
/* GtkCellAreaClass */
372 373 374 375 376 377 378 379 380 381 382 383 384 385
static void      gtk_cell_area_real_add                            (GtkCellArea         *area,
								    GtkCellRenderer     *renderer);
static void      gtk_cell_area_real_remove                         (GtkCellArea         *area,
								    GtkCellRenderer     *renderer);
static void      gtk_cell_area_real_foreach                        (GtkCellArea         *area,
								    GtkCellCallback      callback,
								    gpointer             callback_data);
static void      gtk_cell_area_real_foreach_alloc                  (GtkCellArea         *area,
								    GtkCellAreaContext  *context,
								    GtkWidget           *widget,
								    const GdkRectangle  *cell_area,
								    const GdkRectangle  *background_area,
								    GtkCellAllocCallback callback,
								    gpointer             callback_data);
386
static gint      gtk_cell_area_real_event                          (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
387 388 389 390 391
                                                                    GtkCellAreaContext   *context,
                                                                    GtkWidget            *widget,
                                                                    GdkEvent             *event,
                                                                    const GdkRectangle   *cell_area,
                                                                    GtkCellRendererState  flags);
392
static void      gtk_cell_area_real_render                         (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
393 394 395 396 397 398 399
                                                                    GtkCellAreaContext   *context,
                                                                    GtkWidget            *widget,
                                                                    cairo_t              *cr,
                                                                    const GdkRectangle   *background_area,
                                                                    const GdkRectangle   *cell_area,
                                                                    GtkCellRendererState  flags,
                                                                    gboolean              paint_focus);
400
static void      gtk_cell_area_real_apply_attributes               (GtkCellArea           *area,
Matthias Clasen's avatar
Matthias Clasen committed
401 402 403 404
                                                                    GtkTreeModel          *tree_model,
                                                                    GtkTreeIter           *iter,
                                                                    gboolean               is_expander,
                                                                    gboolean               is_expanded);
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419

static GtkCellAreaContext *gtk_cell_area_real_create_context       (GtkCellArea           *area);
static GtkCellAreaContext *gtk_cell_area_real_copy_context         (GtkCellArea           *area,
								    GtkCellAreaContext    *context);
static GtkSizeRequestMode  gtk_cell_area_real_get_request_mode     (GtkCellArea           *area);
static void      gtk_cell_area_real_get_preferred_width            (GtkCellArea           *area,
								    GtkCellAreaContext    *context,
								    GtkWidget             *widget,
								    gint                  *minimum_width,
								    gint                  *natural_width);
static void      gtk_cell_area_real_get_preferred_height           (GtkCellArea           *area,
								    GtkCellAreaContext    *context,
								    GtkWidget             *widget,
								    gint                  *minimum_height,
								    gint                  *natural_height);
420
static void      gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea           *area,
Matthias Clasen's avatar
Matthias Clasen committed
421 422 423 424 425
                                                                    GtkCellAreaContext    *context,
                                                                    GtkWidget             *widget,
                                                                    gint                   width,
                                                                    gint                  *minimum_height,
                                                                    gint                  *natural_height);
426
static void      gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea           *area,
Matthias Clasen's avatar
Matthias Clasen committed
427 428 429 430 431
                                                                    GtkCellAreaContext    *context,
                                                                    GtkWidget             *widget,
                                                                    gint                   height,
                                                                    gint                  *minimum_width,
                                                                    gint                  *natural_width);
432
static gboolean  gtk_cell_area_real_is_activatable                 (GtkCellArea           *area);
433
static gboolean  gtk_cell_area_real_activate                       (GtkCellArea           *area,
Matthias Clasen's avatar
Matthias Clasen committed
434 435 436 437 438
                                                                    GtkCellAreaContext    *context,
                                                                    GtkWidget             *widget,
                                                                    const GdkRectangle    *cell_area,
                                                                    GtkCellRendererState   flags,
                                                                    gboolean               edit_only);
439 440
static gboolean  gtk_cell_area_real_focus                          (GtkCellArea           *area,
								    GtkDirectionType       direction);
441 442 443 444

/* GtkCellLayoutIface */
static void      gtk_cell_area_cell_layout_init              (GtkCellLayoutIface    *iface);
static void      gtk_cell_area_pack_default                  (GtkCellLayout         *cell_layout,
Matthias Clasen's avatar
Matthias Clasen committed
445 446
                                                              GtkCellRenderer       *renderer,
                                                              gboolean               expand);
447 448
static void      gtk_cell_area_clear                         (GtkCellLayout         *cell_layout);
static void      gtk_cell_area_add_attribute                 (GtkCellLayout         *cell_layout,
Matthias Clasen's avatar
Matthias Clasen committed
449 450 451
                                                              GtkCellRenderer       *renderer,
                                                              const gchar           *attribute,
                                                              gint                   column);
452
static void      gtk_cell_area_set_cell_data_func            (GtkCellLayout         *cell_layout,
Matthias Clasen's avatar
Matthias Clasen committed
453 454 455 456
                                                              GtkCellRenderer       *cell,
                                                              GtkCellLayoutDataFunc  func,
                                                              gpointer               func_data,
                                                              GDestroyNotify         destroy);
457
static void      gtk_cell_area_clear_attributes              (GtkCellLayout         *cell_layout,
Matthias Clasen's avatar
Matthias Clasen committed
458
                                                              GtkCellRenderer       *renderer);
459
static void      gtk_cell_area_reorder                       (GtkCellLayout         *cell_layout,
Matthias Clasen's avatar
Matthias Clasen committed
460 461
                                                              GtkCellRenderer       *cell,
                                                              gint                   position);
462
static GList    *gtk_cell_area_get_cells                     (GtkCellLayout         *cell_layout);
463
static GtkCellArea *gtk_cell_area_get_area                   (GtkCellLayout         *cell_layout);
464

465 466 467
/* GtkBuildableIface */
static void      gtk_cell_area_buildable_init                (GtkBuildableIface     *iface);
static void      gtk_cell_area_buildable_custom_tag_end      (GtkBuildable          *buildable,
Matthias Clasen's avatar
Matthias Clasen committed
468 469 470 471
                                                              GtkBuilder            *builder,
                                                              GObject               *child,
                                                              const gchar           *tagname,
                                                              gpointer              *data);
472

473
/* Used in foreach loop to check if a child renderer is present */
474 475 476 477 478
typedef struct {
  GtkCellRenderer *renderer;
  gboolean         has_renderer;
} HasRendererCheck;

479 480 481 482 483 484
/* Used in foreach loop to get a cell's allocation */
typedef struct {
  GtkCellRenderer *renderer;
  GdkRectangle     allocation;
} RendererAllocationData;

485 486 487 488 489 490 491 492 493 494 495 496
/* Used in foreach loop to render cells */
typedef struct {
  GtkCellArea         *area;
  GtkWidget           *widget;
  cairo_t             *cr;
  GdkRectangle         focus_rect;
  GtkCellRendererState render_flags;
  guint                paint_focus : 1;
  guint                focus_all   : 1;
  guint                first_focus : 1;
} CellRenderData;

497 498 499 500 501 502 503 504
/* Used in foreach loop to get a cell by position */
typedef struct {
  gint             x;
  gint             y;
  GtkCellRenderer *renderer;
  GdkRectangle     cell_area;
} CellByPositionData;

505
/* Attribute/Cell metadata */
506
typedef struct {
507 508 509 510 511
  const gchar *attribute;
  gint         column;
} CellAttribute;

typedef struct {
512
  GSList          *attributes;
513 514 515 516

  GtkCellLayoutDataFunc  func;
  gpointer               data;
  GDestroyNotify         destroy;
517
  GtkCellLayout         *proxy;
518 519 520
} CellInfo;

static CellInfo       *cell_info_new       (GtkCellLayoutDataFunc  func,
Matthias Clasen's avatar
Matthias Clasen committed
521 522
                                            gpointer               data,
                                            GDestroyNotify         destroy);
523 524
static void            cell_info_free      (CellInfo              *info);
static CellAttribute  *cell_attribute_new  (GtkCellRenderer       *renderer,
Matthias Clasen's avatar
Matthias Clasen committed
525 526
                                            const gchar           *attribute,
                                            gint                   column);
527
static void            cell_attribute_free (CellAttribute         *attribute);
528
static gint            cell_attribute_find (CellAttribute         *cell_attribute,
Matthias Clasen's avatar
Matthias Clasen committed
529
                                            const gchar           *attribute);
530

531 532
/* Internal functions/signal emissions */
static void            gtk_cell_area_add_editable     (GtkCellArea        *area,
Matthias Clasen's avatar
Matthias Clasen committed
533 534 535
                                                       GtkCellRenderer    *renderer,
                                                       GtkCellEditable    *editable,
                                                       const GdkRectangle *cell_area);
536
static void            gtk_cell_area_remove_editable  (GtkCellArea        *area,
Matthias Clasen's avatar
Matthias Clasen committed
537 538
                                                       GtkCellRenderer    *renderer,
                                                       GtkCellEditable    *editable);
539
static void            gtk_cell_area_set_edit_widget  (GtkCellArea        *area,
Matthias Clasen's avatar
Matthias Clasen committed
540
                                                       GtkCellEditable    *editable);
541
static void            gtk_cell_area_set_edited_cell  (GtkCellArea        *area,
Matthias Clasen's avatar
Matthias Clasen committed
542
                                                       GtkCellRenderer    *renderer);
543 544


Matthias Clasen's avatar
Matthias Clasen committed
545
/* Struct to pass data along while looping over
546
 * cell renderers to apply attributes
547 548 549 550 551
 */
typedef struct {
  GtkCellArea  *area;
  GtkTreeModel *model;
  GtkTreeIter  *iter;
552 553
  gboolean      is_expander;
  gboolean      is_expanded;
554 555 556 557
} AttributeData;

struct _GtkCellAreaPrivate
{
Matthias Clasen's avatar
Matthias Clasen committed
558
  /* The GtkCellArea bookkeeps any connected
559 560
   * attributes in this hash table.
   */
561
  GHashTable      *cell_info;
562

563
  /* Current path is saved as a side-effect
Matthias Clasen's avatar
Matthias Clasen committed
564 565
   * of gtk_cell_area_apply_attributes()
   */
566 567
  gchar           *current_path;

568 569
  /* Current cell being edited and editable widget used */
  GtkCellEditable *edit_widget;
570
  GtkCellRenderer *edited_cell;
571 572 573 574 575

  /* Signal connections to the editable widget */
  gulong           remove_widget_id;

  /* Currently focused cell */
576
  GtkCellRenderer *focus_cell;
577 578 579

  /* Tracking which cells are focus siblings of focusable cells */
  GHashTable      *focus_siblings;
580
};
581

582 583
enum {
  PROP_0,
584
  PROP_FOCUS_CELL,
585 586
  PROP_EDITED_CELL,
  PROP_EDIT_WIDGET
587
};
588

589
enum {
590
  SIGNAL_APPLY_ATTRIBUTES,
591
  SIGNAL_ADD_EDITABLE,
592
  SIGNAL_REMOVE_EDITABLE,
593
  SIGNAL_FOCUS_CHANGED,
594 595 596 597
  LAST_SIGNAL
};

/* Keep the paramspec pool internal, no need to deliver notifications
Matthias Clasen's avatar
Matthias Clasen committed
598 599
 * on cells. at least no perceived need for now
 */
600 601 602 603 604 605
static GParamSpecPool *cell_property_pool = NULL;
static guint           cell_area_signals[LAST_SIGNAL] = { 0 };

#define PARAM_SPEC_PARAM_ID(pspec)              ((pspec)->param_id)
#define PARAM_SPEC_SET_PARAM_ID(pspec, id)      ((pspec)->param_id = (id))

606
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
607
                                  G_ADD_PRIVATE (GtkCellArea)
Matthias Clasen's avatar
Matthias Clasen committed
608 609 610 611
                                  G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
                                                         gtk_cell_area_cell_layout_init)
                                  G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                         gtk_cell_area_buildable_init))
612 613 614 615

static void
gtk_cell_area_init (GtkCellArea *area)
{
616
  GtkCellAreaPrivate *priv;
617

618
  area->priv = gtk_cell_area_get_instance_private (area);
619 620
  priv = area->priv;

Matthias Clasen's avatar
Matthias Clasen committed
621 622 623 624
  priv->cell_info = g_hash_table_new_full (g_direct_hash,
                                           g_direct_equal,
                                           NULL,
                                           (GDestroyNotify)cell_info_free);
625

Matthias Clasen's avatar
Matthias Clasen committed
626 627 628 629
  priv->focus_siblings = g_hash_table_new_full (g_direct_hash,
                                                g_direct_equal,
                                                NULL,
                                                (GDestroyNotify)g_list_free);
630

631
  priv->focus_cell         = NULL;
632 633 634 635
  priv->edited_cell        = NULL;
  priv->edit_widget        = NULL;

  priv->remove_widget_id   = 0;
636
}
637

Matthias Clasen's avatar
Matthias Clasen committed
638
static void
639
gtk_cell_area_class_init (GtkCellAreaClass *class)
640
{
641
  GObjectClass *object_class = G_OBJECT_CLASS (class);
Matthias Clasen's avatar
Matthias Clasen committed
642

643
  /* GObjectClass */
644 645 646 647
  object_class->dispose      = gtk_cell_area_dispose;
  object_class->finalize     = gtk_cell_area_finalize;
  object_class->get_property = gtk_cell_area_get_property;
  object_class->set_property = gtk_cell_area_set_property;
648

649
  /* general */
650 651 652 653
  class->add              = gtk_cell_area_real_add;
  class->remove           = gtk_cell_area_real_remove;
  class->foreach          = gtk_cell_area_real_foreach;
  class->foreach_alloc    = gtk_cell_area_real_foreach_alloc;
654
  class->event            = gtk_cell_area_real_event;
655
  class->render           = gtk_cell_area_real_render;
656
  class->apply_attributes = gtk_cell_area_real_apply_attributes;
657 658

  /* geometry */
659 660 661 662 663
  class->create_context                 = gtk_cell_area_real_create_context;
  class->copy_context                   = gtk_cell_area_real_copy_context;
  class->get_request_mode               = gtk_cell_area_real_get_request_mode;
  class->get_preferred_width            = gtk_cell_area_real_get_preferred_width;
  class->get_preferred_height           = gtk_cell_area_real_get_preferred_height;
664 665
  class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
  class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
666

667
  /* focus */
668 669
  class->is_activatable = gtk_cell_area_real_is_activatable;
  class->activate       = gtk_cell_area_real_activate;
670
  class->focus          = gtk_cell_area_real_focus;
671 672

  /* Signals */
673 674 675 676 677 678 679 680 681
  /**
   * GtkCellArea::apply-attributes:
   * @area: the #GtkCellArea to apply the attributes to
   * @model: the #GtkTreeModel to apply the attributes from
   * @iter: the #GtkTreeIter indicating which row to apply the attributes of
   * @is_expander: whether the view shows children for this row
   * @is_expanded: whether the view is currently showing the children of this row
   *
   * This signal is emitted whenever applying attributes to @area from @model
682 683
   *
   * Since: 3.0
684 685 686
   */
  cell_area_signals[SIGNAL_APPLY_ATTRIBUTES] =
    g_signal_new (I_("apply-attributes"),
Matthias Clasen's avatar
Matthias Clasen committed
687 688 689 690 691 692 693 694 695 696
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkCellAreaClass, apply_attributes),
                  NULL, NULL,
                  _gtk_marshal_VOID__OBJECT_BOXED_BOOLEAN_BOOLEAN,
                  G_TYPE_NONE, 4,
                  GTK_TYPE_TREE_MODEL,
                  GTK_TYPE_TREE_ITER,
                  G_TYPE_BOOLEAN,
                  G_TYPE_BOOLEAN);
697 698
  g_signal_set_va_marshaller (cell_area_signals[SIGNAL_APPLY_ATTRIBUTES], G_TYPE_FROM_CLASS (class),
                              _gtk_marshal_VOID__OBJECT_BOXED_BOOLEAN_BOOLEANv);
699

700 701 702 703 704 705 706
  /**
   * GtkCellArea::add-editable:
   * @area: the #GtkCellArea where editing started
   * @renderer: the #GtkCellRenderer that started the edited
   * @editable: the #GtkCellEditable widget to add
   * @cell_area: the #GtkWidget relative #GdkRectangle coordinates
   *             where @editable should be added
707
   * @path: the #GtkTreePath string this edit was initiated for
708 709
   *
   * Indicates that editing has started on @renderer and that @editable
710
   * should be added to the owning cell-layouting widget at @cell_area.
711 712
   *
   * Since: 3.0
713
   */
714 715
  cell_area_signals[SIGNAL_ADD_EDITABLE] =
    g_signal_new (I_("add-editable"),
Matthias Clasen's avatar
Matthias Clasen committed
716 717 718 719 720 721 722 723 724 725
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_FIRST,
                  0, /* No class closure here */
                  NULL, NULL,
                  _gtk_marshal_VOID__OBJECT_OBJECT_BOXED_STRING,
                  G_TYPE_NONE, 4,
                  GTK_TYPE_CELL_RENDERER,
                  GTK_TYPE_CELL_EDITABLE,
                  GDK_TYPE_RECTANGLE,
                  G_TYPE_STRING);
726

727 728 729 730 731 732 733 734

  /**
   * GtkCellArea::remove-editable:
   * @area: the #GtkCellArea where editing finished
   * @renderer: the #GtkCellRenderer that finished editeding
   * @editable: the #GtkCellEditable widget to remove
   *
   * Indicates that editing finished on @renderer and that @editable
735
   * should be removed from the owning cell-layouting widget.
736 737
   *
   * Since: 3.0
738
   */
739 740
  cell_area_signals[SIGNAL_REMOVE_EDITABLE] =
    g_signal_new (I_("remove-editable"),
Matthias Clasen's avatar
Matthias Clasen committed
741 742 743 744 745 746 747 748
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_FIRST,
                  0, /* No class closure here */
                  NULL, NULL,
                  _gtk_marshal_VOID__OBJECT_OBJECT,
                  G_TYPE_NONE, 2,
                  GTK_TYPE_CELL_RENDERER,
                  GTK_TYPE_CELL_EDITABLE);
749

750 751 752 753 754 755 756 757 758 759 760 761 762 763
  /**
   * GtkCellArea::focus-changed:
   * @area: the #GtkCellArea where focus changed
   * @renderer: the #GtkCellRenderer that has focus
   * @path: the current #GtkTreePath string set for @area
   *
   * Indicates that focus changed on this @area. This signal
   * is emitted either as a result of focus handling or event
   * handling.
   *
   * It's possible that the signal is emitted even if the
   * currently focused renderer did not change, this is
   * because focus may change to the same renderer in the
   * same cell area for a different row of data.
764 765
   *
   * Since: 3.0
766
   */
767 768
  cell_area_signals[SIGNAL_FOCUS_CHANGED] =
    g_signal_new (I_("focus-changed"),
Matthias Clasen's avatar
Matthias Clasen committed
769 770 771 772 773 774 775 776
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_FIRST,
                  0, /* No class closure here */
                  NULL, NULL,
                  _gtk_marshal_VOID__OBJECT_STRING,
                  G_TYPE_NONE, 2,
                  GTK_TYPE_CELL_RENDERER,
                  G_TYPE_STRING);
777

778
  /* Properties */
779 780 781 782
  /**
   * GtkCellArea:focus-cell:
   *
   * The cell in the area that currently has focus
783 784
   *
   * Since: 3.0
785
   */
786 787 788
  g_object_class_install_property (object_class,
                                   PROP_FOCUS_CELL,
                                   g_param_spec_object
Matthias Clasen's avatar
Matthias Clasen committed
789 790 791 792 793
                                   ("focus-cell",
                                    P_("Focus Cell"),
                                    P_("The cell which currently has focus"),
                                    GTK_TYPE_CELL_RENDERER,
                                    GTK_PARAM_READWRITE));
794

795 796 797 798 799 800 801
  /**
   * GtkCellArea:edited-cell:
   *
   * The cell in the area that is currently edited
   *
   * This property is read-only and only changes as
   * a result of a call gtk_cell_area_activate_cell().
802 803
   *
   * Since: 3.0
804
   */
805 806 807
  g_object_class_install_property (object_class,
                                   PROP_EDITED_CELL,
                                   g_param_spec_object
Matthias Clasen's avatar
Matthias Clasen committed
808 809 810 811 812
                                   ("edited-cell",
                                    P_("Edited Cell"),
                                    P_("The cell which is currently being edited"),
                                    GTK_TYPE_CELL_RENDERER,
                                    G_PARAM_READABLE));
813

814 815 816 817 818 819 820
  /**
   * GtkCellArea:edit-widget:
   *
   * The widget currently editing the edited cell
   *
   * This property is read-only and only changes as
   * a result of a call gtk_cell_area_activate_cell().
821 822
   *
   * Since: 3.0
823
   */
824 825 826
  g_object_class_install_property (object_class,
                                   PROP_EDIT_WIDGET,
                                   g_param_spec_object
Matthias Clasen's avatar
Matthias Clasen committed
827 828 829
                                   ("edit-widget",
                                    P_("Edit Widget"),
                                    P_("The widget currently editing the edited cell"),
830
                                    GTK_TYPE_CELL_EDITABLE,
Matthias Clasen's avatar
Matthias Clasen committed
831
                                    G_PARAM_READABLE));
832

833
  /* Pool for Cell Properties */
834 835
  if (!cell_property_pool)
    cell_property_pool = g_param_spec_pool_new (FALSE);
836 837 838
}

/*************************************************************
839
 *                    CellInfo Basics                        *
840
 *************************************************************/
841 842
static CellInfo *
cell_info_new (GtkCellLayoutDataFunc  func,
Matthias Clasen's avatar
Matthias Clasen committed
843 844
               gpointer               data,
               GDestroyNotify         destroy)
845
{
846 847 848 849 850
  CellInfo *info = g_slice_new0 (CellInfo);

  info->func     = func;
  info->data     = data;
  info->destroy  = destroy;
851

852
  return info;
853 854 855
}

static void
856 857 858 859 860
cell_info_free (CellInfo *info)
{
  if (info->destroy)
    info->destroy (info->data);

Matthias Clasen's avatar
Matthias Clasen committed
861
  g_slist_free_full (info->attributes, (GDestroyNotify)cell_attribute_free);
862 863 864 865 866 867

  g_slice_free (CellInfo, info);
}

static CellAttribute  *
cell_attribute_new  (GtkCellRenderer       *renderer,
Matthias Clasen's avatar
Matthias Clasen committed
868 869
                     const gchar           *attribute,
                     gint                   column)
870
{
871 872 873 874
  GParamSpec *pspec;

  /* Check if the attribute really exists and point to
   * the property string installed on the cell renderer
Matthias Clasen's avatar
Matthias Clasen committed
875
   * class (dont dup the string)
876 877 878 879 880 881
   */
  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), attribute);

  if (pspec)
    {
      CellAttribute *cell_attribute = g_slice_new (CellAttribute);
882

883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
      cell_attribute->attribute = pspec->name;
      cell_attribute->column    = column;

      return cell_attribute;
    }

  return NULL;
}

static void
cell_attribute_free (CellAttribute *attribute)
{
  g_slice_free (CellAttribute, attribute);
}

898
/* GCompareFunc for g_slist_find_custom() */
899 900
static gint
cell_attribute_find (CellAttribute *cell_attribute,
Matthias Clasen's avatar
Matthias Clasen committed
901
                     const gchar   *attribute)
902 903
{
  return g_strcmp0 (cell_attribute->attribute, attribute);
904 905
}

906 907 908 909 910 911 912 913 914 915
/*************************************************************
 *                      GObjectClass                         *
 *************************************************************/
static void
gtk_cell_area_finalize (GObject *object)
{
  GtkCellArea        *area   = GTK_CELL_AREA (object);
  GtkCellAreaPrivate *priv   = area->priv;

  /* All cell renderers should already be removed at this point,
Matthias Clasen's avatar
Matthias Clasen committed
916
   * just kill our (empty) hash tables here.
917 918
   */
  g_hash_table_destroy (priv->cell_info);
919
  g_hash_table_destroy (priv->focus_siblings);
920

921 922
  g_free (priv->current_path);

923 924 925 926 927 928 929 930
  G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
}


static void
gtk_cell_area_dispose (GObject *object)
{
  /* This removes every cell renderer that may be added to the GtkCellArea,
Matthias Clasen's avatar
Matthias Clasen committed
931
   * subclasses should be breaking references to the GtkCellRenderers
932 933 934 935
   * at this point.
   */
  gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));

936
  /* Remove any ref to a focused/edited cell */
937
  gtk_cell_area_set_focus_cell (GTK_CELL_AREA (object), NULL);
938
  gtk_cell_area_set_edited_cell (GTK_CELL_AREA (object), NULL);
939
  gtk_cell_area_set_edit_widget (GTK_CELL_AREA (object), NULL);
940

941 942 943
  G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
}

944 945
static void
gtk_cell_area_set_property (GObject       *object,
Matthias Clasen's avatar
Matthias Clasen committed
946 947 948
                            guint          prop_id,
                            const GValue  *value,
                            GParamSpec    *pspec)
949 950 951 952 953
{
  GtkCellArea *area = GTK_CELL_AREA (object);

  switch (prop_id)
    {
954 955 956
    case PROP_FOCUS_CELL:
      gtk_cell_area_set_focus_cell (area, (GtkCellRenderer *)g_value_get_object (value));
      break;
957 958 959 960 961 962 963 964
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_cell_area_get_property (GObject     *object,
Matthias Clasen's avatar
Matthias Clasen committed
965 966 967
                            guint        prop_id,
                            GValue      *value,
                            GParamSpec  *pspec)
968 969 970 971 972 973
{
  GtkCellArea        *area = GTK_CELL_AREA (object);
  GtkCellAreaPrivate *priv = area->priv;

  switch (prop_id)
    {
974 975 976
    case PROP_FOCUS_CELL:
      g_value_set_object (value, priv->focus_cell);
      break;
977 978 979 980 981 982
    case PROP_EDITED_CELL:
      g_value_set_object (value, priv->edited_cell);
      break;
    case PROP_EDIT_WIDGET:
      g_value_set_object (value, priv->edit_widget);
      break;
983 984 985 986 987
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}
988 989 990 991

/*************************************************************
 *                    GtkCellAreaClass                       *
 *************************************************************/
992 993 994 995
static void
gtk_cell_area_real_add (GtkCellArea         *area,
			GtkCellRenderer     *renderer)
{
996
    g_warning ("GtkCellAreaClass::add not implemented for '%s'",
997 998 999 1000 1001 1002 1003
               g_type_name (G_TYPE_FROM_INSTANCE (area)));
}

static void      
gtk_cell_area_real_remove (GtkCellArea         *area,
			   GtkCellRenderer     *renderer)
{
1004
    g_warning ("GtkCellAreaClass::remove not implemented for '%s'",
1005 1006 1007 1008 1009 1010 1011 1012
               g_type_name (G_TYPE_FROM_INSTANCE (area)));
}

static void
gtk_cell_area_real_foreach (GtkCellArea         *area,
			    GtkCellCallback      callback,
			    gpointer             callback_data)
{
1013
    g_warning ("GtkCellAreaClass::foreach not implemented for '%s'",
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
               g_type_name (G_TYPE_FROM_INSTANCE (area)));
}

static void
gtk_cell_area_real_foreach_alloc (GtkCellArea         *area,
				  GtkCellAreaContext  *context,
				  GtkWidget           *widget,
				  const GdkRectangle  *cell_area,
				  const GdkRectangle  *background_area,
				  GtkCellAllocCallback callback,
				  gpointer             callback_data)
{
1026
    g_warning ("GtkCellAreaClass::foreach_alloc not implemented for '%s'",
1027 1028 1029
               g_type_name (G_TYPE_FROM_INSTANCE (area)));
}

1030 1031
static gint
gtk_cell_area_real_event (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
1032 1033 1034 1035 1036
                          GtkCellAreaContext   *context,
                          GtkWidget            *widget,
                          GdkEvent             *event,
                          const GdkRectangle   *cell_area,
                          GtkCellRendererState  flags)
1037
{
1038
  GtkCellAreaPrivate *priv = area->priv;
1039
  gboolean            retval = FALSE;
1040

1041
  if (event->type == GDK_KEY_PRESS && (flags & GTK_CELL_RENDERER_FOCUSED) != 0)
1042
    {
1043
      GdkEventKey *key_event = (GdkEventKey *)event;
1044

1045 1046
      /* Cancel any edits in progress */
      if (priv->edited_cell && (key_event->keyval == GDK_KEY_Escape))
Matthias Clasen's avatar
Matthias Clasen committed
1047 1048 1049 1050
        {
          gtk_cell_area_stop_editing (area, TRUE);
          retval = TRUE;
        }
1051
    }
1052 1053 1054
  else if (event->type == GDK_BUTTON_PRESS)
    {
      GdkEventButton *button_event = (GdkEventButton *)event;
1055

1056
      if (button_event->button == GDK_BUTTON_PRIMARY)
Matthias Clasen's avatar
Matthias Clasen committed
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
        {
          GtkCellRenderer *renderer = NULL;
          GtkCellRenderer *focus_renderer;
          GdkRectangle     alloc_area;
          gint             event_x, event_y;

          /* We may need some semantics to tell us the offset of the event
           * window we are handling events for (i.e. GtkTreeView has a bin_window) */
          event_x = button_event->x;
          event_y = button_event->y;

          /* Dont try to search for an event coordinate that is not in the area, that will
           * trigger a runtime warning.
           */
          if (event_x >= cell_area->x && event_x <= cell_area->x + cell_area->width &&
              event_y >= cell_area->y && event_y <= cell_area->y + cell_area->height)
            renderer =
              gtk_cell_area_get_cell_at_position (area, context, widget,
                                                  cell_area, event_x, event_y,
                                                  &alloc_area);

          if (renderer)
            {
              focus_renderer = gtk_cell_area_get_focus_from_sibling (area, renderer);
              if (!focus_renderer)
                focus_renderer = renderer;

              /* If we're already editing, cancel it and set focus */
              if (gtk_cell_area_get_edited_cell (area))
                {
                  /* XXX Was it really canceled in this case ? */
                  gtk_cell_area_stop_editing (area, TRUE);
                  gtk_cell_area_set_focus_cell (area, focus_renderer);
                  retval = TRUE;
                }
              else
                {
                  /* If we are activating via a focus sibling,
                   * we need to fetch the right cell area for the real event renderer */
                  if (focus_renderer != renderer)
                    gtk_cell_area_get_cell_allocation (area, context, widget, focus_renderer,
                                                       cell_area, &alloc_area);

                  gtk_cell_area_set_focus_cell (area, focus_renderer);
                  retval = gtk_cell_area_activate_cell (area, widget, focus_renderer,
                                                        event, &alloc_area, flags);
                }
            }
        }
1106 1107 1108
    }

  return retval;
1109 1110
}

1111 1112
static gboolean
render_cell (GtkCellRenderer        *renderer,
Matthias Clasen's avatar
Matthias Clasen committed
1113 1114 1115
             const GdkRectangle     *cell_area,
             const GdkRectangle     *cell_background,
             CellRenderData         *data)
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126
{
  GtkCellRenderer      *focus_cell;
  GtkCellRendererState  flags;
  GdkRectangle          inner_area;

  focus_cell = gtk_cell_area_get_focus_cell (data->area);
  flags      = data->render_flags;

  gtk_cell_area_inner_cell_area (data->area, data->widget, cell_area, &inner_area);

  if ((flags & GTK_CELL_RENDERER_FOCUSED) &&
Matthias Clasen's avatar
Matthias Clasen committed
1127 1128 1129 1130
      (data->focus_all ||
       (focus_cell &&
        (renderer == focus_cell ||
         gtk_cell_area_is_focus_sibling (data->area, focus_cell, renderer)))))
1131 1132 1133 1134 1135 1136
    {
      GdkRectangle cell_focus;

      gtk_cell_renderer_get_aligned_area (renderer, data->widget, flags, &inner_area, &cell_focus);

      if (data->first_focus)
Matthias Clasen's avatar
Matthias Clasen committed
1137 1138 1139 1140
        {
          data->first_focus = FALSE;
          data->focus_rect  = cell_focus;
        }
1141
      else
Matthias Clasen's avatar
Matthias Clasen committed
1142 1143 1144
        {
          gdk_rectangle_union (&data->focus_rect, &cell_focus, &data->focus_rect);
        }
1145 1146 1147
    }

  gtk_cell_renderer_render (renderer, data->cr, data->widget,
Matthias Clasen's avatar
Matthias Clasen committed
1148
                            cell_background, &inner_area, flags);
1149 1150 1151 1152 1153 1154

  return FALSE;
}

static void
gtk_cell_area_real_render (GtkCellArea          *area,
Matthias Clasen's avatar
Matthias Clasen committed
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
                           GtkCellAreaContext   *context,
                           GtkWidget            *widget,
                           cairo_t              *cr,
                           const GdkRectangle   *background_area,
                           const GdkRectangle   *cell_area,
                           GtkCellRendererState  flags,
                           gboolean              paint_focus)
{
  CellRenderData render_data =
    {
      area,
      widget,
      cr,
      { 0, },
      flags,
      paint_focus,
1171 1172 1173 1174
      FALSE, TRUE
    };

  /* Make sure we dont paint a focus rectangle while there
Matthias Clasen's avatar
Matthias Clasen committed
1175
   * is an editable widget in play
1176 1177 1178 1179
   */
  if (gtk_cell_area_get_edited_cell (area))
    render_data.paint_focus = FALSE;

1180 1181 1182
  if (!gtk_widget_has_visible_focus (widget))
    render_data.paint_focus = FALSE;

1183 1184
  /* If no cell can activate but the caller wants focus painted,
   * then we paint focus around all cells */
Matthias Clasen's avatar
Matthias Clasen committed
1185
  if ((flags & GTK_CELL_RENDERER_FOCUSED) != 0 && paint_focus &&
1186 1187 1188
      !gtk_cell_area_is_activatable (area))
    render_data.focus_all = TRUE;

Matthias Clasen's avatar
Matthias Clasen committed
1189 1190
  gtk_cell_area_foreach_alloc (area, context, widget, cell_area, background_area,
                               (GtkCellAllocCallback)render_cell, &render_data);
1191

Matthias Clasen's avatar
Matthias Clasen committed
1192 1193
  if (render_data.paint_focus &&
      render_data.focus_rect.width != 0 &&
1194 1195
      render_data.focus_rect.height != 0)
    {
1196 1197 1198 1199 1200 1201 1202 1203
      GtkStyleContext *style_context;
      GtkStateFlags renderer_state = 0;

      style_context = gtk_widget_get_style_context (widget);
      gtk_style_context_save (style_context);

      renderer_state = gtk_cell_renderer_get_state (NULL, widget, flags);
      gtk_style_context_set_state (style_context, renderer_state);
1204

1205 1206 1207 1208 1209
      cairo_save (cr);

      gdk_cairo_rectangle (cr, background_area);
      cairo_clip (cr);

1210 1211 1212
      gtk_render_focus (style_context, cr,
                        render_data.focus_rect.x,     render_data.focus_rect.y,
                        render_data.focus_rect.width, render_data.focus_rect.height);
1213

1214
      gtk_style_context_restore (style_context);
1215
      cairo_restore (cr);
1216 1217 1218
    }
}

1219 1220
static void
apply_cell_attributes (GtkCellRenderer *renderer,
Matthias Clasen's avatar
Matthias Clasen committed
1221 1222
                       CellInfo        *info,
                       AttributeData   *data)
1223 1224 1225
{
  CellAttribute *attribute;
  GSList        *list;
Javier Jardón's avatar
Javier Jardón committed
1226
  GValue         value = G_VALUE_INIT;
1227 1228 1229 1230 1231
  gboolean       is_expander;
  gboolean       is_expanded;

  g_object_freeze_notify (G_OBJECT (renderer));

Matthias Clasen's avatar
Matthias Clasen committed
1232
  /* Whether a row expands or is presently expanded can only be
1233 1234 1235 1236 1237 1238
   * provided by the view (as these states can vary across views
   * accessing the same model).
   */
  g_object_get (renderer, "is-expander", &is_expander, NULL);
  if (is_expander != data->is_expander)
    g_object_set (renderer, "is-expander", data->is_expander, NULL);
Matthias Clasen's avatar
Matthias Clasen committed
1239

1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
  g_object_get (renderer, "is-expanded", &is_expanded, NULL);
  if (is_expanded != data->is_expanded)
    g_object_set (renderer, "is-expanded", data->is_expanded, NULL);

  /* Apply the attributes directly to the renderer */
  for (list = info->attributes; list; list = list->next)
    {
      attribute = list->data;

      gtk_tree_model_get_value (data->model, data->iter, attribute->column, &value);
      g_object_set_property (G_OBJECT (renderer), attribute->attribute, &value);
      g_value_unset (&value);
    }

  /* Call any GtkCellLayoutDataFunc that may have been set by the user
   */
  if (info->func)
1257 1258
    info->func (info->proxy ? info->proxy : GTK_CELL_LAYOUT (data->area), renderer,
		data->model, data->iter, info->data);
1259 1260 1261 1262 1263 1264

  g_object_thaw_notify (G_OBJECT (renderer));
}

static void
gtk_cell_area_real_apply_attributes (GtkCellArea           *area,
Matthias Clasen's avatar
Matthias Clasen committed
1265 1266 1267 1268
                                     GtkTreeModel          *tree_model,
                                     GtkTreeIter           *iter,
                                     gboolean               is_expander,
                                     gboolean               is_expanded)
1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294
{

  GtkCellAreaPrivate *priv;
  AttributeData       data;
  GtkTreePath        *path;

  priv = area->priv;

  /* Feed in data needed to apply to every renderer */
  data.area        = area;
  data.model       = tree_model;
  data.iter        = iter;
  data.is_expander = is_expander;
  data.is_expanded = is_expanded;

  /* Go over any cells that have attributes or custom GtkCellLayoutDataFuncs and
   * apply the data from the treemodel */
  g_hash_table_foreach (priv->cell_info, (GHFunc)apply_cell_attributes, &data);

  /* Update the currently applied path */
  g_free (priv->current_path);
  path               = gtk_tree_model_get_path (tree_model, iter);
  priv->current_path = gtk_tree_path_to_string (path);
  gtk_tree_path_free (