gtktreeviewcolumn.c 91.1 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
#include "gtkbox.h"
23
#include "gtkbutton.h"
24
25
26
#include "gtkcellareabox.h"
#include "gtkcellareacontext.h"
#include "gtkcelllayout.h"
27
#include "gtkdragsourceprivate.h"
28
#include "gtkframe.h"
29
30
#include "gtkimage.h"
#include "gtkintl.h"
31
#include "gtklabel.h"
32
#include "gtkmarshalers.h"
33
#include "gtkprivate.h"
34
35
#include "gtktreeprivate.h"
#include "gtktreeview.h"
36
#include "gtktypebuiltins.h"
37
#include "gtkwidgetprivate.h"
38
#include "gtkgesturedrag.h"
39
#include "gtkeventcontrollerfocus.h"
40
#include "gtkeventcontrollerkey.h"
41
#include "gtkbuiltiniconprivate.h"
42

43
44
#include <string.h>

Jonathan Blandford's avatar
Jonathan Blandford committed
45

46
/**
47
48
 * GtkTreeViewColumn:
 *
49
 * A visible column in a [class@Gtk.TreeView] widget
50
 *
51
 * The `GtkTreeViewColumn` object represents a visible column in a `GtkTreeView` widget.
52
53
 * 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
 *
55
 * Please refer to the [tree widget conceptual overview](section-tree-widget.html)
56
 * for an overview of all the objects and data types related to the tree widget and
57
58
 * how they work together, and to the [class@Gtk.TreeView] documentation for specifics
 * about the CSS node structure for treeviews and their headers.
59
60
61
 */


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

/* 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);
75
static void gtk_tree_view_column_dispose                       (GObject                 *object);
76
static void gtk_tree_view_column_constructed                   (GObject                 *object);
77

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

82
static GtkCellArea *gtk_tree_view_column_cell_layout_get_area  (GtkCellLayout           *cell_layout);
83

84
/* Button handling code */
85
86
87
88
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 */
89
static void column_button_drag_begin  (GtkGestureDrag    *gesture,
90
91
                                       double             x,
                                       double             y,
92
93
                                       GtkTreeViewColumn *column);
static void column_button_drag_update (GtkGestureDrag    *gesture,
94
95
                                       double             offset_x,
                                       double             offset_y,
96
97
                                       GtkTreeViewColumn *column);

98
99
static void gtk_tree_view_column_button_clicked                (GtkWidget               *widget,
								gpointer                 data);
100
101
102
static gboolean gtk_tree_view_column_mnemonic_activate         (GtkWidget *widget,
					                        gboolean   group_cycling,
								gpointer   data);
103
104
105
106
107

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

108
109
110
111
112
113
114
115
/* 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,
Benjamin Otte's avatar
Benjamin Otte committed
116
								const char              *path_string,
117
118
119
120
121
122
								gpointer                 user_data);
static void gtk_tree_view_column_remove_editable_callback      (GtkCellArea             *area,
								GtkCellRenderer         *renderer,
								GtkCellEditable         *edit_widget,
								gpointer                 user_data);

123
124
125
126
127
/* 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,
128
								GtkCellRenderer         *cell_renderer,
129
								va_list                  args);
130

Johan Dahlin's avatar
Johan Dahlin committed
131
132
/* GtkBuildable implementation */
static void gtk_tree_view_column_buildable_init                 (GtkBuildableIface     *iface);
133

Matthias Clasen's avatar
Matthias Clasen committed
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
typedef struct _GtkTreeViewColumnClass   GtkTreeViewColumnClass;
typedef struct _GtkTreeViewColumnPrivate GtkTreeViewColumnPrivate;

struct _GtkTreeViewColumn
{
  GInitiallyUnowned parent_instance;

  GtkTreeViewColumnPrivate *priv;
};

struct _GtkTreeViewColumnClass
{
  GInitiallyUnownedClass parent_class;

  void (*clicked) (GtkTreeViewColumn *tree_column);
};

151
152
153
154
155
156
157

struct _GtkTreeViewColumnPrivate 
{
  GtkWidget *tree_view;
  GtkWidget *button;
  GtkWidget *child;
  GtkWidget *arrow;
Timm Bäder's avatar
Timm Bäder committed
158
  GtkWidget *frame;
159
  gulong property_changed_signal;
Benjamin Otte's avatar
Benjamin Otte committed
160
  float xalign;
161
162
163
164

