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