1 | /* |
2 | * Copyright © 2019 Benjamin Otte |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Authors: Benjamin Otte <otte@gnome.org> |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include "gtkcolumnviewcolumnprivate.h" |
23 | #include "gtkcolumnviewsorterprivate.h" |
24 | |
25 | #include "gtkcolumnviewprivate.h" |
26 | #include "gtkcolumnviewtitleprivate.h" |
27 | #include "gtkintl.h" |
28 | #include "gtklistbaseprivate.h" |
29 | #include "gtklistitemwidgetprivate.h" |
30 | #include "gtkmain.h" |
31 | #include "gtkprivate.h" |
32 | #include "gtkrbtreeprivate.h" |
33 | #include "gtksizegroup.h" |
34 | #include "gtkwidgetprivate.h" |
35 | #include "gtksorter.h" |
36 | |
37 | /** |
38 | * GtkColumnViewColumn: |
39 | * |
40 | * `GtkColumnViewColumn` represents the columns being added to `GtkColumnView`. |
41 | * |
42 | * The main ingredient for a `GtkColumnViewColumn` is the `GtkListItemFactory` |
43 | * that tells the columnview how to create cells for this column from items in |
44 | * the model. |
45 | * |
46 | * Columns have a title, and can optionally have a header menu set |
47 | * with [method@Gtk.ColumnViewColumn.set_header_menu]. |
48 | * |
49 | * A sorter can be associated with a column using |
50 | * [method@Gtk.ColumnViewColumn.set_sorter], to let users influence sorting |
51 | * by clicking on the column header. |
52 | */ |
53 | |
54 | struct _GtkColumnViewColumn |
55 | { |
56 | GObject parent_instance; |
57 | |
58 | GtkListItemFactory *factory; |
59 | char *title; |
60 | GtkSorter *sorter; |
61 | |
62 | /* data for the view */ |
63 | GtkColumnView *view; |
64 | GtkWidget *header; |
65 | |
66 | int minimum_size_request; |
67 | int natural_size_request; |
68 | int allocation_offset; |
69 | int allocation_size; |
70 | int header_position; |
71 | |
72 | int fixed_width; |
73 | |
74 | guint visible : 1; |
75 | guint resizable : 1; |
76 | guint expand : 1; |
77 | |
78 | GMenuModel *menu; |
79 | |
80 | /* This list isn't sorted - this is just caching for performance */ |
81 | GtkColumnViewCell *first_cell; /* no reference, just caching */ |
82 | }; |
83 | |
84 | struct _GtkColumnViewColumnClass |
85 | { |
86 | GObjectClass parent_class; |
87 | }; |
88 | |
89 | enum |
90 | { |
91 | PROP_0, |
92 | PROP_COLUMN_VIEW, |
93 | PROP_FACTORY, |
94 | PROP_TITLE, |
95 | PROP_SORTER, |
96 | PROP_VISIBLE, |
97 | , |
98 | PROP_RESIZABLE, |
99 | PROP_EXPAND, |
100 | PROP_FIXED_WIDTH, |
101 | |
102 | N_PROPS |
103 | }; |
104 | |
105 | G_DEFINE_TYPE (GtkColumnViewColumn, gtk_column_view_column, G_TYPE_OBJECT) |
106 | |
107 | static GParamSpec *properties[N_PROPS] = { NULL, }; |
108 | |
109 | static void |
110 | gtk_column_view_column_dispose (GObject *object) |
111 | { |
112 | GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object); |
113 | |
114 | g_assert (self->view == NULL); /* would hold a ref otherwise */ |
115 | g_assert (self->first_cell == NULL); /* no view = no children */ |
116 | |
117 | g_clear_object (&self->factory); |
118 | g_clear_object (&self->sorter); |
119 | g_clear_pointer (&self->title, g_free); |
120 | g_clear_object (&self->menu); |
121 | |
122 | G_OBJECT_CLASS (gtk_column_view_column_parent_class)->dispose (object); |
123 | } |
124 | |
125 | static void |
126 | gtk_column_view_column_get_property (GObject *object, |
127 | guint property_id, |
128 | GValue *value, |
129 | GParamSpec *pspec) |
130 | { |
131 | GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object); |
132 | |
133 | switch (property_id) |
134 | { |
135 | case PROP_COLUMN_VIEW: |
136 | g_value_set_object (value, v_object: self->view); |
137 | break; |
138 | |
139 | case PROP_FACTORY: |
140 | g_value_set_object (value, v_object: self->factory); |
141 | break; |
142 | |
143 | case PROP_TITLE: |
144 | g_value_set_string (value, v_string: self->title); |
145 | break; |
146 | |
147 | case PROP_SORTER: |
148 | g_value_set_object (value, v_object: self->sorter); |
149 | break; |
150 | |
151 | case PROP_VISIBLE: |
152 | g_value_set_boolean (value, v_boolean: self->visible); |
153 | break; |
154 | |
155 | case PROP_HEADER_MENU: |
156 | g_value_set_object (value, v_object: self->menu); |
157 | break; |
158 | |
159 | case PROP_RESIZABLE: |
160 | g_value_set_boolean (value, v_boolean: self->resizable); |
161 | break; |
162 | |
163 | case PROP_EXPAND: |
164 | g_value_set_boolean (value, v_boolean: self->expand); |
165 | break; |
166 | |
167 | case PROP_FIXED_WIDTH: |
168 | g_value_set_int (value, v_int: self->fixed_width); |
169 | break; |
170 | |
171 | default: |
172 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
173 | break; |
174 | } |
175 | } |
176 | |
177 | static void |
178 | gtk_column_view_column_set_property (GObject *object, |
179 | guint property_id, |
180 | const GValue *value, |
181 | GParamSpec *pspec) |
182 | { |
183 | GtkColumnViewColumn *self = GTK_COLUMN_VIEW_COLUMN (object); |
184 | |
185 | switch (property_id) |
186 | { |
187 | case PROP_FACTORY: |
188 | gtk_column_view_column_set_factory (self, factory: g_value_get_object (value)); |
189 | break; |
190 | |
191 | case PROP_TITLE: |
192 | gtk_column_view_column_set_title (self, title: g_value_get_string (value)); |
193 | break; |
194 | |
195 | case PROP_SORTER: |
196 | gtk_column_view_column_set_sorter (self, sorter: g_value_get_object (value)); |
197 | break; |
198 | |
199 | case PROP_VISIBLE: |
200 | gtk_column_view_column_set_visible (self, visible: g_value_get_boolean (value)); |
201 | break; |
202 | |
203 | case PROP_HEADER_MENU: |
204 | gtk_column_view_column_set_header_menu (self, menu: g_value_get_object (value)); |
205 | break; |
206 | |
207 | case PROP_RESIZABLE: |
208 | gtk_column_view_column_set_resizable (self, resizable: g_value_get_boolean (value)); |
209 | break; |
210 | |
211 | case PROP_EXPAND: |
212 | gtk_column_view_column_set_expand (self, expand: g_value_get_boolean (value)); |
213 | break; |
214 | |
215 | case PROP_FIXED_WIDTH: |
216 | gtk_column_view_column_set_fixed_width (self, fixed_width: g_value_get_int (value)); |
217 | break; |
218 | |
219 | default: |
220 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
221 | break; |
222 | } |
223 | } |
224 | |
225 | static void |
226 | gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass) |
227 | { |
228 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
229 | |
230 | gobject_class->dispose = gtk_column_view_column_dispose; |
231 | gobject_class->get_property = gtk_column_view_column_get_property; |
232 | gobject_class->set_property = gtk_column_view_column_set_property; |
233 | |
234 | /** |
235 | * GtkColumnViewColumn:column-view: (attributes org.gtk.Property.get=gtk_column_view_column_get_column_view) |
236 | * |
237 | * The `GtkColumnView` this column is a part of. |
238 | */ |
239 | properties[PROP_COLUMN_VIEW] = |
240 | g_param_spec_object (name: "column-view" , |
241 | P_("Column view" ), |
242 | P_("Column view this column is a part of" ), |
243 | GTK_TYPE_COLUMN_VIEW, |
244 | flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
245 | |
246 | /** |
247 | * GtkColumnViewColumn:factory: (attributes org.gtk.Property.get=gtk_column_view_column_get_factory org.gtk.Property.set=gtk_column_view_column_set_factory) |
248 | * |
249 | * Factory for populating list items. |
250 | */ |
251 | properties[PROP_FACTORY] = |
252 | g_param_spec_object (name: "factory" , |
253 | P_("Factory" ), |
254 | P_("Factory for populating list items" ), |
255 | GTK_TYPE_LIST_ITEM_FACTORY, |
256 | flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
257 | |
258 | /** |
259 | * GtkColumnViewColumn:title: (attributes org.gtk.Property.get=gtk_column_view_column_get_title org.gtk.Property.set=gtk_column_view_column_set_title) |
260 | * |
261 | * Title displayed in the header. |
262 | */ |
263 | properties[PROP_TITLE] = |
264 | g_param_spec_string (name: "title" , |
265 | P_("Title" ), |
266 | P_("Title displayed in the header" ), |
267 | NULL, |
268 | flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
269 | |
270 | /** |
271 | * GtkColumnViewColumn:sorter: (attributes org.gtk.Property.get=gtk_column_view_column_get_sorter org.gtk.Property.set=gtk_column_view_column_set_sorter) |
272 | * |
273 | * Sorter for sorting items according to this column. |
274 | */ |
275 | properties[PROP_SORTER] = |
276 | g_param_spec_object (name: "sorter" , |
277 | P_("Sorter" ), |
278 | P_("Sorter for sorting items according to this column" ), |
279 | GTK_TYPE_SORTER, |
280 | flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
281 | |
282 | /** |
283 | * GtkColumnViewColumn:visible: (attributes org.gtk.Property.get=gtk_column_view_column_get_visible org.gtk.Property.set=gtk_column_view_column_set_visible) |
284 | * |
285 | * Whether this column is visible. |
286 | */ |
287 | properties[PROP_VISIBLE] = |
288 | g_param_spec_boolean (name: "visible" , |
289 | P_("Visible" ), |
290 | P_("Whether this column is visible" ), |
291 | TRUE, |
292 | flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
293 | |
294 | /** |
295 | * GtkColumnViewColumn:header-menu: (attributes org.gtk.Property.get=gtk_column_view_column_get_header_menu org.gtk.Property.set=gtk_column_view_column_set_header_menu) |
296 | * |
297 | * Menu model used to create the context menu for the column header. |
298 | */ |
299 | properties[PROP_HEADER_MENU] = |
300 | g_param_spec_object (name: "header-menu" , |
301 | P_("Header menu" ), |
302 | P_("Menu to use on the title of this column" ), |
303 | G_TYPE_MENU_MODEL, |
304 | flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
305 | |
306 | /** |
307 | * GtkColumnViewColumn:resizable: (attributes org.gtk.Property.get=gtk_column_view_column_get_resizable org.gtk.Property.set=gtk_column_view_column_set_resizable) |
308 | * |
309 | * Whether this column is resizable. |
310 | */ |
311 | properties[PROP_RESIZABLE] = |
312 | g_param_spec_boolean (name: "resizable" , |
313 | P_("Resizable" ), |
314 | P_("Whether this column is resizable" ), |
315 | FALSE, |
316 | flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
317 | |
318 | /** |
319 | * GtkColumnViewColumn:expand: (attributes org.gtk.Property.get=gtk_column_view_column_get_expand org.gtk.Property.set=gtk_column_view_column_set_expand) |
320 | * |
321 | * Column gets share of extra width allocated to the view. |
322 | */ |
323 | properties[PROP_EXPAND] = |
324 | g_param_spec_boolean (name: "expand" , |
325 | P_("Expand" ), |
326 | P_("column gets share of extra width" ), |
327 | FALSE, |
328 | flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
329 | |
330 | /** |
331 | * GtkColumnViewColumn:fixed-width: (attributes org.gtk.Property.get=gtk_column_view_column_get_fixed_width org.gtk.Property.set=gtk_column_view_column_set_fixed_width) |
332 | * |
333 | * If not -1, this is the width that the column is allocated, |
334 | * regardless of the size of its content. |
335 | */ |
336 | properties[PROP_FIXED_WIDTH] = |
337 | g_param_spec_int (name: "fixed-width" , |
338 | P_("Fixed width" ), |
339 | P_("Fixed width of this column" ), |
340 | minimum: -1, G_MAXINT, default_value: -1, |
341 | flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
342 | |
343 | g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_PROPS, pspecs: properties); |
344 | } |
345 | |
346 | static void |
347 | gtk_column_view_column_init (GtkColumnViewColumn *self) |
348 | { |
349 | self->minimum_size_request = -1; |
350 | self->natural_size_request = -1; |
351 | self->visible = TRUE; |
352 | self->resizable = FALSE; |
353 | self->expand = FALSE; |
354 | self->fixed_width = -1; |
355 | } |
356 | |
357 | /** |
358 | * gtk_column_view_column_new: |
359 | * @title: (nullable): Title to use for this column |
360 | * @factory: (transfer full) (nullable): The factory to populate items with |
361 | * |
362 | * Creates a new `GtkColumnViewColumn` that uses the given @factory for |
363 | * mapping items to widgets. |
364 | * |
365 | * You most likely want to call [method@Gtk.ColumnView.append_column] next. |
366 | * |
367 | * The function takes ownership of the argument, so you can write code like: |
368 | * |
369 | * ```c |
370 | * column = gtk_column_view_column_new (_("Name"), |
371 | * gtk_builder_list_item_factory_new_from_resource ("/name.ui")); |
372 | * ``` |
373 | * |
374 | * Returns: a new `GtkColumnViewColumn` using the given @factory |
375 | */ |
376 | GtkColumnViewColumn * |
377 | gtk_column_view_column_new (const char *title, |
378 | GtkListItemFactory *factory) |
379 | { |
380 | GtkColumnViewColumn *result; |
381 | |
382 | g_return_val_if_fail (factory == NULL || GTK_IS_LIST_ITEM_FACTORY (factory), NULL); |
383 | |
384 | result = g_object_new (GTK_TYPE_COLUMN_VIEW_COLUMN, |
385 | first_property_name: "factory" , factory, |
386 | "title" , title, |
387 | NULL); |
388 | |
389 | g_clear_object (&factory); |
390 | |
391 | return result; |
392 | } |
393 | |
394 | GtkColumnViewCell * |
395 | gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self) |
396 | { |
397 | return self->first_cell; |
398 | } |
399 | |
400 | void |
401 | gtk_column_view_column_add_cell (GtkColumnViewColumn *self, |
402 | GtkColumnViewCell *cell) |
403 | { |
404 | self->first_cell = cell; |
405 | |
406 | gtk_widget_set_visible (GTK_WIDGET (cell), visible: self->visible); |
407 | gtk_column_view_column_queue_resize (self); |
408 | } |
409 | |
410 | void |
411 | gtk_column_view_column_remove_cell (GtkColumnViewColumn *self, |
412 | GtkColumnViewCell *cell) |
413 | { |
414 | if (cell == self->first_cell) |
415 | self->first_cell = gtk_column_view_cell_get_next (self: cell); |
416 | |
417 | gtk_column_view_column_queue_resize (self); |
418 | gtk_widget_queue_resize (GTK_WIDGET (cell)); |
419 | } |
420 | |
421 | void |
422 | gtk_column_view_column_queue_resize (GtkColumnViewColumn *self) |
423 | { |
424 | GtkColumnViewCell *cell; |
425 | |
426 | if (self->minimum_size_request < 0) |
427 | return; |
428 | |
429 | self->minimum_size_request = -1; |
430 | self->natural_size_request = -1; |
431 | |
432 | if (self->header) |
433 | gtk_widget_queue_resize (widget: self->header); |
434 | |
435 | for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (self: cell)) |
436 | { |
437 | gtk_widget_queue_resize (GTK_WIDGET (cell)); |
438 | } |
439 | } |
440 | |
441 | void |
442 | gtk_column_view_column_measure (GtkColumnViewColumn *self, |
443 | int *minimum, |
444 | int *natural) |
445 | { |
446 | if (self->fixed_width > -1) |
447 | { |
448 | self->minimum_size_request = self->fixed_width; |
449 | self->natural_size_request = self->fixed_width; |
450 | } |
451 | |
452 | if (self->minimum_size_request < 0) |
453 | { |
454 | GtkColumnViewCell *cell; |
455 | int min, nat, cell_min, cell_nat; |
456 | |
457 | if (self->header) |
458 | { |
459 | gtk_widget_measure (widget: self->header, orientation: GTK_ORIENTATION_HORIZONTAL, for_size: -1, minimum: &min, natural: &nat, NULL, NULL); |
460 | } |
461 | else |
462 | { |
463 | min = 0; |
464 | nat = 0; |
465 | } |
466 | |
467 | for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (self: cell)) |
468 | { |
469 | gtk_widget_measure (GTK_WIDGET (cell), |
470 | orientation: GTK_ORIENTATION_HORIZONTAL, |
471 | for_size: -1, |
472 | minimum: &cell_min, natural: &cell_nat, |
473 | NULL, NULL); |
474 | |
475 | min = MAX (min, cell_min); |
476 | nat = MAX (nat, cell_nat); |
477 | } |
478 | |
479 | self->minimum_size_request = min; |
480 | self->natural_size_request = nat; |
481 | } |
482 | |
483 | *minimum = self->minimum_size_request; |
484 | *natural = self->natural_size_request; |
485 | } |
486 | |
487 | void |
488 | gtk_column_view_column_allocate (GtkColumnViewColumn *self, |
489 | int offset, |
490 | int size) |
491 | { |
492 | self->allocation_offset = offset; |
493 | self->allocation_size = size; |
494 | self->header_position = offset; |
495 | } |
496 | |
497 | void |
498 | gtk_column_view_column_get_allocation (GtkColumnViewColumn *self, |
499 | int *offset, |
500 | int *size) |
501 | { |
502 | if (offset) |
503 | *offset = self->allocation_offset; |
504 | if (size) |
505 | *size = self->allocation_size; |
506 | } |
507 | |
508 | static void |
509 | gtk_column_view_column_create_cells (GtkColumnViewColumn *self) |
510 | { |
511 | GtkListView *list; |
512 | GtkWidget *row; |
513 | |
514 | if (self->first_cell) |
515 | return; |
516 | |
517 | list = gtk_column_view_get_list_view (GTK_COLUMN_VIEW (self->view)); |
518 | for (row = gtk_widget_get_first_child (GTK_WIDGET (list)); |
519 | row != NULL; |
520 | row = gtk_widget_get_next_sibling (widget: row)) |
521 | { |
522 | GtkListItemWidget *list_item; |
523 | GtkWidget *cell; |
524 | |
525 | if (!gtk_widget_get_root (widget: row)) |
526 | continue; |
527 | |
528 | list_item = GTK_LIST_ITEM_WIDGET (row); |
529 | cell = gtk_column_view_cell_new (column: self); |
530 | gtk_list_item_widget_add_child (self: list_item, child: cell); |
531 | gtk_list_item_widget_update (GTK_LIST_ITEM_WIDGET (cell), |
532 | position: gtk_list_item_widget_get_position (self: list_item), |
533 | item: gtk_list_item_widget_get_item (self: list_item), |
534 | selected: gtk_list_item_widget_get_selected (self: list_item)); |
535 | } |
536 | } |
537 | |
538 | static void |
539 | gtk_column_view_column_remove_cells (GtkColumnViewColumn *self) |
540 | { |
541 | while (self->first_cell) |
542 | gtk_column_view_cell_remove (self: self->first_cell); |
543 | } |
544 | |
545 | static void |
546 | gtk_column_view_column_create_header (GtkColumnViewColumn *self) |
547 | { |
548 | if (self->header != NULL) |
549 | return; |
550 | |
551 | self->header = gtk_column_view_title_new (column: self); |
552 | gtk_widget_set_visible (widget: self->header, visible: self->visible); |
553 | gtk_list_item_widget_add_child (self: gtk_column_view_get_header_widget (self: self->view), |
554 | child: self->header); |
555 | gtk_column_view_column_queue_resize (self); |
556 | } |
557 | |
558 | static void |
559 | gtk_column_view_column_remove_header (GtkColumnViewColumn *self) |
560 | { |
561 | if (self->header == NULL) |
562 | return; |
563 | |
564 | gtk_list_item_widget_remove_child (self: gtk_column_view_get_header_widget (self: self->view), |
565 | child: self->header); |
566 | self->header = NULL; |
567 | gtk_column_view_column_queue_resize (self); |
568 | } |
569 | |
570 | static void |
571 | gtk_column_view_column_ensure_cells (GtkColumnViewColumn *self) |
572 | { |
573 | if (self->view && gtk_widget_get_root (GTK_WIDGET (self->view))) |
574 | gtk_column_view_column_create_cells (self); |
575 | else |
576 | gtk_column_view_column_remove_cells (self); |
577 | |
578 | if (self->view) |
579 | gtk_column_view_column_create_header (self); |
580 | else |
581 | gtk_column_view_column_remove_header (self); |
582 | } |
583 | |
584 | /** |
585 | * gtk_column_view_column_get_column_view: (attributes org.gtk.Method.get_property=column-view) |
586 | * @self: a `GtkColumnViewColumn` |
587 | * |
588 | * Gets the column view that's currently displaying this column. |
589 | * |
590 | * If @self has not been added to a column view yet, %NULL is returned. |
591 | * |
592 | * Returns: (nullable) (transfer none): The column view displaying @self. |
593 | */ |
594 | GtkColumnView * |
595 | gtk_column_view_column_get_column_view (GtkColumnViewColumn *self) |
596 | { |
597 | g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL); |
598 | |
599 | return self->view; |
600 | } |
601 | |
602 | void |
603 | gtk_column_view_column_set_column_view (GtkColumnViewColumn *self, |
604 | GtkColumnView *view) |
605 | { |
606 | if (self->view == view) |
607 | return; |
608 | |
609 | gtk_column_view_column_remove_cells (self); |
610 | gtk_column_view_column_remove_header (self); |
611 | |
612 | self->view = view; |
613 | |
614 | gtk_column_view_column_ensure_cells (self); |
615 | |
616 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_COLUMN_VIEW]); |
617 | } |
618 | |
619 | void |
620 | gtk_column_view_column_set_position (GtkColumnViewColumn *self, |
621 | guint position) |
622 | { |
623 | GtkColumnViewCell *cell; |
624 | |
625 | gtk_list_item_widget_reorder_child (self: gtk_column_view_get_header_widget (self: self->view), |
626 | child: self->header, |
627 | position); |
628 | |
629 | for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (self: cell)) |
630 | { |
631 | GtkListItemWidget *list_item; |
632 | |
633 | list_item = GTK_LIST_ITEM_WIDGET (gtk_widget_get_parent (GTK_WIDGET (cell))); |
634 | gtk_list_item_widget_reorder_child (self: list_item, GTK_WIDGET (cell), position); |
635 | } |
636 | } |
637 | |
638 | /** |
639 | * gtk_column_view_column_get_factory: (attributes org.gtk.Method.get_property=factory) |
640 | * @self: a `GtkColumnViewColumn` |
641 | * |
642 | * Gets the factory that's currently used to populate list items for |
643 | * this column. |
644 | * |
645 | * Returns: (nullable) (transfer none): The factory in use |
646 | **/ |
647 | GtkListItemFactory * |
648 | gtk_column_view_column_get_factory (GtkColumnViewColumn *self) |
649 | { |
650 | g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL); |
651 | |
652 | return self->factory; |
653 | } |
654 | |
655 | /** |
656 | * gtk_column_view_column_set_factory: (attributes org.gtk.Method.set_property=factory) |
657 | * @self: a `GtkColumnViewColumn` |
658 | * @factory: (nullable) (transfer none): the factory to use |
659 | * |
660 | * Sets the `GtkListItemFactory` to use for populating list items for this |
661 | * column. |
662 | */ |
663 | void |
664 | gtk_column_view_column_set_factory (GtkColumnViewColumn *self, |
665 | GtkListItemFactory *factory) |
666 | { |
667 | g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self)); |
668 | g_return_if_fail (factory == NULL || GTK_LIST_ITEM_FACTORY (factory)); |
669 | |
670 | if (!g_set_object (&self->factory, factory)) |
671 | return; |
672 | |
673 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_FACTORY]); |
674 | } |
675 | |
676 | /** |
677 | * gtk_column_view_column_set_title: (attributes org.gtk.Method.set_property=title) |
678 | * @self: a `GtkColumnViewColumn` |
679 | * @title: (nullable): Title to use for this column |
680 | * |
681 | * Sets the title of this column. |
682 | * |
683 | * The title is displayed in the header of a `GtkColumnView` |
684 | * for this column and is therefore user-facing text that should |
685 | * be translated. |
686 | */ |
687 | void |
688 | gtk_column_view_column_set_title (GtkColumnViewColumn *self, |
689 | const char *title) |
690 | { |
691 | g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self)); |
692 | |
693 | if (g_strcmp0 (str1: self->title, str2: title) == 0) |
694 | return; |
695 | |
696 | g_free (mem: self->title); |
697 | self->title = g_strdup (str: title); |
698 | |
699 | if (self->header) |
700 | gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header)); |
701 | |
702 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_TITLE]); |
703 | } |
704 | |
705 | /** |
706 | * gtk_column_view_column_get_title: (attributes org.gtk.Method.get_property=title) |
707 | * @self: a `GtkColumnViewColumn` |
708 | * |
709 | * Returns the title set with gtk_column_view_column_set_title(). |
710 | * |
711 | * Returns: (nullable): The column's title |
712 | */ |
713 | const char * |
714 | gtk_column_view_column_get_title (GtkColumnViewColumn *self) |
715 | { |
716 | g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), FALSE); |
717 | |
718 | return self->title; |
719 | } |
720 | |
721 | #if 0 |
722 | static void |
723 | gtk_column_view_column_add_to_sorter (GtkColumnViewColumn *self) |
724 | { |
725 | if (self->view == NULL) |
726 | return; |
727 | |
728 | gtk_column_view_sorter_add_column (GTK_COLUMN_VIEW_SORTER (gtk_column_view_get_sorter (self->view)), self); |
729 | } |
730 | #endif |
731 | |
732 | static void |
733 | gtk_column_view_column_remove_from_sorter (GtkColumnViewColumn *self) |
734 | { |
735 | if (self->view == NULL) |
736 | return; |
737 | |
738 | gtk_column_view_sorter_remove_column (self: GTK_COLUMN_VIEW_SORTER (ptr: gtk_column_view_get_sorter (self: self->view)), column: self); |
739 | } |
740 | |
741 | /** |
742 | * gtk_column_view_column_set_sorter: (attributes org.gtk.Method.set_property=sorter) |
743 | * @self: a `GtkColumnViewColumn` |
744 | * @sorter: (nullable): the `GtkSorter` to associate with @column |
745 | * |
746 | * Associates a sorter with the column. |
747 | * |
748 | * If @sorter is %NULL, the column will not let users change |
749 | * the sorting by clicking on its header. |
750 | * |
751 | * This sorter can be made active by clicking on the column |
752 | * header, or by calling [method@Gtk.ColumnView.sort_by_column]. |
753 | * |
754 | * See [method@Gtk.ColumnView.get_sorter] for the necessary steps |
755 | * for setting up customizable sorting for [class@Gtk.ColumnView]. |
756 | */ |
757 | void |
758 | gtk_column_view_column_set_sorter (GtkColumnViewColumn *self, |
759 | GtkSorter *sorter) |
760 | { |
761 | g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self)); |
762 | g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter)); |
763 | |
764 | if (!g_set_object (&self->sorter, sorter)) |
765 | return; |
766 | |
767 | gtk_column_view_column_remove_from_sorter (self); |
768 | |
769 | if (self->header) |
770 | gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header)); |
771 | |
772 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_SORTER]); |
773 | } |
774 | |
775 | /** |
776 | * gtk_column_view_column_get_sorter: (attributes org.gtk.Method.get_property=sorter) |
777 | * @self: a `GtkColumnViewColumn` |
778 | * |
779 | * Returns the sorter that is associated with the column. |
780 | * |
781 | * Returns: (nullable) (transfer none): the `GtkSorter` of @self |
782 | */ |
783 | GtkSorter * |
784 | gtk_column_view_column_get_sorter (GtkColumnViewColumn *self) |
785 | { |
786 | g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL); |
787 | |
788 | return self->sorter; |
789 | } |
790 | |
791 | void |
792 | gtk_column_view_column_notify_sort (GtkColumnViewColumn *self) |
793 | { |
794 | if (self->header) |
795 | gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header)); |
796 | } |
797 | |
798 | /** |
799 | * gtk_column_view_column_set_visible: (attributes org.gtk.Method.set_property=visible) |
800 | * @self: a `GtkColumnViewColumn` |
801 | * @visible: whether this column should be visible |
802 | * |
803 | * Sets whether this column should be visible in views. |
804 | */ |
805 | void |
806 | gtk_column_view_column_set_visible (GtkColumnViewColumn *self, |
807 | gboolean visible) |
808 | { |
809 | GtkColumnViewCell *cell; |
810 | |
811 | g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self)); |
812 | |
813 | if (self->visible == visible) |
814 | return; |
815 | |
816 | self->visible = visible; |
817 | |
818 | self->minimum_size_request = -1; |
819 | self->natural_size_request = -1; |
820 | |
821 | if (self->header) |
822 | gtk_widget_set_visible (GTK_WIDGET (self->header), visible); |
823 | |
824 | for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (self: cell)) |
825 | { |
826 | gtk_widget_set_visible (GTK_WIDGET (cell), visible); |
827 | } |
828 | |
829 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_VISIBLE]); |
830 | } |
831 | |
832 | /** |
833 | * gtk_column_view_column_get_visible: (attributes org.gtk.Method.get_property=visible) |
834 | * @self: a `GtkColumnViewColumn` |
835 | * |
836 | * Returns whether this column is visible. |
837 | * |
838 | * Returns: %TRUE if this column is visible |
839 | */ |
840 | gboolean |
841 | gtk_column_view_column_get_visible (GtkColumnViewColumn *self) |
842 | { |
843 | g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), TRUE); |
844 | |
845 | return self->visible; |
846 | } |
847 | |
848 | /** |
849 | * gtk_column_view_column_set_header_menu: (attributes org.gtk.Method.set_property=header-menu) |
850 | * @self: a `GtkColumnViewColumn` |
851 | * @menu: (nullable): a `GMenuModel` |
852 | * |
853 | * Sets the menu model that is used to create the context menu |
854 | * for the column header. |
855 | */ |
856 | void |
857 | gtk_column_view_column_set_header_menu (GtkColumnViewColumn *self, |
858 | GMenuModel *) |
859 | { |
860 | g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self)); |
861 | g_return_if_fail (menu == NULL || G_IS_MENU_MODEL (menu)); |
862 | |
863 | if (!g_set_object (&self->menu, menu)) |
864 | return; |
865 | |
866 | if (self->header) |
867 | gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header)); |
868 | |
869 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_HEADER_MENU]); |
870 | } |
871 | |
872 | /** |
873 | * gtk_column_view_column_get_header_menu: (attributes org.gtk.Method.get_property=header-menu) |
874 | * @self: a `GtkColumnViewColumn` |
875 | * |
876 | * Gets the menu model that is used to create the context menu |
877 | * for the column header. |
878 | * |
879 | * Returns: (transfer none) (nullable): the `GMenuModel` |
880 | */ |
881 | GMenuModel * |
882 | gtk_column_view_column_get_header_menu (GtkColumnViewColumn *self) |
883 | { |
884 | g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL); |
885 | |
886 | return self->menu; |
887 | } |
888 | |
889 | /** |
890 | * gtk_column_view_column_set_expand: (attributes org.gtk.Method.set_property=expand) |
891 | * @self: a `GtkColumnViewColumn` |
892 | * @expand: %TRUE if this column should expand to fill available sace |
893 | * |
894 | * Sets the column to take available extra space. |
895 | * |
896 | * The extra space is shared equally amongst all columns that |
897 | * have the expand set to %TRUE. |
898 | */ |
899 | void |
900 | gtk_column_view_column_set_expand (GtkColumnViewColumn *self, |
901 | gboolean expand) |
902 | { |
903 | g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self)); |
904 | |
905 | if (self->expand == expand) |
906 | return; |
907 | |
908 | self->expand = expand; |
909 | |
910 | if (self->visible && self->view) |
911 | gtk_widget_queue_resize (GTK_WIDGET (self->view)); |
912 | |
913 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_EXPAND]); |
914 | } |
915 | |
916 | /** |
917 | * gtk_column_view_column_get_expand: (attributes org.gtk.Method.get_property=expand) |
918 | * @self: a `GtkColumnViewColumn` |
919 | * |
920 | * Returns whether this column should expand. |
921 | * |
922 | * Returns: %TRUE if this column expands |
923 | */ |
924 | gboolean |
925 | gtk_column_view_column_get_expand (GtkColumnViewColumn *self) |
926 | { |
927 | g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), TRUE); |
928 | |
929 | return self->expand; |
930 | } |
931 | |
932 | /** |
933 | * gtk_column_view_column_set_resizable: (attributes org.gtk.Method.set_property=resizable) |
934 | * @self: a `GtkColumnViewColumn` |
935 | * @resizable: whether this column should be resizable |
936 | * |
937 | * Sets whether this column should be resizable by dragging. |
938 | */ |
939 | void |
940 | gtk_column_view_column_set_resizable (GtkColumnViewColumn *self, |
941 | gboolean resizable) |
942 | { |
943 | g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self)); |
944 | |
945 | if (self->resizable == resizable) |
946 | return; |
947 | |
948 | self->resizable = resizable; |
949 | |
950 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_RESIZABLE]); |
951 | } |
952 | |
953 | /** |
954 | * gtk_column_view_column_get_resizable: (attributes org.gtk.Method.get_property=resizable) |
955 | * @self: a `GtkColumnViewColumn` |
956 | * |
957 | * Returns whether this column is resizable. |
958 | * |
959 | * Returns: %TRUE if this column is resizable |
960 | */ |
961 | gboolean |
962 | gtk_column_view_column_get_resizable (GtkColumnViewColumn *self) |
963 | { |
964 | g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), TRUE); |
965 | |
966 | return self->resizable; |
967 | } |
968 | |
969 | /** |
970 | * gtk_column_view_column_set_fixed_width: (attributes org.gtk.Method.set_property=fixed-width) |
971 | * @self: a `GtkColumnViewColumn` |
972 | * @fixed_width: the new fixed width, or -1 |
973 | * |
974 | * If @fixed_width is not -1, sets the fixed width of @column; |
975 | * otherwise unsets it. |
976 | * |
977 | * Setting a fixed width overrides the automatically calculated |
978 | * width. Interactive resizing also sets the “fixed-width” property. |
979 | */ |
980 | void |
981 | gtk_column_view_column_set_fixed_width (GtkColumnViewColumn *self, |
982 | int fixed_width) |
983 | { |
984 | g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self)); |
985 | g_return_if_fail (fixed_width >= -1); |
986 | |
987 | if (self->fixed_width == fixed_width) |
988 | return; |
989 | |
990 | self->fixed_width = fixed_width; |
991 | |
992 | gtk_column_view_column_queue_resize (self); |
993 | |
994 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_FIXED_WIDTH]); |
995 | } |
996 | |
997 | /** |
998 | * gtk_column_view_column_get_fixed_width: (attributes org.gtk.Method.get_property=fixed-width) |
999 | * @self: a `GtkColumnViewColumn` |
1000 | * |
1001 | * Gets the fixed width of the column. |
1002 | * |
1003 | * Returns: the fixed with of the column |
1004 | */ |
1005 | int |
1006 | gtk_column_view_column_get_fixed_width (GtkColumnViewColumn *self) |
1007 | { |
1008 | g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), -1); |
1009 | |
1010 | return self->fixed_width; |
1011 | } |
1012 | |
1013 | GtkWidget * |
1014 | gtk_column_view_column_get_header (GtkColumnViewColumn *self) |
1015 | { |
1016 | return self->header; |
1017 | } |
1018 | |
1019 | void |
1020 | gtk_column_view_column_set_header_position (GtkColumnViewColumn *self, |
1021 | int offset) |
1022 | { |
1023 | self->header_position = offset; |
1024 | } |
1025 | |
1026 | void |
1027 | gtk_column_view_column_get_header_allocation (GtkColumnViewColumn *self, |
1028 | int *offset, |
1029 | int *size) |
1030 | { |
1031 | if (offset) |
1032 | *offset = self->header_position; |
1033 | |
1034 | if (size) |
1035 | *size = self->allocation_size; |
1036 | } |
1037 | |