gtktreeviewcolumn.c 96.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* gtktreeviewcolumn.c
 * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.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
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 17
 */

18
#include "config.h"
19

20
#include "gtktreeviewcolumn.h"
21 22 23

#include <string.h>

24
#include "gtktreeview.h"
25
#include "gtktreeprivate.h"
26
#include "gtkcelllayout.h"
27
#include "gtkbutton.h"
Matthias Clasen's avatar
Matthias Clasen committed
28
#include "deprecated/gtkalignment.h"
29
#include "gtklabel.h"
Matthias Clasen's avatar
Matthias Clasen committed
30
#include "gtkbox.h"
31
#include "gtkmarshalers.h"
32
#include "gtkimage.h"
33 34
#include "gtkcellareacontext.h"
#include "gtkcellareabox.h"
35
#include "gtkprivate.h"
36
#include "gtkintl.h"
37
#include "gtktypebuiltins.h"
38
#include "a11y/gtktreeviewaccessibleprivate.h"
39

Jonathan Blandford's avatar
Jonathan Blandford committed
40

41 42 43 44
/**
 * SECTION:gtktreeviewcolumn
 * @Short_description: A visible column in a GtkTreeView widget
 * @Title: GtkTreeViewColumn
45
 * @See_also: #GtkTreeView, #GtkTreeSelection, #GtkTreeModel, #GtkTreeSortable,
46
 *   #GtkTreeModelSort, #GtkListStore, #GtkTreeStore, #GtkCellRenderer, #GtkCellEditable,
47
 *   #GtkCellRendererPixbuf, #GtkCellRendererText, #GtkCellRendererToggle,
48
 *   [GtkTreeView drag-and-drop][gtk3-GtkTreeView-drag-and-drop]
49 50 51 52 53
 *
 * The GtkTreeViewColumn object represents a visible column in a #GtkTreeView widget.
 * It allows to set properties of the column header, and functions as a holding pen for
 * the cell renderers which determine how the data in the column is displayed.
 *
54
 * Please refer to the [tree widget conceptual overview][TreeWidget]
55 56 57 58 59
 * for an overview of all the objects and data types related to the tree widget and how
 * they work together.
 */


60
/* Type methods */
61
static void gtk_tree_view_column_cell_layout_init              (GtkCellLayoutIface      *iface);
62 63 64 65 66 67 68 69 70 71 72

/* GObject methods */
static void gtk_tree_view_column_set_property                  (GObject                 *object,
								guint                    prop_id,
								const GValue            *value,
								GParamSpec              *pspec);
static void gtk_tree_view_column_get_property                  (GObject                 *object,
								guint                    prop_id,
								GValue                  *value,
								GParamSpec              *pspec);
static void gtk_tree_view_column_finalize                      (GObject                 *object);
73
static void gtk_tree_view_column_dispose                       (GObject                 *object);
74
static void gtk_tree_view_column_constructed                   (GObject                 *object);
75

76
/* GtkCellLayout implementation */
77 78 79
static void       gtk_tree_view_column_ensure_cell_area        (GtkTreeViewColumn      *column,
                                                                GtkCellArea            *cell_area);

80
static GtkCellArea *gtk_tree_view_column_cell_layout_get_area  (GtkCellLayout           *cell_layout);
81

82
/* Button handling code */
83 84 85 86
static void gtk_tree_view_column_create_button                 (GtkTreeViewColumn       *tree_column);
static void gtk_tree_view_column_update_button                 (GtkTreeViewColumn       *tree_column);

/* Button signal handlers */
87
static gint gtk_tree_view_column_button_event                  (GtkWidget               *widget,
88 89 90 91
								GdkEvent                *event,
								gpointer                 data);
static void gtk_tree_view_column_button_clicked                (GtkWidget               *widget,
								gpointer                 data);
92 93 94
static gboolean gtk_tree_view_column_mnemonic_activate         (GtkWidget *widget,
					                        gboolean   group_cycling,
								gpointer   data);
95 96 97 98 99

/* Property handlers */
static void gtk_tree_view_model_sort_column_changed            (GtkTreeSortable         *sortable,
								GtkTreeViewColumn       *tree_column);

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
/* GtkCellArea/GtkCellAreaContext callbacks */
static void gtk_tree_view_column_context_changed               (GtkCellAreaContext      *context,
								GParamSpec              *pspec,
								GtkTreeViewColumn       *tree_column);
static void gtk_tree_view_column_add_editable_callback         (GtkCellArea             *area,
								GtkCellRenderer         *renderer,
								GtkCellEditable         *edit_widget,
								GdkRectangle            *cell_area,
								const gchar             *path_string,
								gpointer                 user_data);
static void gtk_tree_view_column_remove_editable_callback      (GtkCellArea             *area,
								GtkCellRenderer         *renderer,
								GtkCellEditable         *edit_widget,
								gpointer                 user_data);

115 116 117 118 119
/* Internal functions */
static void gtk_tree_view_column_sort                          (GtkTreeViewColumn       *tree_column,
								gpointer                 data);
static void gtk_tree_view_column_setup_sort_column_id_callback (GtkTreeViewColumn       *tree_column);
static void gtk_tree_view_column_set_attributesv               (GtkTreeViewColumn       *tree_column,
120
								GtkCellRenderer         *cell_renderer,
121
								va_list                  args);
122