  /* Sizing fields */
  /* see gtk+/doc/tree-column-sizing.txt for more information on them */
  GtkTreeViewColumnSizing column_type;
Benjamin Otte's avatar
Benjamin Otte committed
165
166
167
168
169
170
  int padding;
  int x_offset;
  int width;
  int fixed_width;
  int min_width;
  int max_width;
171
172

  /* dragging columns */
Benjamin Otte's avatar
Benjamin Otte committed
173
174
  int drag_x;
  int drag_y;
175

Benjamin Otte's avatar
Benjamin Otte committed
176
  char *title;
177
178
179
180

  /* Sorting */
  gulong      sort_clicked_signal;
  gulong      sort_column_changed_signal;
Benjamin Otte's avatar
Benjamin Otte committed
181
  int         sort_column_id;
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  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,
207
  PROP_X_OFFSET,
208
209
210
211
212
213
214
215
216
217
218
219
220
221
  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,
222
  PROP_SORT_COLUMN_ID,
223
224
  PROP_CELL_AREA,
  LAST_PROP
225
226
227
228
229
230
231
232
};

enum
{
  CLICKED,
  LAST_SIGNAL
};

233
static guint tree_column_signals[LAST_SIGNAL] = { 0 };
234
static GParamSpec *tree_column_props[LAST_PROP] = { NULL, };
235

Matthias Clasen's avatar
Matthias Clasen committed
236
G_DEFINE_TYPE_WITH_CODE (GtkTreeViewColumn, gtk_tree_view_column, G_TYPE_INITIALLY_UNOWNED,
237
                         G_ADD_PRIVATE (GtkTreeViewColumn)
Matthias Clasen's avatar
Matthias Clasen committed
238
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
Johan Dahlin's avatar
Johan Dahlin committed
239
240
241
242
						gtk_tree_view_column_cell_layout_init)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
						gtk_tree_view_column_buildable_init))

243
244
245
246

static void
gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class)
{
247
  GObjectClass *object_class;
248

249
  object_class = (GObjectClass*) class;
250

251
  class->clicked = NULL;
252

253
  object_class->constructed = gtk_tree_view_column_constructed;
254
  object_class->finalize = gtk_tree_view_column_finalize;
255
  object_class->dispose = gtk_tree_view_column_dispose;
256
257
  object_class->set_property = gtk_tree_view_column_set_property;
  object_class->get_property = gtk_tree_view_column_get_property;
258
259
260

  /**
   * GtkTreeViewColumn::clicked:
Matthias Clasen's avatar
Matthias Clasen committed
261
   * @column: the `GtkTreeViewColumn` that emitted the signal
262
263
264
   *
   * Emitted when the column's header has been clicked.
   */
265
  tree_column_signals[CLICKED] =
Matthias Clasen's avatar
Matthias Clasen committed
266
    g_signal_new (I_("clicked"),
Manish Singh's avatar
Manish Singh committed
267
                  G_OBJECT_CLASS_TYPE (object_class),
268
269
270
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTreeViewColumnClass, clicked),
                  NULL, NULL,
271
                  NULL,
Manish Singh's avatar
Manish Singh committed
272
                  G_TYPE_NONE, 0);
273

274
  tree_column_props[PROP_VISIBLE] =
275
      g_param_spec_boolean ("visible", NULL, NULL,
276
277
278
279
                            TRUE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_RESIZABLE] =
280
      g_param_spec_boolean ("resizable", NULL, NULL,
281
282
283
284
                            FALSE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_X_OFFSET] =
285
      g_param_spec_int ("x-offset", NULL, NULL,
286
287
288
289
290
                        -G_MAXINT, G_MAXINT,
                        0,
                        GTK_PARAM_READABLE);

  tree_column_props[PROP_WIDTH] =
291
      g_param_spec_int ("width", NULL, NULL,
292
293
294
295
296
                        0, G_MAXINT,
                        0,
                        GTK_PARAM_READABLE);

  tree_column_props[PROP_SPACING] =
297
      g_param_spec_int ("spacing", NULL, NULL,
298
299
300
301
302
                        0, G_MAXINT,
                        0,
                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_SIZING] =
303
      g_param_spec_enum ("sizing", NULL, NULL,
304
305
306
307
308
                         GTK_TYPE_TREE_VIEW_COLUMN_SIZING,
                         GTK_TREE_VIEW_COLUMN_GROW_ONLY,
                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_FIXED_WIDTH] =
