1 /*
2 * e-table-sort-info.c
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 #include "e-table-sort-info.h"
19
20 #include <string.h>
21
22 #include "e-table-specification.h"
23 #include "e-xml-utils.h"
24
25 #define E_TABLE_SORT_INFO_GET_PRIVATE(obj) \
26 (G_TYPE_INSTANCE_GET_PRIVATE \
27 ((obj), E_TYPE_TABLE_SORT_INFO, ETableSortInfoPrivate))
28
29 typedef struct _ColumnData ColumnData;
30
31 struct _ETableSortInfoPrivate {
32 GWeakRef specification;
33 GArray *groupings;
34 GArray *sortings;
35 gboolean can_group;
36 };
37
38 struct _ColumnData {
39 ETableColumnSpecification *column_spec;
40 GtkSortType sort_type;
41 };
42
43 enum {
44 PROP_0,
45 PROP_SPECIFICATION
46 };
47
48 enum {
49 SORT_INFO_CHANGED,
50 GROUP_INFO_CHANGED,
51 LAST_SIGNAL
52 };
53
54 static guint signals[LAST_SIGNAL];
55
G_DEFINE_TYPE(ETableSortInfo,e_table_sort_info,G_TYPE_OBJECT)56 G_DEFINE_TYPE (ETableSortInfo , e_table_sort_info, G_TYPE_OBJECT)
57
58 static void
59 column_data_clear (ColumnData *data)
60 {
61 g_clear_object (&data->column_spec);
62 }
63
64 static void
table_sort_info_parser_start_group(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,ETableSortInfo * sort_info,GPtrArray * columns,GError ** error)65 table_sort_info_parser_start_group (GMarkupParseContext *context,
66 const gchar *element_name,
67 const gchar **attribute_names,
68 const gchar **attribute_values,
69 ETableSortInfo *sort_info,
70 GPtrArray *columns,
71 GError **error)
72 {
73 const gchar *index_str;
74 gboolean ascending;
75 gboolean success;
76
77 success = g_markup_collect_attributes (
78 element_name,
79 attribute_names,
80 attribute_values,
81 error,
82
83 G_MARKUP_COLLECT_STRING,
84 "column",
85 &index_str,
86
87 G_MARKUP_COLLECT_BOOLEAN |
88 G_MARKUP_COLLECT_OPTIONAL,
89 "ascending",
90 &ascending,
91
92 G_MARKUP_COLLECT_INVALID);
93
94 if (success) {
95 ETableColumnSpecification *column_spec;
96 ColumnData column_data;
97 gint64 index;
98
99 g_return_if_fail (index_str != NULL);
100 index = g_ascii_strtoll (index_str, NULL, 10);
101
102 g_return_if_fail (index < columns->len);
103 column_spec = g_ptr_array_index (columns, index);
104
105 column_data.column_spec = g_object_ref (column_spec);
106
107 if (ascending)
108 column_data.sort_type = GTK_SORT_ASCENDING;
109 else
110 column_data.sort_type = GTK_SORT_DESCENDING;
111
112 g_array_append_val (sort_info->priv->groupings, column_data);
113 }
114 }
115
116 static void
table_sort_info_parser_start_leaf(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,ETableSortInfo * sort_info,GPtrArray * columns,GError ** error)117 table_sort_info_parser_start_leaf (GMarkupParseContext *context,
118 const gchar *element_name,
119 const gchar **attribute_names,
120 const gchar **attribute_values,
121 ETableSortInfo *sort_info,
122 GPtrArray *columns,
123 GError **error)
124 {
125 const gchar *index_str;
126 gboolean ascending;
127 gboolean success;
128
129 success = g_markup_collect_attributes (
130 element_name,
131 attribute_names,
132 attribute_values,
133 error,
134
135 G_MARKUP_COLLECT_STRING,
136 "column",
137 &index_str,
138
139 G_MARKUP_COLLECT_BOOLEAN |
140 G_MARKUP_COLLECT_OPTIONAL,
141 "ascending",
142 &ascending,
143
144 G_MARKUP_COLLECT_INVALID);
145
146 if (success) {
147 ETableColumnSpecification *column_spec;
148 ColumnData column_data;
149 gint64 index;
150
151 g_return_if_fail (index_str != NULL);
152 index = g_ascii_strtoll (index_str, NULL, 10);
153
154 g_return_if_fail (index < columns->len);
155 column_spec = g_ptr_array_index (columns, index);
156
157 column_data.column_spec = g_object_ref (column_spec);
158
159 if (ascending)
160 column_data.sort_type = GTK_SORT_ASCENDING;
161 else
162 column_data.sort_type = GTK_SORT_DESCENDING;
163
164 g_array_append_val (sort_info->priv->sortings, column_data);
165 }
166 }
167
168 static void
table_sort_info_parser_start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)169 table_sort_info_parser_start_element (GMarkupParseContext *context,
170 const gchar *element_name,
171 const gchar **attribute_names,
172 const gchar **attribute_values,
173 gpointer user_data,
174 GError **error)
175 {
176 ETableSpecification *specification;
177 ETableSortInfo *sort_info;
178 GPtrArray *columns;
179
180 sort_info = E_TABLE_SORT_INFO (user_data);
181 specification = e_table_sort_info_ref_specification (sort_info);
182 columns = e_table_specification_ref_columns (specification);
183
184 if (g_str_equal (element_name, "group"))
185 table_sort_info_parser_start_group (
186 context,
187 element_name,
188 attribute_names,
189 attribute_values,
190 sort_info,
191 columns,
192 error);
193
194 if (g_str_equal (element_name, "leaf"))
195 table_sort_info_parser_start_leaf (
196 context,
197 element_name,
198 attribute_names,
199 attribute_values,
200 sort_info,
201 columns,
202 error);
203
204 g_object_unref (specification);
205 g_ptr_array_unref (columns);
206 }
207
208 static void
table_sort_info_parser_error(GMarkupParseContext * context,GError * error,gpointer user_data)209 table_sort_info_parser_error (GMarkupParseContext *context,
210 GError *error,
211 gpointer user_data)
212 {
213 g_object_unref (E_TABLE_SORT_INFO (user_data));
214 }
215
216 static const GMarkupParser table_sort_info_parser = {
217 table_sort_info_parser_start_element,
218 NULL,
219 NULL,
220 NULL,
221 table_sort_info_parser_error
222 };
223
224 static void
table_sort_info_set_specification(ETableSortInfo * sort_info,ETableSpecification * specification)225 table_sort_info_set_specification (ETableSortInfo *sort_info,
226 ETableSpecification *specification)
227 {
228 g_return_if_fail (E_IS_TABLE_SPECIFICATION (specification));
229
230 g_weak_ref_set (&sort_info->priv->specification, specification);
231 }
232
233 static void
table_sort_info_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)234 table_sort_info_set_property (GObject *object,
235 guint property_id,
236 const GValue *value,
237 GParamSpec *pspec)
238 {
239 switch (property_id) {
240 case PROP_SPECIFICATION:
241 table_sort_info_set_specification (
242 E_TABLE_SORT_INFO (object),
243 g_value_get_object (value));
244 return;
245 }
246
247 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
248 }
249
250 static void
table_sort_info_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)251 table_sort_info_get_property (GObject *object,
252 guint property_id,
253 GValue *value,
254 GParamSpec *pspec)
255 {
256 switch (property_id) {
257 case PROP_SPECIFICATION:
258 g_value_take_object (
259 value,
260 e_table_sort_info_ref_specification (
261 E_TABLE_SORT_INFO (object)));
262 return;
263 }
264
265 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
266 }
267
268 static void
table_sort_info_dispose(GObject * object)269 table_sort_info_dispose (GObject *object)
270 {
271 ETableSortInfoPrivate *priv;
272
273 priv = E_TABLE_SORT_INFO_GET_PRIVATE (object);
274
275 g_weak_ref_set (&priv->specification, NULL);
276
277 g_array_set_size (priv->groupings, 0);
278 g_array_set_size (priv->sortings, 0);
279
280 /* Chain up to parent's dispose() method. */
281 G_OBJECT_CLASS (e_table_sort_info_parent_class)->dispose (object);
282 }
283
284 static void
table_sort_info_finalize(GObject * object)285 table_sort_info_finalize (GObject *object)
286 {
287 ETableSortInfoPrivate *priv;
288
289 priv = E_TABLE_SORT_INFO_GET_PRIVATE (object);
290
291 g_array_free (priv->groupings, TRUE);
292 g_array_free (priv->sortings, TRUE);
293
294 /* Chain up to parent's finalize() method. */
295 G_OBJECT_CLASS (e_table_sort_info_parent_class)->finalize (object);
296 }
297
298 static void
e_table_sort_info_class_init(ETableSortInfoClass * class)299 e_table_sort_info_class_init (ETableSortInfoClass *class)
300 {
301 GObjectClass * object_class;
302
303 g_type_class_add_private (class, sizeof (ETableSortInfoPrivate));
304
305 object_class = G_OBJECT_CLASS (class);
306 object_class->set_property = table_sort_info_set_property;
307 object_class->get_property = table_sort_info_get_property;
308 object_class->dispose = table_sort_info_dispose;
309 object_class->finalize = table_sort_info_finalize;
310
311 g_object_class_install_property (
312 object_class,
313 PROP_SPECIFICATION,
314 g_param_spec_object (
315 "specification",
316 "Table Specification",
317 "Specification for the table state",
318 E_TYPE_TABLE_SPECIFICATION,
319 G_PARAM_READWRITE |
320 G_PARAM_CONSTRUCT_ONLY |
321 G_PARAM_STATIC_STRINGS));
322
323 signals[SORT_INFO_CHANGED] = g_signal_new (
324 "sort_info_changed",
325 G_TYPE_FROM_CLASS (object_class),
326 G_SIGNAL_RUN_LAST,
327 G_STRUCT_OFFSET (ETableSortInfoClass, sort_info_changed),
328 (GSignalAccumulator) NULL, NULL,
329 g_cclosure_marshal_VOID__VOID,
330 G_TYPE_NONE, 0);
331
332 signals[GROUP_INFO_CHANGED] = g_signal_new (
333 "group_info_changed",
334 G_TYPE_FROM_CLASS (object_class),
335 G_SIGNAL_RUN_LAST,
336 G_STRUCT_OFFSET (ETableSortInfoClass, group_info_changed),
337 (GSignalAccumulator) NULL, NULL,
338 g_cclosure_marshal_VOID__VOID,
339 G_TYPE_NONE, 0);
340 }
341
342 static void
e_table_sort_info_init(ETableSortInfo * sort_info)343 e_table_sort_info_init (ETableSortInfo *sort_info)
344 {
345 sort_info->priv = E_TABLE_SORT_INFO_GET_PRIVATE (sort_info);
346
347 sort_info->priv->groupings = g_array_new (
348 FALSE, TRUE, sizeof (ColumnData));
349 g_array_set_clear_func (
350 sort_info->priv->groupings,
351 (GDestroyNotify) column_data_clear);
352
353 sort_info->priv->sortings = g_array_new (
354 FALSE, TRUE, sizeof (ColumnData));
355 g_array_set_clear_func (
356 sort_info->priv->sortings,
357 (GDestroyNotify) column_data_clear);
358
359 sort_info->priv->can_group = TRUE;
360 }
361
362 /**
363 * e_table_sort_info_new:
364 * @specification: an #ETableSpecification
365 *
366 * This creates a new #ETableSortInfo object that contains no
367 * grouping and no sorting defined as of yet. This object is used
368 * to keep track of multi-level sorting and multi-level grouping of
369 * an #ETable.
370 *
371 * Returns: A new #ETableSortInfo object
372 */
373 ETableSortInfo *
e_table_sort_info_new(ETableSpecification * specification)374 e_table_sort_info_new (ETableSpecification *specification)
375 {
376 g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), NULL);
377
378 return g_object_new (
379 E_TYPE_TABLE_SORT_INFO,
380 "specification", specification, NULL);
381 }
382
383 /**
384 * e_table_sort_info_parse_context_push:
385 * @context: a #GMarkupParseContext
386 * @specification: an #ETableSpecification
387 *
388 * Creates a new #ETableSortInfo from a segment of XML data being fed to
389 * @context. Call this function for the appropriate opening tag from the
390 * <structfield>start_element</structfield> callback of a #GMarkupParser,
391 * then call e_table_sort_info_parse_context_pop() for the corresponding
392 * closing tag from the <structfield>end_element</structfield> callback.
393 **/
394 void
e_table_sort_info_parse_context_push(GMarkupParseContext * context,ETableSpecification * specification)395 e_table_sort_info_parse_context_push (GMarkupParseContext *context,
396 ETableSpecification *specification)
397 {
398 g_return_if_fail (context != NULL);
399 g_return_if_fail (E_IS_TABLE_SPECIFICATION (specification));
400
401 g_markup_parse_context_push (
402 context, &table_sort_info_parser,
403 e_table_sort_info_new (specification));
404 }
405
406 /**
407 * e_table_sort_info_parse_context_pop:
408 * @context: a #GMarkupParseContext
409 *
410 * Creates a new #ETableSortInfo from a segment of XML data being fed to
411 * @context. Call e_table_sort_info_parse_context_push() for the appropriate
412 * opening tag from the <structfield>start_element</structfield> callback of a
413 * #GMarkupParser, then call this function for the corresponding closing tag
414 * from the <structfield>end_element</structfield> callback.
415 *
416 * Unreference the newly-created #ETableSortInfo with g_object_unref() when
417 * finished with it.
418 *
419 * Returns: an #ETableSortInfo
420 **/
421 ETableSortInfo *
e_table_sort_info_parse_context_pop(GMarkupParseContext * context)422 e_table_sort_info_parse_context_pop (GMarkupParseContext *context)
423 {
424 gpointer user_data;
425
426 g_return_val_if_fail (context != NULL, NULL);
427
428 user_data = g_markup_parse_context_pop (context);
429
430 return E_TABLE_SORT_INFO (user_data);
431 }
432
433 /**
434 * e_table_sort_info_ref_specification:
435 * @sort_info: an #ETableSortInfo
436 *
437 * Returns the #ETableSpecification passed to e_table_sort_info_new().
438 *
439 * The returned #ETableSpecification is referenced for thread-safety and must
440 * be unreferenced with g_object_unref() when finished with it.
441 *
442 * Returns: an #ETableSpecification
443 **/
444 ETableSpecification *
e_table_sort_info_ref_specification(ETableSortInfo * sort_info)445 e_table_sort_info_ref_specification (ETableSortInfo *sort_info)
446 {
447 g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), NULL);
448
449 return g_weak_ref_get (&sort_info->priv->specification);
450 }
451
452 gboolean
e_table_sort_info_get_can_group(ETableSortInfo * sort_info)453 e_table_sort_info_get_can_group (ETableSortInfo *sort_info)
454 {
455 g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), FALSE);
456
457 return sort_info->priv->can_group;
458 }
459
460 void
e_table_sort_info_set_can_group(ETableSortInfo * sort_info,gboolean can_group)461 e_table_sort_info_set_can_group (ETableSortInfo *sort_info,
462 gboolean can_group)
463 {
464 g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
465
466 sort_info->priv->can_group = can_group;
467 }
468
469 /**
470 * e_table_sort_info_grouping_get_count:
471 * @sort_info: an #ETableSortInfo
472 *
473 * Returns: the number of grouping criteria in the object.
474 */
475 guint
e_table_sort_info_grouping_get_count(ETableSortInfo * sort_info)476 e_table_sort_info_grouping_get_count (ETableSortInfo *sort_info)
477 {
478 guint count = 0;
479
480 g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), 0);
481
482 if (e_table_sort_info_get_can_group (sort_info))
483 count = sort_info->priv->groupings->len;
484
485 return count;
486 }
487
488 /**
489 * e_table_sort_info_grouping_truncate:
490 * @sort_info: an #ETableSortInfo
491 * @length: position where the truncation happens.
492 *
493 * This routine can be used to reduce or grow the number of grouping
494 * criteria in the object.
495 */
496 void
e_table_sort_info_grouping_truncate(ETableSortInfo * sort_info,guint length)497 e_table_sort_info_grouping_truncate (ETableSortInfo *sort_info,
498 guint length)
499 {
500 g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
501
502 g_array_set_size (sort_info->priv->groupings, length);
503
504 g_signal_emit (sort_info, signals[GROUP_INFO_CHANGED], 0);
505 }
506
507 /**
508 * e_table_sort_info_grouping_get_nth:
509 * @sort_info: an #ETableSortInfo
510 * @n: Item information to fetch.
511 * @out_sort_type: return location for a #GtkSortType value, or %NULL
512 *
513 * Returns: the description of the @n-th grouping criteria in the @info object.
514 */
515 ETableColumnSpecification *
e_table_sort_info_grouping_get_nth(ETableSortInfo * sort_info,guint n,GtkSortType * out_sort_type)516 e_table_sort_info_grouping_get_nth (ETableSortInfo *sort_info,
517 guint n,
518 GtkSortType *out_sort_type)
519 {
520 ETableColumnSpecification *column_spec = NULL;
521 GArray *array;
522 gboolean can_group;
523
524 g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), NULL);
525
526 array = sort_info->priv->groupings;
527 can_group = e_table_sort_info_get_can_group (sort_info);
528
529 if (can_group && n < array->len) {
530 ColumnData *column_data;
531
532 column_data = &g_array_index (array, ColumnData, n);
533
534 if (out_sort_type != NULL)
535 *out_sort_type = column_data->sort_type;
536
537 column_spec = column_data->column_spec;
538 }
539
540 return column_spec;
541 }
542
543 /**
544 * e_table_sort_info_grouping_set_nth:
545 * @sort_info: an #ETableSortInfo
546 * @n: Item information to fetch.
547 * @spec: an #ETableColumnSpecification
548 * @sort_type: a #GtkSortType
549 *
550 * Sets the grouping criteria for index @n to @spec and @sort_type.
551 */
552 void
e_table_sort_info_grouping_set_nth(ETableSortInfo * sort_info,guint n,ETableColumnSpecification * spec,GtkSortType sort_type)553 e_table_sort_info_grouping_set_nth (ETableSortInfo *sort_info,
554 guint n,
555 ETableColumnSpecification *spec,
556 GtkSortType sort_type)
557 {
558 GArray *array;
559 ColumnData *column_data;
560
561 g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
562 g_return_if_fail (E_IS_TABLE_COLUMN_SPECIFICATION (spec));
563
564 array = sort_info->priv->groupings;
565 g_array_set_size (array, MAX (n + 1, array->len));
566 column_data = &g_array_index (array, ColumnData, n);
567
568 /* In case it's setting the same specification, to not free it */
569 g_object_ref (spec);
570
571 column_data_clear (column_data);
572
573 column_data->column_spec = spec;
574 column_data->sort_type = sort_type;
575
576 g_signal_emit (sort_info, signals[GROUP_INFO_CHANGED], 0);
577 }
578
579 /**
580 * e_table_sort_info_get_count:
581 * @sort_info: an #ETableSortInfo
582 *
583 * Returns: the number of sorting criteria in the object.
584 */
585 guint
e_table_sort_info_sorting_get_count(ETableSortInfo * sort_info)586 e_table_sort_info_sorting_get_count (ETableSortInfo *sort_info)
587 {
588 g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), 0);
589
590 return sort_info->priv->sortings->len;
591 }
592
593 /**
594 * e_table_sort_info_sorting_remove:
595 * @sort_info: an #ETableSortInfo
596 * @n: the index of the element to remove
597 *
598 * Removes the sorting element at the given index. The following sorting
599 * elements are moved down one place.
600 **/
601 void
e_table_sort_info_sorting_remove(ETableSortInfo * sort_info,guint n)602 e_table_sort_info_sorting_remove (ETableSortInfo *sort_info,
603 guint n)
604 {
605 g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
606
607 g_array_remove_index (sort_info->priv->sortings, n);
608
609 g_signal_emit (sort_info, signals[SORT_INFO_CHANGED], 0);
610 }
611
612 /**
613 * e_table_sort_info_sorting_truncate:
614 * @sort_info: an #ETableSortInfo
615 * @length: position where the truncation happens.
616 *
617 * This routine can be used to reduce or grow the number of sort
618 * criteria in the object.
619 */
620 void
e_table_sort_info_sorting_truncate(ETableSortInfo * sort_info,guint length)621 e_table_sort_info_sorting_truncate (ETableSortInfo *sort_info,
622 guint length)
623 {
624 g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
625
626 g_array_set_size (sort_info->priv->sortings, length);
627
628 g_signal_emit (sort_info, signals[SORT_INFO_CHANGED], 0);
629 }
630
631 /**
632 * e_table_sort_info_sorting_get_nth:
633 * @sort_info: an #ETableSortInfo
634 * @n: Item information to fetch.
635 * @out_sort_type: return location for a #GtkSortType value, or %NULL
636 *
637 * Returns: the description of the @n-th grouping criteria in the @info object.
638 */
639 ETableColumnSpecification *
e_table_sort_info_sorting_get_nth(ETableSortInfo * sort_info,guint n,GtkSortType * out_sort_type)640 e_table_sort_info_sorting_get_nth (ETableSortInfo *sort_info,
641 guint n,
642 GtkSortType *out_sort_type)
643 {
644 ETableColumnSpecification *column_spec = NULL;
645 GArray *array;
646
647 g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), NULL);
648
649 array = sort_info->priv->sortings;
650
651 if (n < array->len) {
652 ColumnData *column_data;
653
654 column_data = &g_array_index (array, ColumnData, n);
655
656 if (out_sort_type != NULL)
657 *out_sort_type = column_data->sort_type;
658
659 column_spec = column_data->column_spec;
660 }
661
662 return column_spec;
663 }
664
665 /**
666 * e_table_sort_info_sorting_set_nth:
667 * @sort_info: an #ETableSortInfo
668 * @n: Item information to fetch.
669 * @spec: an #ETableColumnSpecification
670 * @sort_type: a #GtkSortType
671 *
672 * Sets the sorting criteria for index @n to @spec and @sort_type.
673 */
674 void
e_table_sort_info_sorting_set_nth(ETableSortInfo * sort_info,guint n,ETableColumnSpecification * spec,GtkSortType sort_type)675 e_table_sort_info_sorting_set_nth (ETableSortInfo *sort_info,
676 guint n,
677 ETableColumnSpecification *spec,
678 GtkSortType sort_type)
679 {
680 GArray *array;
681 ColumnData *column_data;
682
683 g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
684 g_return_if_fail (E_IS_TABLE_COLUMN_SPECIFICATION (spec));
685
686 array = sort_info->priv->sortings;
687 g_array_set_size (array, MAX (n + 1, array->len));
688 column_data = &g_array_index (array, ColumnData, n);
689
690 /* In case it's setting the same specification, to not free it */
691 g_object_ref (spec);
692
693 column_data_clear (column_data);
694
695 column_data->column_spec = spec;
696 column_data->sort_type = sort_type;
697
698 g_signal_emit (sort_info, signals[SORT_INFO_CHANGED], 0);
699 }
700
701 /**
702 * @sort_info: an #ETableSortInfo
703 * @n: Index to insert to.
704 * @spec: an #ETableColumnSpecification
705 * @sort_type: a #GtkSortType
706 *
707 * Inserts the sorting criteria for index @n to @spec and @sort_type.
708 *
709 * Since: 3.12
710 **/
711 void
e_table_sort_info_sorting_insert(ETableSortInfo * sort_info,guint n,ETableColumnSpecification * spec,GtkSortType sort_type)712 e_table_sort_info_sorting_insert (ETableSortInfo *sort_info,
713 guint n,
714 ETableColumnSpecification *spec,
715 GtkSortType sort_type)
716 {
717 GArray *array;
718 ColumnData *column_data, tmp;
719
720 g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
721 g_return_if_fail (E_IS_TABLE_COLUMN_SPECIFICATION (spec));
722
723 array = sort_info->priv->sortings;
724 if (array->len == 0) {
725 e_table_sort_info_sorting_set_nth (sort_info, 0, spec, sort_type);
726 return;
727 }
728
729 if ((gint) n == -1)
730 n = 0;
731 else if (n > array->len)
732 n = array->len;
733
734 tmp.column_spec = NULL;
735 tmp.sort_type = sort_type;
736 column_data = &tmp;
737
738 if (n == array->len)
739 g_array_append_val (array, column_data);
740 else
741 g_array_insert_val (array, n, column_data);
742
743 column_data = &g_array_index (array, ColumnData, n);
744 column_data->column_spec = g_object_ref (spec);
745 column_data->sort_type = sort_type;
746
747 g_signal_emit (sort_info, signals[SORT_INFO_CHANGED], 0);
748 }
749
750 /**
751 * e_table_sort_info_load_from_node:
752 * @sort_info: an #ETableSortInfo
753 * @node: pointer to the xmlNode that describes the sorting and grouping information
754 * @state_version:
755 *
756 * This loads the state for the #ETableSortInfo object @info from the
757 * xml node @node.
758 */
759 void
e_table_sort_info_load_from_node(ETableSortInfo * sort_info,xmlNode * node,gdouble state_version)760 e_table_sort_info_load_from_node (ETableSortInfo *sort_info,
761 xmlNode *node,
762 gdouble state_version)
763 {
764 ETableSpecification *specification;
765 GPtrArray *columns;
766 xmlNode *grouping;
767 guint gcnt = 0;
768 guint scnt = 0;
769
770 g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
771 g_return_if_fail (node != NULL);
772
773 specification = e_table_sort_info_ref_specification (sort_info);
774 columns = e_table_specification_ref_columns (specification);
775
776 for (grouping = node->children; grouping; grouping = grouping->next) {
777
778 if (grouping->type != XML_ELEMENT_NODE)
779 continue;
780
781 if (g_str_equal ((gchar *) grouping->name, "group")) {
782 GtkSortType sort_type;
783 gboolean ascending;
784 guint index;
785
786 index = e_xml_get_integer_prop_by_name (
787 grouping, (guchar *) "column");
788 ascending = e_xml_get_bool_prop_by_name (
789 grouping, (guchar *) "ascending");
790
791 if (ascending)
792 sort_type = GTK_SORT_ASCENDING;
793 else
794 sort_type = GTK_SORT_DESCENDING;
795
796 if (index < columns->len)
797 e_table_sort_info_grouping_set_nth (
798 sort_info, gcnt++,
799 columns->pdata[index],
800 sort_type);
801 }
802
803 if (g_str_equal ((gchar *) grouping->name, "leaf")) {
804 GtkSortType sort_type;
805 gboolean ascending;
806 gint index;;
807
808 index = e_xml_get_integer_prop_by_name (
809 grouping, (guchar *) "column");
810 ascending = e_xml_get_bool_prop_by_name (
811 grouping, (guchar *) "ascending");
812
813 if (ascending)
814 sort_type = GTK_SORT_ASCENDING;
815 else
816 sort_type = GTK_SORT_DESCENDING;
817
818 if (index < columns->len)
819 e_table_sort_info_sorting_set_nth (
820 sort_info, scnt++,
821 columns->pdata[index],
822 sort_type);
823 }
824 }
825
826 g_object_unref (specification);
827 g_ptr_array_unref (columns);
828
829 g_signal_emit (sort_info, signals[SORT_INFO_CHANGED], 0);
830 }
831
832 /**
833 * e_table_sort_info_save_to_node:
834 * @sort_info: an #ETableSortInfo
835 * @parent: xmlNode that will be hosting the saved state of the @info object.
836 *
837 * This function is used
838 *
839 * Returns: the node that has been appended to @parent as a child containing
840 * the sorting and grouping information for this ETableSortInfo object.
841 */
842 xmlNode *
e_table_sort_info_save_to_node(ETableSortInfo * sort_info,xmlNode * parent)843 e_table_sort_info_save_to_node (ETableSortInfo *sort_info,
844 xmlNode *parent)
845 {
846 ETableSpecification *specification;
847 xmlNode *grouping;
848 guint sort_count;
849 guint group_count;
850 guint ii;
851
852 g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), NULL);
853
854 sort_count = e_table_sort_info_sorting_get_count (sort_info);
855 group_count = e_table_sort_info_grouping_get_count (sort_info);
856
857 grouping = xmlNewChild (parent, NULL, (guchar *) "grouping", NULL);
858
859 specification = e_table_sort_info_ref_specification (sort_info);
860
861 for (ii = 0; ii < group_count; ii++) {
862 ETableColumnSpecification *column_spec;
863 GtkSortType sort_type = GTK_SORT_ASCENDING;
864 xmlNode *new_node;
865 gint index;
866
867 column_spec = e_table_sort_info_grouping_get_nth (
868 sort_info, ii, &sort_type);
869
870 index = e_table_specification_get_column_index (
871 specification, column_spec);
872
873 if (index < 0) {
874 g_warn_if_reached ();
875 continue;
876 }
877
878 new_node = xmlNewChild (
879 grouping, NULL, (guchar *) "group", NULL);
880
881 e_xml_set_integer_prop_by_name (
882 new_node, (guchar *) "column", index);
883 e_xml_set_bool_prop_by_name (
884 new_node, (guchar *) "ascending",
885 (sort_type == GTK_SORT_ASCENDING));
886 }
887
888 for (ii = 0; ii < sort_count; ii++) {
889 ETableColumnSpecification *column_spec;
890 GtkSortType sort_type = GTK_SORT_ASCENDING;
891 xmlNode *new_node;
892 gint index;
893
894 column_spec = e_table_sort_info_sorting_get_nth (
895 sort_info, ii, &sort_type);
896
897 index = e_table_specification_get_column_index (
898 specification, column_spec);
899
900 if (index < 0) {
901 g_warn_if_reached ();
902 continue;
903 }
904
905 new_node = xmlNewChild (
906 grouping, NULL, (guchar *) "leaf", NULL);
907
908 e_xml_set_integer_prop_by_name (
909 new_node, (guchar *) "column", index);
910 e_xml_set_bool_prop_by_name (
911 new_node, (guchar *) "ascending",
912 (sort_type == GTK_SORT_ASCENDING));
913 }
914
915 g_object_unref (specification);
916
917 return grouping;
918 }
919
920 ETableSortInfo *
e_table_sort_info_duplicate(ETableSortInfo * sort_info)921 e_table_sort_info_duplicate (ETableSortInfo *sort_info)
922 {
923 ETableSpecification *specification;
924 ETableSortInfo *new_info;
925 gint ii;
926
927 g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), NULL);
928
929 specification = e_table_sort_info_ref_specification (sort_info);
930 new_info = e_table_sort_info_new (specification);
931 g_object_unref (specification);
932
933 g_array_set_size (
934 new_info->priv->groupings,
935 sort_info->priv->groupings->len);
936 if (new_info->priv->groupings->data &&
937 sort_info->priv->groupings->data &&
938 sort_info->priv->groupings->len) {
939 memmove (
940 new_info->priv->groupings->data,
941 sort_info->priv->groupings->data,
942 sort_info->priv->groupings->len *
943 g_array_get_element_size (sort_info->priv->groupings));
944 }
945
946 for (ii = 0; ii < new_info->priv->groupings->len; ii++) {
947 ColumnData *column_data;
948
949 column_data = &g_array_index (new_info->priv->groupings, ColumnData, ii);
950
951 g_object_ref (column_data->column_spec);
952 }
953
954 g_array_set_size (
955 new_info->priv->sortings,
956 sort_info->priv->sortings->len);
957 if (new_info->priv->sortings->data &&
958 sort_info->priv->sortings->data &&
959 sort_info->priv->sortings->len) {
960 memmove (
961 new_info->priv->sortings->data,
962 sort_info->priv->sortings->data,
963 sort_info->priv->sortings->len *
964 g_array_get_element_size (sort_info->priv->sortings));
965 }
966
967 for (ii = 0; ii < new_info->priv->sortings->len; ii++) {
968 ColumnData *column_data;
969
970 column_data = &g_array_index (new_info->priv->sortings, ColumnData, ii);
971
972 g_object_ref (column_data->column_spec);
973 }
974
975 new_info->priv->can_group = sort_info->priv->can_group;
976
977 return new_info;
978 }
979
980