Johan Dahlin's avatar
Johan Dahlin committed
123 124
/* GtkBuildable implementation */
static void gtk_tree_view_column_buildable_init                 (GtkBuildableIface     *iface);
125

126 127 128 129 130 131 132 133 134 135

struct _GtkTreeViewColumnPrivate 
{
  GtkWidget *tree_view;
  GtkWidget *button;
  GtkWidget *child;
  GtkWidget *arrow;
  GtkWidget *alignment;
  GdkWindow *window;
  gulong property_changed_signal;
136
  gfloat xalign;
137 138 139 140

  /* Sizing fields */
  /* see gtk+/doc/tree-column-sizing.txt for more information on them */
  GtkTreeViewColumnSizing column_type;
141
  gint padding;
142
  gint x_offset;
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
  gint width;
  gint fixed_width;
  gint min_width;
  gint max_width;

  /* dragging columns */
  gint drag_x;
  gint drag_y;

  gchar *title;

  /* Sorting */
  gulong      sort_clicked_signal;
  gulong      sort_column_changed_signal;
  gint        sort_column_id;
  GtkSortType sort_order;

  /* Cell area */
  GtkCellArea        *cell_area;
  GtkCellAreaContext *cell_area_context;
  gulong              add_editable_signal;
  gulong              remove_editable_signal;
  gulong              context_changed_signal;

  /* Flags */
  guint visible             : 1;
  guint resizable           : 1;
  guint clickable           : 1;
  guint dirty               : 1;
  guint show_sort_indicator : 1;
  guint maybe_reordered     : 1;
  guint reorderable         : 1;
  guint expand              : 1;
};

enum
{
  PROP_0,
  PROP_VISIBLE,
  PROP_RESIZABLE,
183
  PROP_X_OFFSET,
184 185 186 187 188 189 190 191 192 193 194 195 196 197
  PROP_WIDTH,
  PROP_SPACING,
  PROP_SIZING,
  PROP_FIXED_WIDTH,
  PROP_MIN_WIDTH,
  PROP_MAX_WIDTH,
  PROP_TITLE,
  PROP_EXPAND,
  PROP_CLICKABLE,
  PROP_WIDGET,
  PROP_ALIGNMENT,
  PROP_REORDERABLE,
  PROP_SORT_INDICATOR,
  PROP_SORT_ORDER,
198 199
  PROP_SORT_COLUMN_ID,
  PROP_CELL_AREA
200 201 202 203 204 205 206 207
};

enum
{
  CLICKED,
  LAST_SIGNAL
};

208 209
static guint tree_column_signals[LAST_SIGNAL] = { 0 };

210
G_DEFINE_TYPE_WITH_CODE (GtkTreeViewColumn, gtk_tree_view_column, G_TYPE_INITIALLY_UNOWNED,
211
                         G_ADD_PRIVATE (GtkTreeViewColumn)
Matthias Clasen's avatar
Matthias Clasen committed
212
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
Johan Dahlin's avatar
Johan Dahlin committed
213 214 215 216
						gtk_tree_view_column_cell_layout_init)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
						gtk_tree_view_column_buildable_init))

217 218 219 220

static void
gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class)
{
221
  GObjectClass *object_class;
222

223
  object_class = (GObjectClass*) class;
224

225
  class->clicked = NULL;
226

227
  object_class->constructed = gtk_tree_view_column_constructed;
228
  object_class->finalize = gtk_tree_view_column_finalize;
229
  object_class->dispose = gtk_tree_view_column_dispose;
230 231 232
  object_class->set_property = gtk_tree_view_column_set_property;
  object_class->get_property = gtk_tree_view_column_get_property;
  
233
  tree_column_signals[CLICKED] =
Matthias Clasen's avatar
Matthias Clasen committed
234
    g_signal_new (I_("clicked"),
Manish Singh's avatar
Manish Singh committed
235
                  G_OBJECT_CLASS_TYPE (object_class),
236 237 238
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTreeViewColumnClass, clicked),
                  NULL, NULL,
239
                  _gtk_marshal_VOID__VOID,
Manish Singh's avatar
Manish Singh committed
240
                  G_TYPE_NONE, 0);