309
      g_param_spec_int ("fixed-width", NULL, NULL,
310
311
312
313
314
                         -1, G_MAXINT,
                         -1,
                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_MIN_WIDTH] =
315
      g_param_spec_int ("min-width", NULL, NULL,
316
317
318
319
320
                        -1, G_MAXINT,
                        -1,
                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_MAX_WIDTH] =
321
      g_param_spec_int ("max-width", NULL, NULL,
322
323
324
325
326
                        -1, G_MAXINT,
                        -1,
                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_TITLE] =
327
      g_param_spec_string ("title", NULL, NULL,
328
329
330
331
                           "",
                           GTK_PARAM_READWRITE);

  tree_column_props[PROP_EXPAND] =
332
      g_param_spec_boolean ("expand", NULL, NULL,
333
334
335
336
                            FALSE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_CLICKABLE] =
337
      g_param_spec_boolean ("clickable", NULL, NULL,
338
339
340
341
                            FALSE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_WIDGET] =
342
      g_param_spec_object ("widget", NULL, NULL,
343
344
345
346
                           GTK_TYPE_WIDGET,
                           GTK_PARAM_READWRITE);

  tree_column_props[PROP_ALIGNMENT] =
347
      g_param_spec_float ("alignment", NULL, NULL,
348
349
350
351
                          0.0, 1.0, 0.0,
                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_REORDERABLE] =
352
      g_param_spec_boolean ("reorderable", NULL, NULL,
353
354
355
356
                            FALSE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_SORT_INDICATOR] =