241 242 243 244

  g_object_class_install_property (object_class,
                                   PROP_VISIBLE,
                                   g_param_spec_boolean ("visible",
245 246
                                                        P_("Visible"),
                                                        P_("Whether to display the column"),
247
                                                         TRUE,
248
                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
249
  
250 251 252
  g_object_class_install_property (object_class,
                                   PROP_RESIZABLE,
                                   g_param_spec_boolean ("resizable",
253 254
							 P_("Resizable"),
							 P_("Column is user-resizable"),
255
                                                         FALSE,
256
                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
257
  
258 259 260 261 262 263 264 265 266 267
  g_object_class_install_property (object_class,
                                   PROP_X_OFFSET,
                                   g_param_spec_int ("x-offset",
                                                     P_("X position"),
                                                     P_("Current X position of the column"),
                                                     -G_MAXINT,
                                                     G_MAXINT,
                                                     0,
                                                     GTK_PARAM_READABLE));

268 269 270
  g_object_class_install_property (object_class,
                                   PROP_WIDTH,
                                   g_param_spec_int ("width",
271 272
						     P_("Width"),
						     P_("Current width of the column"),
273 274 275
						     0,
						     G_MAXINT,
						     0,
276
						     GTK_PARAM_READABLE));
277 278 279 280 281 282 283 284
  g_object_class_install_property (object_class,
                                   PROP_SPACING,
                                   g_param_spec_int ("spacing",
						     P_("Spacing"),
						     P_("Space which is inserted between cells"),
						     0,
						     G_MAXINT,
						     0,
285
						     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
286 287 288
  g_object_class_install_property (object_class,
                                   PROP_SIZING,
                                   g_param_spec_enum ("sizing",
289 290
                                                      P_("Sizing"),
                                                      P_("Resize mode of the column"),
291
                                                      GTK_TYPE_TREE_VIEW_COLUMN_SIZING,
292
                                                      GTK_TREE_VIEW_COLUMN_GROW_ONLY,
293
                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
294 295
  
  g_object_class_install_property (object_class,
296
                                   PROP_FIXED_WIDTH,
Matthias Clasen's avatar
x  
Matthias Clasen committed
297
                                   g_param_spec_int ("fixed-width",
298 299
                                                     P_("Fixed Width"),
                                                     P_("Current fixed width of the column"),
300 301
                                                     -1, G_MAXINT, -1,
                                                     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
302 303 304

  g_object_class_install_property (object_class,
                                   PROP_MIN_WIDTH,
Matthias Clasen's avatar
x  
Matthias Clasen committed
305
                                   g_param_spec_int ("min-width",
306 307
                                                     P_("Minimum Width"),
                                                     P_("Minimum allowed width of the column"),
308 309
                                                     -1, G_MAXINT, -1,
                                                     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
310 311 312

  g_object_class_install_property (object_class,
                                   PROP_MAX_WIDTH,
Matthias Clasen's avatar
x  
Matthias Clasen committed
313
                                   g_param_spec_int ("max-width",
314 315
                                                     P_("Maximum Width"),
                                                     P_("Maximum allowed width of the column"),
316 317
                                                     -1, G_MAXINT, -1,
                                                     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
318 319 320 321

  g_object_class_install_property (object_class,
                                   PROP_TITLE,
                                   g_param_spec_string ("title",
322 323
                                                        P_("Title"),
                                                        P_("Title to appear in column header"),
324
                                                        "",
325
                                                        GTK_PARAM_READWRITE));
326
  
Jonathan Blandford's avatar
Jonathan Blandford committed
327 328 329
  g_object_class_install_property (object_class,
                                   PROP_EXPAND,
                                   g_param_spec_boolean ("expand",
330 331
							 P_("Expand"),
							 P_("Column gets share of extra width allocated to the widget"),
Jonathan Blandford's avatar
Jonathan Blandford committed
332
							 FALSE,
333
							 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Jonathan Blandford's avatar
Jonathan Blandford committed
334
  
335 336 337
  g_object_class_install_property (object_class,
                                   PROP_CLICKABLE,
                                   g_param_spec_boolean ("clickable",
338 339
                                                        P_("Clickable"),
                                                        P_("Whether the header can be clicked"),
340
                                                         FALSE,
341
                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
342 343 344 345 346
  

  g_object_class_install_property (object_class,
                                   PROP_WIDGET,
                                   g_param_spec_object ("widget",
347 348
                                                        P_("Widget"),
                                                        P_("Widget to put in column header button instead of column title"),
349
                                                        GTK_TYPE_WIDGET,
350
                                                        GTK_PARAM_READWRITE));
351 352

  g_object_class_install_property (object_class,
Havoc Pennington's avatar
Havoc Pennington committed
353 354
                                   PROP_ALIGNMENT,
                                   g_param_spec_float ("alignment",
355 356
                                                       P_("Alignment"),
                                                       P_("X Alignment of the column header text or widget"),
357 358
                                                       0.0, 1.0, 0.0,
                                                       GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Havoc Pennington's avatar
Havoc Pennington committed
359

360 361 362
  g_object_class_install_property (object_class,
                                   PROP_REORDERABLE,
                                   g_param_spec_boolean ("reorderable",
363 364
							 P_("Reorderable"),
							 P_("Whether the column can be reordered around the headers"),
365
							 FALSE,
366
							 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
367

Havoc Pennington's avatar
Havoc Pennington committed
368 369
  g_object_class_install_property (object_class,
                                   PROP_SORT_INDICATOR,
Matthias Clasen's avatar
x  
Matthias Clasen committed
370
                                   g_param_spec_boolean ("sort-indicator",
371 372
                                                        P_("Sort indicator"),
                                                        P_("Whether to show a sort indicator"),
Havoc Pennington's avatar
Havoc Pennington committed
373
                                                         FALSE,
374
                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Havoc Pennington's avatar
Havoc Pennington committed
375 376 377

  g_object_class_install_property (object_class,
                                   PROP_SORT_ORDER,
Matthias Clasen's avatar
x  
Matthias Clasen committed
378
                                   g_param_spec_enum ("sort-order",
379 380
                                                      P_("Sort order"),
                                                      P_("Sort direction the sort indicator should indicate"),
381 382
                                                      GTK_TYPE_SORT_TYPE,
                                                      GTK_SORT_ASCENDING,
383
                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
384 385 386 387 388

  /**
   * GtkTreeViewColumn:sort-column-id:
   *
   * Logical sort column ID this column sorts on when selected for sorting. Setting the sort column ID makes the column header
Benjamin Otte's avatar
Benjamin Otte committed
389
   * clickable. Set to -1 to make the column unsortable.
390 391 392 393 394 395 396 397
   *
   * Since: 2.18
   **/
  g_object_class_install_property (object_class,
                                   PROP_SORT_COLUMN_ID,
                                   g_param_spec_int ("sort-column-id",
                                                     P_("Sort column ID"),
                                                     P_("Logical sort column ID this column sorts on when selected for sorting"),
398 399
                                                     -1, G_MAXINT, -1,
                                                     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
400

401 402 403 404 405
  /**
   * GtkTreeViewColumn:cell-area:
   *
   * The #GtkCellArea used to layout cell renderers for this column.
   *
406 407 408
   * If no area is specified when creating the tree view column with gtk_tree_view_column_new_with_area() 
   * a horizontally oriented #GtkCellAreaBox will be used.
   *
409 410 411 412 413 414 415 416 417
   * Since: 3.0
   */
  g_object_class_install_property (object_class,
				   PROP_CELL_AREA,
				   g_param_spec_object ("cell-area",
							P_("Cell Area"),
							P_("The GtkCellArea used to layout cells"),
							GTK_TYPE_CELL_AREA,
							GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
418 419
}

420 421 422 423 424 425 426 427 428 429 430
static void
gtk_tree_view_column_custom_tag_end (GtkBuildable *buildable,
				     GtkBuilder   *builder,
				     GObject      *child,
				     const gchar  *tagname,
				     gpointer     *data)
{
  /* Just ignore the boolean return from here */
  _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data);
}

Johan Dahlin's avatar
Johan Dahlin committed
431 432 433
static void
gtk_tree_view_column_buildable_init (GtkBuildableIface *iface)
{
434
  iface->add_child = _gtk_cell_layout_buildable_add_child;
Johan Dahlin's avatar
Johan Dahlin committed
435
  iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start;
436
  iface->custom_tag_end = gtk_tree_view_column_custom_tag_end;
Johan Dahlin's avatar
Johan Dahlin committed
437 438
}

439 440 441
static void
gtk_tree_view_column_cell_layout_init (GtkCellLayoutIface *iface)
{
442
  iface->get_area = gtk_tree_view_column_cell_layout_get_area;
443 444
}

445 446 447
static void
gtk_tree_view_column_init (GtkTreeViewColumn *tree_column)
{
448 449
  GtkTreeViewColumnPrivate *priv;

450
  tree_column->priv = gtk_tree_view_column_get_instance_private (tree_column);
451 452 453 454 455
  priv = tree_column->priv;

  priv->button = NULL;
  priv->xalign = 0.0;
  priv->width = 0;
456
  priv->padding = 0;
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
  priv->min_width = -1;
  priv->max_width = -1;
  priv->column_type = GTK_TREE_VIEW_COLUMN_GROW_ONLY;
  priv->visible = TRUE;
  priv->resizable = FALSE;
  priv->expand = FALSE;
  priv->clickable = FALSE;
  priv->dirty = TRUE;
  priv->sort_order = GTK_SORT_ASCENDING;
  priv->show_sort_indicator = FALSE;
  priv->property_changed_signal = 0;
  priv->sort_clicked_signal = 0;
  priv->sort_column_changed_signal = 0;
  priv->sort_column_id = -1;
  priv->reorderable = FALSE;
  priv->maybe_reordered = FALSE;
John Lindgren's avatar
John Lindgren committed
473
  priv->fixed_width = -1;
474
  priv->title = g_strdup ("");
475 476
}

477 478
static void
gtk_tree_view_column_constructed (GObject *object)
479
{
480
  GtkTreeViewColumn *tree_column = GTK_TREE_VIEW_COLUMN (object);
481

482
  G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->constructed (object);
483

484
  gtk_tree_view_column_ensure_cell_area (tree_column, NULL);
485 486 487
}

static void
488
gtk_tree_view_column_dispose (GObject *object)
489
{
490 491
  GtkTreeViewColumn        *tree_column = (GtkTreeViewColumn *) object;
  GtkTreeViewColumnPrivate *priv        = tree_column->priv;
492

493 494 495 496 497 498
  /* Remove this column from its treeview, 
   * in case this column is destroyed before its treeview.
   */ 
  if (priv->tree_view)
    gtk_tree_view_remove_column (GTK_TREE_VIEW (priv->tree_view), tree_column);
    
499
  if (priv->cell_area_context)
500
    { 
501 502
      g_signal_handler_disconnect (priv->cell_area_context,
				   priv->context_changed_signal);
503

504
      g_object_unref (priv->cell_area_context);
505

506 507
      priv->cell_area_context = NULL;
      priv->context_changed_signal = 0;
508
    }
509

510
  if (priv->cell_area)
511
    {
512 513 514 515
      g_signal_handler_disconnect (priv->cell_area,
				   priv->add_editable_signal);
      g_signal_handler_disconnect (priv->cell_area,
				   priv->remove_editable_signal);
516

517 518
      g_object_unref (priv->cell_area);
      priv->cell_area = NULL;
519 520
      priv->add_editable_signal = 0;
      priv->remove_editable_signal = 0;
521
    }
522

523
  if (priv->child)
524
    {
525 526
      g_object_unref (priv->child);
      priv->child = NULL;
527 528 529 530 531 532 533 534
    }

  G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->dispose (object);
}

static void
gtk_tree_view_column_finalize (GObject *object)
{
535 536
  GtkTreeViewColumn        *tree_column = (GtkTreeViewColumn *) object;
  GtkTreeViewColumnPrivate *priv        = tree_column->priv;
537

538
  g_free (priv->title);
539

Matthias Clasen's avatar
Matthias Clasen committed
540
  G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->finalize (object);
541 542
}

543 544 545 546
static void
gtk_tree_view_column_set_property (GObject         *object,
                                   guint            prop_id,
                                   const GValue    *value,
Tim Janik's avatar
Tim Janik committed
547
                                   GParamSpec      *pspec)
548 549
{
  GtkTreeViewColumn *tree_column;
550
  GtkCellArea       *area;
551 552 553 554 555 556 557 558 559 560

  tree_column = GTK_TREE_VIEW_COLUMN (object);

  switch (prop_id)
    {
    case PROP_VISIBLE:
      gtk_tree_view_column_set_visible (tree_column,
                                        g_value_get_boolean (value));
      break;

Jonathan Blandford's avatar
Jonathan Blandford committed
561 562 563 564 565
    case PROP_RESIZABLE:
      gtk_tree_view_column_set_resizable (tree_column,
					  g_value_get_boolean (value));
      break;

566 567 568 569 570
    case PROP_SIZING:
      gtk_tree_view_column_set_sizing (tree_column,
                                       g_value_get_enum (value));
      break;

571 572 573
    case PROP_FIXED_WIDTH:
      gtk_tree_view_column_set_fixed_width (tree_column,
					    g_value_get_int (value));
574 575 576 577 578 579 580 581 582 583 584 585
      break;

    case PROP_MIN_WIDTH:
      gtk_tree_view_column_set_min_width (tree_column,
                                          g_value_get_int (value));
      break;

    case PROP_MAX_WIDTH:
      gtk_tree_view_column_set_max_width (tree_column,
                                          g_value_get_int (value));
      break;

586 587 588 589 590
    case PROP_SPACING:
      gtk_tree_view_column_set_spacing (tree_column,
					g_value_get_int (value));
      break;

591 592 593 594 595
    case PROP_TITLE:
      gtk_tree_view_column_set_title (tree_column,
                                      g_value_get_string (value));
      break;

Jonathan Blandford's avatar
Jonathan Blandford committed
596 597 598 599 600
    case PROP_EXPAND:
      gtk_tree_view_column_set_expand (tree_column,
				       g_value_get_boolean (value));
      break;

601 602 603 604 605 606 607 608 609 610
    case PROP_CLICKABLE:
      gtk_tree_view_column_set_clickable (tree_column,
                                          g_value_get_boolean (value));
      break;

    case PROP_WIDGET:
      gtk_tree_view_column_set_widget (tree_column,
                                       (GtkWidget*) g_value_get_object (value));
      break;

Havoc Pennington's avatar
Havoc Pennington committed
611 612 613 614 615
    case PROP_ALIGNMENT:
      gtk_tree_view_column_set_alignment (tree_column,
                                          g_value_get_float (value));
      break;

616 617 618 619 620
    case PROP_REORDERABLE:
      gtk_tree_view_column_set_reorderable (tree_column,
					    g_value_get_boolean (value));
      break;

Havoc Pennington's avatar
Havoc Pennington committed
621 622 623
    case PROP_SORT_INDICATOR:
      gtk_tree_view_column_set_sort_indicator (tree_column,
                                               g_value_get_boolean (value));
624 625
      break;

Havoc Pennington's avatar
Havoc Pennington committed
626 627 628 629 630
    case PROP_SORT_ORDER:
      gtk_tree_view_column_set_sort_order (tree_column,
                                           g_value_get_enum (value));
      break;
      
631 632 633 634
    case PROP_SORT_COLUMN_ID:
      gtk_tree_view_column_set_sort_column_id (tree_column,
                                               g_value_get_int (value));
      break;
635 636 637 638 639 640

    case PROP_CELL_AREA:
      /* Construct-only, can only be assigned once */
      area = g_value_get_object (value);

      if (area)
641 642 643 644 645 646 647 648 649 650
        {
          if (tree_column->priv->cell_area != NULL)
            {
              g_warning ("cell-area has already been set, ignoring construct property");
              g_object_ref_sink (area);
              g_object_unref (area);
            }
          else
            gtk_tree_view_column_ensure_cell_area (tree_column, area);
        }
651
      break;
652

653 654 655 656 657 658 659 660 661 662
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_tree_view_column_get_property (GObject         *object,
                                   guint            prop_id,
                                   GValue          *value,
Tim Janik's avatar
Tim Janik committed
663
                                   GParamSpec      *pspec)
664 665 666 667 668 669 670 671
{
  GtkTreeViewColumn *tree_column;

  tree_column = GTK_TREE_VIEW_COLUMN (object);

  switch (prop_id)
    {
    case PROP_VISIBLE:
Havoc Pennington's avatar
Havoc Pennington committed
672 673
      g_value_set_boolean (value,
                           gtk_tree_view_column_get_visible (tree_column));
674 675
      break;

Jonathan Blandford's avatar
Jonathan Blandford committed
676 677 678 679 680
    case PROP_RESIZABLE:
      g_value_set_boolean (value,
                           gtk_tree_view_column_get_resizable (tree_column));
      break;

681 682 683 684 685
    case PROP_X_OFFSET:
      g_value_set_int (value,
                       gtk_tree_view_column_get_x_offset (tree_column));
      break;

686 687 688 689 690
    case PROP_WIDTH:
      g_value_set_int (value,
                       gtk_tree_view_column_get_width (tree_column));
      break;

691 692 693 694 695
    case PROP_SPACING:
      g_value_set_int (value,
                       gtk_tree_view_column_get_spacing (tree_column));
      break;

696
    case PROP_SIZING:
Havoc Pennington's avatar
Havoc Pennington committed
697 698
      g_value_set_enum (value,
                        gtk_tree_view_column_get_sizing (tree_column));
699 700
      break;

701
    case PROP_FIXED_WIDTH:
Havoc Pennington's avatar
Havoc Pennington committed
702
      g_value_set_int (value,
703
                       gtk_tree_view_column_get_fixed_width (tree_column));
704 705 706
      break;

    case PROP_MIN_WIDTH:
Havoc Pennington's avatar
Havoc Pennington committed
707 708
      g_value_set_int (value,
                       gtk_tree_view_column_get_min_width (tree_column));
709 710 711
      break;

    case PROP_MAX_WIDTH:
Havoc Pennington's avatar
Havoc Pennington committed
712 713
      g_value_set_int (value,
                       gtk_tree_view_column_get_max_width (tree_column));
714 715 716
      break;

    case PROP_TITLE:
Havoc Pennington's avatar
Havoc Pennington committed
717 718
      g_value_set_string (value,
                          gtk_tree_view_column_get_title (tree_column));
719 720
      break;

Jonathan Blandford's avatar
Jonathan Blandford committed
721 722 723 724 725
    case PROP_EXPAND:
      g_value_set_boolean (value,
                          gtk_tree_view_column_get_expand (tree_column));
      break;

726
    case PROP_CLICKABLE:
Havoc Pennington's avatar
Havoc Pennington committed
727 728
      g_value_set_boolean (value,
                           gtk_tree_view_column_get_clickable (tree_column));
729 730 731
      break;

    case PROP_WIDGET:
Havoc Pennington's avatar
Havoc Pennington committed
732 733
      g_value_set_object (value,
                          (GObject*) gtk_tree_view_column_get_widget (tree_column));
734 735
      break;

Havoc Pennington's avatar
Havoc Pennington committed
736 737 738
    case PROP_ALIGNMENT:
      g_value_set_float (value,
                         gtk_tree_view_column_get_alignment (tree_column));
739 740
      break;

741 742 743 744 745
    case PROP_REORDERABLE:
      g_value_set_boolean (value,
			   gtk_tree_view_column_get_reorderable (tree_column));
      break;

Havoc Pennington's avatar
Havoc Pennington committed
746 747 748 749 750 751 752 753 754 755
    case PROP_SORT_INDICATOR:
      g_value_set_boolean (value,
                           gtk_tree_view_column_get_sort_indicator (tree_column));
      break;

    case PROP_SORT_ORDER:
      g_value_set_enum (value,
                        gtk_tree_view_column_get_sort_order (tree_column));
      break;
      
756 757 758 759
    case PROP_SORT_COLUMN_ID:
      g_value_set_int (value,
                       gtk_tree_view_column_get_sort_column_id (tree_column));
      break;
760 761

    case PROP_CELL_AREA:
762
      g_value_set_object (value, tree_column->priv->cell_area);
763
      break;
764
      
765 766 767 768 769 770
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

771 772
/* Implementation of GtkCellLayout interface
 */
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806

static void
gtk_tree_view_column_ensure_cell_area (GtkTreeViewColumn *column,
                                       GtkCellArea       *cell_area)
{
  GtkTreeViewColumnPrivate *priv = column->priv;

  if (priv->cell_area)
    return;

  if (cell_area)
    priv->cell_area = cell_area;
  else
    priv->cell_area = gtk_cell_area_box_new ();

  g_object_ref_sink (priv->cell_area);

  priv->add_editable_signal =
    g_signal_connect (priv->cell_area, "add-editable",
                      G_CALLBACK (gtk_tree_view_column_add_editable_callback),
                      column);
  priv->remove_editable_signal =
    g_signal_connect (priv->cell_area, "remove-editable",
                      G_CALLBACK (gtk_tree_view_column_remove_editable_callback),
                      column);

  priv->cell_area_context = gtk_cell_area_create_context (priv->cell_area);

  priv->context_changed_signal =
    g_signal_connect (priv->cell_area_context, "notify",
                      G_CALLBACK (gtk_tree_view_column_context_changed),
                      column);
}

807 808
static GtkCellArea *
gtk_tree_view_column_cell_layout_get_area (GtkCellLayout   *cell_layout)
809
{
810 811
  GtkTreeViewColumn        *column = GTK_TREE_VIEW_COLUMN (cell_layout);
  GtkTreeViewColumnPrivate *priv   = column->priv;
812

813 814 815
  if (G_UNLIKELY (!priv->cell_area))
    gtk_tree_view_column_ensure_cell_area (column, NULL);

816
  return priv->cell_area;
Kristian Rietveld's avatar
Kristian Rietveld committed
817 818
}

819 820
/* Button handling code
 */
821
static void
822
gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column)
823
{
824
  GtkTreeViewColumnPrivate *priv = tree_column->priv;
825 826 827
  GtkTreeView *tree_view;
  GtkWidget *child;
  GtkWidget *hbox;
828

829
  tree_view = (GtkTreeView *) priv->tree_view;
830

831
  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
832
  g_return_if_fail (priv->button == NULL);
833

834
  priv->button = gtk_button_new ();
835 836
  if (priv->visible)
    gtk_widget_show (priv->button);
837
  gtk_widget_add_events (priv->button, GDK_POINTER_MOTION_MASK);
838 839

  /* make sure we own a reference to it as well. */
840 841 842
  if (_gtk_tree_view_get_header_window (tree_view))
    gtk_widget_set_parent_window (priv->button, _gtk_tree_view_get_header_window (tree_view));

843
  gtk_widget_set_parent (priv->button, GTK_WIDGET (tree_view));
844

845
  g_signal_connect (priv->button, "event",
846
		    G_CALLBACK (gtk_tree_view_column_button_event),
Manish Singh's avatar
Manish Singh committed
847
		    tree_column);
848
  g_signal_connect (priv->button, "clicked",
Manish Singh's avatar
Manish Singh committed
849 850
		    G_CALLBACK (gtk_tree_view_column_button_clicked),
		    tree_column);
851

Matthias Clasen's avatar
Matthias Clasen committed
852
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
853
  priv->alignment = gtk_alignment_new (priv->xalign, 0.5, 0.0, 0.0);
Matthias Clasen's avatar
Matthias Clasen committed
854
G_GNUC_END_IGNORE_DEPRECATIONS
855

856
  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
857
  priv->arrow = gtk_image_new_from_icon_name ("pan-down-symbolic", GTK_ICON_SIZE_BUTTON);
858

859 860
  if (priv->child)
    child = priv->child;
861 862
  else
    {
863
      child = gtk_label_new (priv->title);
864 865 866
      gtk_widget_show (child);
    }

867
  g_signal_connect (child, "mnemonic-activate",
868
		    G_CALLBACK (gtk_tree_view_column_mnemonic_activate),
Manish Singh's avatar
Manish Singh committed
869
		    tree_column);
870

871
  if (priv->xalign <= 0.5)
872 873 874 875
    {
      gtk_box_pack_start (GTK_BOX (hbox), priv->alignment, TRUE, TRUE, 0);
      gtk_box_pack_start (GTK_BOX (hbox), priv->arrow, FALSE, FALSE, 0);
    }
876
  else
877 878 879 880
    {
      gtk_box_pack_start (GTK_BOX (hbox), priv->arrow, FALSE, FALSE, 0);
      gtk_box_pack_start (GTK_BOX (hbox), priv->alignment, TRUE, TRUE, 0);
    }
881

882 883
  gtk_container_add (GTK_CONTAINER (priv->alignment), child);
  gtk_container_add (GTK_CONTAINER (priv->button), hbox);
884 885

  gtk_widget_show (hbox);
886
  gtk_widget_show (priv->alignment);
887
  gtk_tree_view_column_update_button (tree_column);
888 889
}

890 891 892
static void 
gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column)
{
893
  GtkTreeViewColumnPrivate *priv = tree_column->priv;
894
  gint sort_column_id = -1;
895 896 897 898
  GtkWidget *hbox;
  GtkWidget *alignment;
  GtkWidget *arrow;
  GtkWidget *current_child;
899
  const gchar *icon_name = "missing-image";
900 901
  GtkTreeModel *model;

902 903
  if (priv->tree_view)
    model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
904 905
  else
    model = NULL;
906 907

  /* Create a button if necessary */
908 909 910 911
  if (priv->visible &&
      priv->button == NULL &&
      priv->tree_view &&
      gtk_widget_get_realized (priv->tree_view))
912 913
    gtk_tree_view_column_create_button (tree_column);
  
914
  if (! priv->button)
915 916
    return;

917 918 919
  hbox = gtk_bin_get_child (GTK_BIN (priv->button));
  alignment = priv->alignment;
  arrow = priv->arrow;
Javier Jardón's avatar
Javier Jardón committed
920
  current_child = gtk_bin_get_child (GTK_BIN (alignment));
921 922

  /* Set up the actual button */
Matthias Clasen's avatar
Matthias Clasen committed
923 924 925
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  gtk_alignment_set (GTK_ALIGNMENT (alignment), priv->xalign, 0.5, 0.0, 0.0);
G_GNUC_END_IGNORE_DEPRECATIONS
926
      
927
  if (priv->child)
928
    {
929
      if (current_child != priv->child)
930 931 932 933
	{
	  gtk_container_remove (GTK_CONTAINER (alignment),
				current_child);
	  gtk_container_add (GTK_CONTAINER (alignment),
934
			     priv->child);
935 936 937 938 939 940 941 942 943 944 945 946 947 948
	}
    }
  else 
    {
      if (current_child == NULL)
	{
	  current_child = gtk_label_new (NULL);
	  gtk_widget_show (current_child);
	  gtk_container_add (GTK_CONTAINER (alignment),
			     current_child);
	}

      g_return_if_fail (GTK_IS_LABEL (current_child));

949
      if (priv->title)
950
	gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child),
951
					  priv->title);
952
      else
953 954
	gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child),
					  "");
955 956
    }

957 958 959 960 961
  if (GTK_IS_TREE_SORTABLE (model))
    gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model),
					  &sort_column_id,
					  NULL);

962
  if (priv->show_sort_indicator)
963
    {
964 965
      gboolean alternative;

966
      g_object_get (gtk_widget_get_settings (priv->tree_view),
967 968 969
		    "gtk-alternative-sort-arrows", &alternative,
		    NULL);

970
      switch (priv->sort_order)
971 972
        {
	  case GTK_SORT_ASCENDING:
973
            icon_name = alternative ? "pan-up-symbolic" : "pan-down-symbolic";
974 975 976
	    break;

	  case GTK_SORT_DESCENDING:
977
            icon_name = alternative ? "pan-down-symbolic" : "pan-up-symbolic";
978 979 980 981 982
	    break;

	  default:
	    g_warning (G_STRLOC": bad sort order");
	    break;
Kristian Rietveld's avatar
Kristian Rietveld committed
983
	}
984 985
    }

986
  gtk_image_set_from_icon_name (GTK_IMAGE (arrow), icon_name, GTK_ICON_SIZE_BUTTON);
Kristian Rietveld's avatar
Kristian Rietveld committed
987

988 989 990 991
  /* Put arrow on the right if the text is left-or-center justified, and on the
   * left otherwise; do this by packing boxes, so flipping text direction will
   * reverse things
   */
992
  if (priv->xalign <= 0.5)
993
    gtk_box_reorder_child (GTK_BOX (hbox), arrow, 1);
994
  else
995
    gtk_box_reorder_child (GTK_BOX (hbox), arrow, 0);
996

997 998
  if (priv->show_sort_indicator
      || (GTK_IS_TREE_SORTABLE (model) && priv->sort_column_id >= 0))
999 1000 1001 1002
    gtk_widget_show (arrow);
  else
    gtk_widget_hide (arrow);

1003 1004 1005 1006 1007
  if (priv->show_sort_indicator)
    gtk_widget_set_opacity (arrow, 1.0);
  else
    gtk_widget_set_opacity (arrow, 0.0);

1008 1009
  /* It's always safe to hide the button.  It isn't always safe to show it, as
   * if you show it before it's realized, it'll get the wrong window. */
1010 1011 1012
  if (priv->button &&
      priv->tree_view != NULL &&
      gtk_widget_get_realized (priv->tree_view))
1013
    {
1014 1015
      if (priv->visible &&
          gdk_window_is_visible (_gtk_tree_view_get_header_window (GTK_TREE_VIEW (priv->tree_view))))
1016
	{
1017
          gtk_widget_show (priv->button);
1018

1019
	  if (priv->window)
1020
	    {
1021
	      if (priv->resizable)
1022
		{
1023 1024
		  gdk_window_show (priv->window);
		  gdk_window_raise (priv->window);
1025 1026 1027
		}
	      else
		{
1028
		  gdk_window_hide (priv->window);
1029 1030 1031 1032 1033
		}
	    }
	}
      else
	{
1034 1035 1036
	  gtk_widget_hide (priv->button);
	  if (priv->window)
	    gdk_window_hide (priv->window);
1037 1038
	}
    }
1039
  
1040
  if (priv->reorderable || priv->clickable)
1041
    {
1042
      gtk_widget_set_can_focus (priv->button, TRUE);
1043
    }
1044
  else
1045
    {
1046 1047
      gtk_widget_set_can_focus (priv->button, FALSE);
      if (gtk_widget_has_focus (priv->button))
1048
	{
1049
	  GtkWidget *toplevel = gtk_widget_get_toplevel (priv->tree_view);
1050
	  if (gtk_widget_is_toplevel (toplevel))
1051 1052 1053
	    {
	      gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
	    }
1054
	}
1055
    }
1056 1057 1058
  /* Queue a resize on the assumption that we always want to catch all changes
   * and columns don't change all that often.
   */
1059 1060
  if (gtk_widget_get_realized (priv->tree_view))
     gtk_widget_queue_resize (priv->tree_view);
1061

1062 1063 1064 1065
}

/* Button signal handlers
 */
1066 1067

static gint
1068 1069 1070
gtk_tree_view_column_button_event (GtkWidget *widget,
				   GdkEvent  *event,
				   gpointer   data)
1071
{
1072 1073
  GtkTreeViewColumn        *column = (GtkTreeViewColumn *) data;
  GtkTreeViewColumnPrivate *priv   = column->priv;
1074

1075 1076
  g_return_val_if_fail (event != NULL, FALSE);

1077
  if (event->type == GDK_BUTTON_PRESS &&
1078
      priv->reorderable &&
1079
      ((GdkEventButton *)event)->button == GDK_BUTTON_PRIMARY)
1080
    {
1081
      priv->maybe_reordered = TRUE;
1082 1083
      priv->drag_x = event->button.x;
      priv->drag_y = event->button.y;
1084 1085 1086
      gtk_widget_grab_focus (widget);
    }

1087 1088
  if (event->type == GDK_BUTTON_RELEASE ||
      event->type == GDK_LEAVE_NOTIFY)
1089
    priv->maybe_reordered = FALSE;
1090
  
1091
  if (event->type == GDK_MOTION_NOTIFY &&
1092
      priv->maybe_reordered &&
1093
      (gtk_drag_check_threshold (widget,
1094 1095
				 priv->drag_x,
				 priv->drag_y,
1096 1097
				 (gint) ((GdkEventMotion *)event)->x,
				 (gint) ((GdkEventMotion *)event)->y)))
1098
    {
1099 1100
      priv->maybe_reordered = FALSE;
      _gtk_tree_view_column_start_drag (GTK_TREE_VIEW (priv->tree_view), column,
1101
                                        event->motion.device);
1102
      return TRUE;