357
      g_param_spec_boolean ("sort-indicator", NULL, NULL,
358
359
360
361
                            FALSE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  tree_column_props[PROP_SORT_ORDER] =
362
      g_param_spec_enum ("sort-order", NULL, NULL,
363
364
365
                         GTK_TYPE_SORT_TYPE,
                         GTK_SORT_ASCENDING,
                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
366
367
368
369
370

  /**
   * 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
371
   * clickable. Set to -1 to make the column unsortable.
372
   **/
373
  tree_column_props[PROP_SORT_COLUMN_ID] =
374
      g_param_spec_int ("sort-column-id", NULL, NULL,
375
376
377
                        -1, G_MAXINT,
                        -1,
                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
378

379
380
381
  /**
   * GtkTreeViewColumn:cell-area:
   *
Matthias Clasen's avatar
Matthias Clasen committed
382
   * The `GtkCellArea` used to layout cell renderers for this column.
383
   *
384
   * If no area is specified when creating the tree view column with gtk_tree_view_column_new_with_area() 
Matthias Clasen's avatar
Matthias Clasen committed
385
   * a horizontally oriented `GtkCellAreaBox` will be used.
386
   */
387
  tree_column_props[PROP_CELL_AREA] =
388
      g_param_spec_object ("cell-area", NULL, NULL,
389
390
391
392
                           GTK_TYPE_CELL_AREA,
                           GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);

  g_object_class_install_properties (object_class, LAST_PROP, tree_column_props);
393
394
}

395
396
397
398
static void
gtk_tree_view_column_custom_tag_end (GtkBuildable *buildable,
				     GtkBuilder   *builder,
				     GObject      *child,
Benjamin Otte's avatar
Benjamin Otte committed
399
				     const char   *tagname,
400
				     gpointer      data)
401
402
403
404
405
{
  /* 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
406
407
408
static void
gtk_tree_view_column_buildable_init (GtkBuildableIface *iface)
{
409
  iface->add_child = _gtk_cell_layout_buildable_add_child;
Johan Dahlin's avatar
Johan Dahlin committed
410
  iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start;
411
  iface->custom_tag_end = gtk_tree_view_column_custom_tag_end;
Johan Dahlin's avatar
Johan Dahlin committed
412
413
}

414
415
416
static void
gtk_tree_view_column_cell_layout_init (GtkCellLayoutIface *iface)
{
417
  iface->get_area = gtk_tree_view_column_cell_layout_get_area;
418
419
}

420
421
422
static void
gtk_tree_view_column_init (GtkTreeViewColumn *tree_column)
{
423
424
  GtkTreeViewColumnPrivate *priv;

425
  tree_column->priv = gtk_tree_view_column_get_instance_private (tree_column);
426
427
428
429
430
  priv = tree_column->priv;

  priv->button = NULL;
  priv->xalign = 0.0;
  priv->width = 0;
431
  priv->padding = 0;
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
  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
448
  priv->fixed_width = -1;
449
  priv->title = g_strdup ("");
450
451

  gtk_tree_view_column_create_button (tree_column);
452
453
}

454
455
static void
gtk_tree_view_column_constructed (GObject *object)
456
{
457
  GtkTreeViewColumn *tree_column = GTK_TREE_VIEW_COLUMN (object);
458

459
  G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->constructed (object);
460

461
  gtk_tree_view_column_ensure_cell_area (tree_column, NULL);
462
463
464
}

static void
465
gtk_tree_view_column_dispose (GObject *object)
466
{
467
468
  GtkTreeViewColumn        *tree_column = (GtkTreeViewColumn *) object;
  GtkTreeViewColumnPrivate *priv        = tree_column->priv;
469

470
471
472
473
474
475
  /* 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);
    
476
  if (priv->cell_area_context)
477
    { 
478
479
      g_signal_handler_disconnect (priv->cell_area_context,
				   priv->context_changed_signal);
480

481
      g_object_unref (priv->cell_area_context);
482

483
484
      priv->cell_area_context = NULL;
      priv->context_changed_signal = 0;
485
    }
486

487
  if (priv->cell_area)
488
    {
489
490
491
492
      g_signal_handler_disconnect (priv->cell_area,
				   priv->add_editable_signal);
      g_signal_handler_disconnect (priv->cell_area,
				   priv->remove_editable_signal);
493

494
495
      g_object_unref (priv->cell_area);
      priv->cell_area = NULL;
496
497
      priv->add_editable_signal = 0;
      priv->remove_editable_signal = 0;
498
    }
499

500
  if (priv->child)
501
    {
502
503
      g_object_unref (priv->child);
      priv->child = NULL;
504
505
    }

506
507
  g_clear_object (&priv->button);

508
509
510
511
512
513
  G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->dispose (object);
}

static void
gtk_tree_view_column_finalize (GObject *object)
{
514
515
  GtkTreeViewColumn        *tree_column = (GtkTreeViewColumn *) object;
  GtkTreeViewColumnPrivate *priv        = tree_column->priv;
516

517
  g_free (priv->title);
518

Matthias Clasen's avatar
Matthias Clasen committed
519
  G_OBJECT_CLASS (gtk_tree_view_column_parent_class)->finalize (object);
520
521
}

522
523
524
525
static void
gtk_tree_view_column_set_property (GObject         *object,
                                   guint            prop_id,
                                   const GValue    *value,
Tim Janik's avatar
Tim Janik committed
526
                                   GParamSpec      *pspec)
527
528
{
  GtkTreeViewColumn *tree_column;
529
  GtkCellArea       *area;
530
531
532
533
534
535
536
537
538
539

  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
540
541
542
543
544
    case PROP_RESIZABLE:
      gtk_tree_view_column_set_resizable (tree_column,
					  g_value_get_boolean (value));
      break;

545
546
547
548
549
    case PROP_SIZING:
      gtk_tree_view_column_set_sizing (tree_column,
                                       g_value_get_enum (value));
      break;

550
551
552
    case PROP_FIXED_WIDTH:
      gtk_tree_view_column_set_fixed_width (tree_column,
					    g_value_get_int (value));
553
554
555
556
557
558
559
560
561
562
563
564
      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;

565
566
567
568
569
    case PROP_SPACING:
      gtk_tree_view_column_set_spacing (tree_column,
					g_value_get_int (value));
      break;

570
571
572
573
574
    case PROP_TITLE:
      gtk_tree_view_column_set_title (tree_column,
                                      g_value_get_string (value));
      break;

Jonathan Blandford's avatar
Jonathan Blandford committed
575
576
577
578
579
    case PROP_EXPAND:
      gtk_tree_view_column_set_expand (tree_column,
				       g_value_get_boolean (value));
      break;

580
581
582
583
584
585
586
587
588
589
    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
590
591
592
593
594
    case PROP_ALIGNMENT:
      gtk_tree_view_column_set_alignment (tree_column,
                                          g_value_get_float (value));
      break;

595
596
597
598
599
    case PROP_REORDERABLE:
      gtk_tree_view_column_set_reorderable (tree_column,
					    g_value_get_boolean (value));
      break;

Havoc Pennington's avatar
Havoc Pennington committed
600
601
602
    case PROP_SORT_INDICATOR:
      gtk_tree_view_column_set_sort_indicator (tree_column,
                                               g_value_get_boolean (value));
603
604
      break;

Havoc Pennington's avatar
Havoc Pennington committed
605
606
607
608
609
    case PROP_SORT_ORDER:
      gtk_tree_view_column_set_sort_order (tree_column,
                                           g_value_get_enum (value));
      break;
      
610
611
612
613
    case PROP_SORT_COLUMN_ID:
      gtk_tree_view_column_set_sort_column_id (tree_column,
                                               g_value_get_int (value));
      break;
614
615
616
617
618
619

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

      if (area)
620
621
622
623
624
625
626
627
628
629
        {
          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);
        }
630
      break;
631

632
633
634
635
636
637
638
639
640
641
    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
642
                                   GParamSpec      *pspec)
643
644
645
646
647
648
649
650
{
  GtkTreeViewColumn *tree_column;

  tree_column = GTK_TREE_VIEW_COLUMN (object);

  switch (prop_id)
    {
    case PROP_VISIBLE:
Havoc Pennington's avatar
Havoc Pennington committed
651
652
      g_value_set_boolean (value,
                           gtk_tree_view_column_get_visible (tree_column));
653
654
      break;

Jonathan Blandford's avatar
Jonathan Blandford committed
655
656
657
658
659
    case PROP_RESIZABLE:
      g_value_set_boolean (value,
                           gtk_tree_view_column_get_resizable (tree_column));
      break;

660
661
662
663
664
    case PROP_X_OFFSET:
      g_value_set_int (value,
                       gtk_tree_view_column_get_x_offset (tree_column));
      break;

665
666
667
668
669
    case PROP_WIDTH:
      g_value_set_int (value,
                       gtk_tree_view_column_get_width (tree_column));
      break;

670
671
672
673
674
    case PROP_SPACING:
      g_value_set_int (value,
                       gtk_tree_view_column_get_spacing (tree_column));
      break;

675
    case PROP_SIZING:
Havoc Pennington's avatar
Havoc Pennington committed
676
677
      g_value_set_enum (value,
                        gtk_tree_view_column_get_sizing (tree_column));
678
679
      break;

680
    case PROP_FIXED_WIDTH:
Havoc Pennington's avatar
Havoc Pennington committed
681
      g_value_set_int (value,
682
                       gtk_tree_view_column_get_fixed_width (tree_column));
683
684
685
      break;

    case PROP_MIN_WIDTH:
Havoc Pennington's avatar
Havoc Pennington committed
686
687
      g_value_set_int (value,
                       gtk_tree_view_column_get_min_width (tree_column));
688
689
690
      break;

    case PROP_MAX_WIDTH:
Havoc Pennington's avatar
Havoc Pennington committed
691
692
      g_value_set_int (value,
                       gtk_tree_view_column_get_max_width (tree_column));
693
694
695
      break;

    case PROP_TITLE:
Havoc Pennington's avatar
Havoc Pennington committed
696
697
      g_value_set_string (value,
                          gtk_tree_view_column_get_title (tree_column));
698
699
      break;

Jonathan Blandford's avatar
Jonathan Blandford committed
700
701
702
703
704
    case PROP_EXPAND:
      g_value_set_boolean (value,
                          gtk_tree_view_column_get_expand (tree_column));
      break;

705
    case PROP_CLICKABLE:
Havoc Pennington's avatar
Havoc Pennington committed
706
707
      g_value_set_boolean (value,
                           gtk_tree_view_column_get_clickable (tree_column));
708
709
710
      break;

    case PROP_WIDGET:
Havoc Pennington's avatar
Havoc Pennington committed
711
712
      g_value_set_object (value,
                          (GObject*) gtk_tree_view_column_get_widget (tree_column));
713
714
      break;

Havoc Pennington's avatar
Havoc Pennington committed
715
716
717
    case PROP_ALIGNMENT:
      g_value_set_float (value,
                         gtk_tree_view_column_get_alignment (tree_column));
718
719
      break;

720
721
722
723
724
    case PROP_REORDERABLE:
      g_value_set_boolean (value,
			   gtk_tree_view_column_get_reorderable (tree_column));
      break;

Havoc Pennington's avatar
Havoc Pennington committed
725
726
727
728
729
730
731
732
733
734
    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;
      
735
736
737
738
    case PROP_SORT_COLUMN_ID:
      g_value_set_int (value,
                       gtk_tree_view_column_get_sort_column_id (tree_column));
      break;
739
740

    case PROP_CELL_AREA:
741
      g_value_set_object (value, tree_column->priv->cell_area);
742
      break;
743
      
744
745
746
747
748
749
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

750
751
/* Implementation of GtkCellLayout interface
 */
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785

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);
}

786
787
static GtkCellArea *
gtk_tree_view_column_cell_layout_get_area (GtkCellLayout   *cell_layout)
788
{
789
790
  GtkTreeViewColumn        *column = GTK_TREE_VIEW_COLUMN (cell_layout);
  GtkTreeViewColumnPrivate *priv   = column->priv;
791

792
793
794
  if (G_UNLIKELY (!priv->cell_area))
    gtk_tree_view_column_ensure_cell_area (column, NULL);

795
  return priv->cell_area;
Kristian Rietveld's avatar
Kristian Rietveld committed
796
797
}

798
799
800
801
static void
focus_in (GtkEventControllerKey *controller,
          GtkTreeViewColumn     *column)
{
802
  _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (column->priv->tree_view), column);
803
804
}

805
806
/* Button handling code
 */
807
static void
808
gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column)
809
{
810
  GtkTreeViewColumnPrivate *priv = tree_column->priv;
811
  GtkEventController *controller;
812
813
  GtkWidget *child;
  GtkWidget *hbox;
814

815
  g_return_if_fail (priv->button == NULL);
816

817
  priv->button = gtk_button_new ();
818
  g_object_ref_sink (priv->button);
819
  gtk_widget_set_focus_on_click (priv->button, FALSE);
820
  gtk_widget_set_overflow (priv->button, GTK_OVERFLOW_HIDDEN);
821

822
  g_signal_connect (priv->button, "clicked",
Manish Singh's avatar
Manish Singh committed
823
824
		    G_CALLBACK (gtk_tree_view_column_button_clicked),
		    tree_column);
825

826
827
828
829
830
831
832
833
  controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
  g_signal_connect (controller, "drag-begin",
                    G_CALLBACK (column_button_drag_begin), tree_column);
  g_signal_connect (controller, "drag-update",
                    G_CALLBACK (column_button_drag_update), tree_column);
  gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
  gtk_widget_add_controller (priv->button, controller);

834
835
  controller = gtk_event_controller_focus_new ();
  g_signal_connect (controller, "enter", G_CALLBACK (focus_in), tree_column);
836
837
  gtk_widget_add_controller (priv->button, controller);

838
  priv->frame = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
839
  gtk_widget_set_hexpand (priv->frame, TRUE);
Timm Bäder's avatar
Timm Bäder committed
840
  gtk_widget_set_halign (priv->frame, GTK_ALIGN_START);
841

842
  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
843
  priv->arrow = gtk_builtin_icon_new ("sort-indicator");
844

845
846
  if (priv->child)
    child = priv->child;
847
  else
848
    child = gtk_label_new (priv->title);
849

850
  g_signal_connect (child, "mnemonic-activate",
851
		    G_CALLBACK (gtk_tree_view_column_mnemonic_activate),
Manish Singh's avatar
Manish Singh committed
852
		    tree_column);
853

854
  if (priv->xalign <= 0.5)
855
    {
856
857
      gtk_box_append (GTK_BOX (hbox), priv->frame);
      gtk_box_append (GTK_BOX (hbox), priv->arrow);
858
    }
859
  else
860
    {
861
862
      gtk_box_append (GTK_BOX (hbox), priv->arrow);
      gtk_box_append (GTK_BOX (hbox), priv->frame);
863
    }
864

865
  gtk_box_append (GTK_BOX (priv->frame), child);
866
  gtk_button_set_child (GTK_BUTTON (priv->button), hbox);
867
868
}

869
static void
870
871
gtk_tree_view_column_update_button (GtkTreeViewColumn *tree_column)
{
872
  GtkTreeViewColumnPrivate *priv = tree_column->priv;
Benjamin Otte's avatar
Benjamin Otte committed
873
  int sort_column_id = -1;
874
  GtkWidget *hbox;
Timm Bäder's avatar
Timm Bäder committed
875
  GtkWidget *frame;
876
877
  GtkWidget *arrow;
  GtkWidget *current_child;
878
879
  GtkTreeModel *model;

880
881
  if (priv->tree_view)
    model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
882
883
  else
    model = NULL;
884

885
  hbox = gtk_button_get_child (GTK_BUTTON (priv->button));
Timm Bäder's avatar
Timm Bäder committed
886
  frame = priv->frame;
887
  arrow = priv->arrow;
888
  current_child = gtk_widget_get_first_child (frame);
889
890

  /* Set up the actual button */
891
  if (priv->child)
892
    {
893
      if (current_child != priv->child)
894
        {
895
896
          gtk_box_remove (GTK_BOX (frame), current_child);
          gtk_box_append (GTK_BOX (frame), priv->child);
897
        }
898
    }
899
  else
900
901
    {
      if (current_child == NULL)
902
903
904
        {
          current_child = gtk_label_new (NULL);
          gtk_widget_show (current_child);
905
          gtk_box_append (GTK_BOX (frame), current_child);
906
        }
907
908
909

      g_return_if_fail (GTK_IS_LABEL (current_child));

910
      if (priv->title)
911
912
        gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child),
                                          priv->title);
913
      else
914
915
        gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child),
                                          "");
916
917
    }

918
919
  if (GTK_IS_TREE_SORTABLE (model))
    gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model),
920
921
                                          &sort_column_id,
                                          NULL);
922

923
  if (priv->show_sort_indicator)
924
    {
925
926
      gboolean alternative;

927
928
929
930
931
932
      if (priv->tree_view)
        g_object_get (gtk_widget_get_settings (priv->tree_view),
                      "gtk-alternative-sort-arrows", &alternative,
                      NULL);
      else
        alternative = FALSE;
933

934
935
936
937
938
939
940
      if ((!alternative && priv->sort_order == GTK_SORT_ASCENDING) ||
          (alternative && priv->sort_order == GTK_SORT_DESCENDING))
        {
          gtk_widget_remove_css_class (arrow, "descending");
          gtk_widget_add_css_class (arrow, "ascending");
        }
      else
941
        {
942
943
944
          gtk_widget_remove_css_class (arrow, "ascending");
          gtk_widget_add_css_class (arrow, "descending");
        }
945
946
    }

947
948
949
950
  /* 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
   */
951
  if (priv->xalign <= 0.5)
952
    gtk_box_reorder_child_after (GTK_BOX (hbox), arrow, gtk_widget_get_last_child (hbox));
953
  else
954
    gtk_box_reorder_child_after (GTK_BOX (hbox), arrow, NULL);
955

956
957
  if (priv->show_sort_indicator
      || (GTK_IS_TREE_SORTABLE (model) && priv->sort_column_id >= 0))
958
959
960
961
    gtk_widget_show (arrow);
  else
    gtk_widget_hide (arrow);

962
963
964
965
966
  if (priv->show_sort_indicator)
    gtk_widget_set_opacity (arrow, 1.0);
  else
    gtk_widget_set_opacity (arrow, 0.0);

967
968
  /* 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. */
969
  if (priv->tree_view != NULL &&
970
      gtk_widget_get_realized (priv->tree_view))
971
    {
972
      if (priv->visible &&
973
          gtk_tree_view_get_headers_visible (GTK_TREE_VIEW (priv->tree_view)))
974
	{
975
          gtk_widget_show (priv->button);
976
977
978
	}
      else
	{
979
	  gtk_widget_hide (priv->button);
980
981
	}
    }
982
  
983
  if (priv->reorderable || priv->clickable)
984
    {
985
      gtk_widget_set_focusable (priv->button, TRUE);
986
    }
987
  else
988
    {
989
      gtk_widget_set_focusable (priv->button, FALSE);
990
      if (gtk_widget_has_focus (priv->button))
991
	{
992
993
          GtkRoot *root = gtk_widget_get_root (priv->tree_view);
	  gtk_root_set_focus (root, NULL);
994
	}
995
    }
996
997
998
  /* Queue a resize on the assumption that we always want to catch all changes
   * and columns don't change all that often.
   */
999
  if (priv->tree_view && gtk_widget_get_realized (priv->tree_view))
1000
     gtk_widget_queue_resize (priv->tree_view);