evolution-3.6.4/widgets/table/e-tree-selection-model.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-tree-selection-model.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-tree-selection-model.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /*
  2  * This program is free software; you can redistribute it and/or
  3  * modify it under the terms of the GNU Lesser General Public
  4  * License as published by the Free Software Foundation; either
  5  * version 2 of the License, or (at your option) version 3.
  6  *
  7  * This program is distributed in the hope that it will be useful,
  8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 10  * Lesser General Public License for more details.
 11  *
 12  * You should have received a copy of the GNU Lesser General Public
 13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 14  *
 15  *
 16  * Authors:
 17  *		Chris Lahey <clahey@ximian.com>
 18  *		Mike Kestner <mkestner@ximian.com>
 19  *
 20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 21  *
 22  */
 23 
 24 #ifdef HAVE_CONFIG_H
 25 #include <config.h>
 26 #endif
 27 
 28 #include "table/e-tree-table-adapter.h"
 29 #include <glib/gi18n.h>
 30 #include "e-util/e-util.h"
 31 
 32 #include "e-tree-selection-model.h"
 33 
 34 #define E_TREE_SELECTION_MODEL_GET_PRIVATE(obj) \
 35 	(G_TYPE_INSTANCE_GET_PRIVATE \
 36 	((obj), E_TYPE_TREE_SELECTION_MODEL, ETreeSelectionModelPrivate))
 37 
 38 G_DEFINE_TYPE (
 39 	ETreeSelectionModel, e_tree_selection_model, E_TYPE_SELECTION_MODEL)
 40 
 41 enum {
 42 	PROP_0,
 43 	PROP_CURSOR_ROW,
 44 	PROP_CURSOR_COL,
 45 	PROP_MODEL,
 46 	PROP_ETTA
 47 };
 48 
 49 struct _ETreeSelectionModelPrivate {
 50 	ETreeTableAdapter *etta;
 51 	ETreeModel *model;
 52 
 53 	GHashTable *paths;
 54 	ETreePath cursor_path;
 55 	ETreePath start_path;
 56 	gint cursor_col;
 57 	gchar *cursor_save_id;
 58 
 59 	gint tree_model_pre_change_id;
 60 	gint tree_model_no_change_id;
 61 	gint tree_model_node_changed_id;
 62 	gint tree_model_node_data_changed_id;
 63 	gint tree_model_node_col_changed_id;
 64 	gint tree_model_node_inserted_id;
 65 	gint tree_model_node_removed_id;
 66 	gint tree_model_node_deleted_id;
 67 };
 68 
 69 static gint
 70 get_cursor_row (ETreeSelectionModel *etsm)
 71 {
 72 	if (etsm->priv->cursor_path)
 73 		return e_tree_table_adapter_row_of_node (
 74 			etsm->priv->etta, etsm->priv->cursor_path);
 75 
 76 	return -1;
 77 }
 78 
 79 static void
 80 clear_selection (ETreeSelectionModel *etsm)
 81 {
 82 	g_hash_table_destroy (etsm->priv->paths);
 83 	etsm->priv->paths = g_hash_table_new (NULL, NULL);
 84 }
 85 
 86 static void
 87 change_one_path (ETreeSelectionModel *etsm,
 88                  ETreePath path,
 89                  gboolean grow)
 90 {
 91 	if (!path)
 92 		return;
 93 
 94 	if (grow)
 95 		g_hash_table_insert (etsm->priv->paths, path, path);
 96 	else if (g_hash_table_lookup (etsm->priv->paths, path))
 97 		g_hash_table_remove (etsm->priv->paths, path);
 98 }
 99 
100 static void
101 select_single_path (ETreeSelectionModel *etsm,
102                     ETreePath path)
103 {
104 	clear_selection (etsm);
105 	change_one_path (etsm, path, TRUE);
106 	etsm->priv->cursor_path = path;
107 	etsm->priv->start_path = NULL;
108 }
109 
110 static void
111 select_range (ETreeSelectionModel *etsm,
112               gint start,
113               gint end)
114 {
115 	gint i;
116 
117 	if (start > end) {
118 		i = start;
119 		start = end;
120 		end = i;
121 	}
122 
123 	for (i = start; i <= end; i++) {
124 		ETreePath path = e_tree_table_adapter_node_at_row (etsm->priv->etta, i);
125 		if (path)
126 			g_hash_table_insert (etsm->priv->paths, path, path);
127 	}
128 }
129 
130 static void
131 free_id (ETreeSelectionModel *etsm)
132 {
133 	g_free (etsm->priv->cursor_save_id);
134 	etsm->priv->cursor_save_id = NULL;
135 }
136 
137 static void
138 restore_cursor (ETreeSelectionModel *etsm,
139                 ETreeModel *etm)
140 {
141 	clear_selection (etsm);
142 	etsm->priv->cursor_path = NULL;
143 
144 	if (etsm->priv->cursor_save_id) {
145 		etsm->priv->cursor_path = e_tree_model_get_node_by_id (
146 			etm, etsm->priv->cursor_save_id);
147 		if (etsm->priv->cursor_path != NULL && etsm->priv->cursor_col == -1)
148 			etsm->priv->cursor_col = 0;
149 
150 		select_single_path (etsm, etsm->priv->cursor_path);
151 	}
152 
153 	e_selection_model_selection_changed (E_SELECTION_MODEL (etsm));
154 
155 	if (etsm->priv->cursor_path) {
156 		gint cursor_row = get_cursor_row (etsm);
157 		e_selection_model_cursor_changed (
158 			E_SELECTION_MODEL (etsm),
159 			cursor_row, etsm->priv->cursor_col);
160 	} else {
161 		e_selection_model_cursor_changed (
162 			E_SELECTION_MODEL (etsm), -1, -1);
163 		e_selection_model_cursor_activated (
164 			E_SELECTION_MODEL (etsm), -1, -1);
165 
166 	}
167 
168 	free_id (etsm);
169 }
170 
171 static void
172 etsm_pre_change (ETreeModel *etm,
173                  ETreeSelectionModel *etsm)
174 {
175 	g_free (etsm->priv->cursor_save_id);
176 	etsm->priv->cursor_save_id = NULL;
177 
178 	if (e_tree_model_has_get_node_by_id (etm) &&
179 	    e_tree_model_has_save_id (etm) &&
180 	    etsm->priv->cursor_path) {
181 		etsm->priv->cursor_save_id = e_tree_model_get_save_id (
182 			etm, etsm->priv->cursor_path);
183 	}
184 }
185 
186 static void
187 etsm_no_change (ETreeModel *etm,
188                 ETreeSelectionModel *etsm)
189 {
190 	free_id (etsm);
191 }
192 
193 static void
194 etsm_node_changed (ETreeModel *etm,
195                    ETreePath node,
196                    ETreeSelectionModel *etsm)
197 {
198 	restore_cursor (etsm, etm);
199 }
200 
201 static void
202 etsm_node_data_changed (ETreeModel *etm,
203                         ETreePath node,
204                         ETreeSelectionModel *etsm)
205 {
206 	free_id (etsm);
207 }
208 
209 static void
210 etsm_node_col_changed (ETreeModel *etm,
211                        ETreePath node,
212                        gint col,
213                        ETreeSelectionModel *etsm)
214 {
215 	free_id (etsm);
216 }
217 
218 static void
219 etsm_node_inserted (ETreeModel *etm,
220                     ETreePath parent,
221                     ETreePath child,
222                     ETreeSelectionModel *etsm)
223 {
224 	restore_cursor (etsm, etm);
225 }
226 
227 static void
228 etsm_node_removed (ETreeModel *etm,
229                    ETreePath parent,
230                    ETreePath child,
231                    gint old_position,
232                    ETreeSelectionModel *etsm)
233 {
234 	restore_cursor (etsm, etm);
235 }
236 
237 static void
238 etsm_node_deleted (ETreeModel *etm,
239                    ETreePath child,
240                    ETreeSelectionModel *etsm)
241 {
242 	restore_cursor (etsm, etm);
243 }
244 
245 static void
246 add_model (ETreeSelectionModel *etsm,
247            ETreeModel *model)
248 {
249 	ETreeSelectionModelPrivate *priv = etsm->priv;
250 
251 	priv->model = model;
252 
253 	if (!priv->model)
254 		return;
255 
256 	g_object_ref (priv->model);
257 
258 	priv->tree_model_pre_change_id = g_signal_connect_after (
259 		priv->model, "pre_change",
260 		G_CALLBACK (etsm_pre_change), etsm);
261 
262 	priv->tree_model_no_change_id = g_signal_connect_after (
263 		priv->model, "no_change",
264 		G_CALLBACK (etsm_no_change), etsm);
265 
266 	priv->tree_model_node_changed_id = g_signal_connect_after (
267 		priv->model, "node_changed",
268 		G_CALLBACK (etsm_node_changed), etsm);
269 
270 	priv->tree_model_node_data_changed_id = g_signal_connect_after (
271 		priv->model, "node_data_changed",
272 		G_CALLBACK (etsm_node_data_changed), etsm);
273 
274 	priv->tree_model_node_col_changed_id = g_signal_connect_after (
275 		priv->model, "node_col_changed",
276 		G_CALLBACK (etsm_node_col_changed), etsm);
277 
278 	priv->tree_model_node_inserted_id = g_signal_connect_after (
279 		priv->model, "node_inserted",
280 		G_CALLBACK (etsm_node_inserted), etsm);
281 
282 	priv->tree_model_node_removed_id = g_signal_connect_after (
283 		priv->model, "node_removed",
284 		G_CALLBACK (etsm_node_removed), etsm);
285 
286 	priv->tree_model_node_deleted_id = g_signal_connect_after (
287 		priv->model, "node_deleted",
288 		G_CALLBACK (etsm_node_deleted), etsm);
289 }
290 
291 static void
292 drop_model (ETreeSelectionModel *etsm)
293 {
294 	ETreeSelectionModelPrivate *priv = etsm->priv;
295 
296 	if (!priv->model)
297 		return;
298 
299 	g_signal_handler_disconnect (
300 		priv->model, priv->tree_model_pre_change_id);
301 	g_signal_handler_disconnect (
302 		priv->model, priv->tree_model_no_change_id);
303 	g_signal_handler_disconnect (
304 		priv->model, priv->tree_model_node_changed_id);
305 	g_signal_handler_disconnect (
306 		priv->model, priv->tree_model_node_data_changed_id);
307 	g_signal_handler_disconnect (
308 		priv->model, priv->tree_model_node_col_changed_id);
309 	g_signal_handler_disconnect (
310 		priv->model, priv->tree_model_node_inserted_id);
311 	g_signal_handler_disconnect (
312 		priv->model, priv->tree_model_node_removed_id);
313 	g_signal_handler_disconnect (
314 		priv->model, priv->tree_model_node_deleted_id);
315 
316 	g_object_unref (priv->model);
317 	priv->model = NULL;
318 
319 	priv->tree_model_pre_change_id = 0;
320 	priv->tree_model_no_change_id = 0;
321 	priv->tree_model_node_changed_id = 0;
322 	priv->tree_model_node_data_changed_id = 0;
323 	priv->tree_model_node_col_changed_id = 0;
324 	priv->tree_model_node_inserted_id = 0;
325 	priv->tree_model_node_removed_id = 0;
326 	priv->tree_model_node_deleted_id = 0;
327 }
328 
329 static void
330 etsm_dispose (GObject *object)
331 {
332 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (object);
333 
334 	drop_model (etsm);
335 
336 	/* Chain up to parent's dispose() method. */
337 	G_OBJECT_CLASS (e_tree_selection_model_parent_class)->dispose (object);
338 }
339 
340 static void
341 etsm_finalize (GObject *object)
342 {
343 	ETreeSelectionModelPrivate *priv;
344 
345 	priv = E_TREE_SELECTION_MODEL_GET_PRIVATE (object);
346 
347 	clear_selection (E_TREE_SELECTION_MODEL (object));
348 	g_hash_table_destroy (priv->paths);
349 
350 	/* Chain up to parent's finalize() method. */
351 	G_OBJECT_CLASS (e_tree_selection_model_parent_class)->finalize (object);
352 }
353 
354 static void
355 etsm_get_property (GObject *object,
356                    guint property_id,
357                    GValue *value,
358                    GParamSpec *pspec)
359 {
360 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (object);
361 
362 	switch (property_id) {
363 	case PROP_CURSOR_ROW:
364 		g_value_set_int (value, get_cursor_row (etsm));
365 		break;
366 
367 	case PROP_CURSOR_COL:
368 		g_value_set_int (value, etsm->priv->cursor_col);
369 		break;
370 
371 	case PROP_MODEL:
372 		g_value_set_object (value, etsm->priv->model);
373 		break;
374 
375 	case PROP_ETTA:
376 		g_value_set_object (value, etsm->priv->etta);
377 		break;
378 	}
379 }
380 
381 static void
382 etsm_set_property (GObject *object,
383                    guint property_id,
384                    const GValue *value,
385                    GParamSpec *pspec)
386 {
387 	ESelectionModel *esm = E_SELECTION_MODEL (object);
388 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (object);
389 
390 	switch (property_id) {
391 	case PROP_CURSOR_ROW:
392 		e_selection_model_do_something (
393 			esm, g_value_get_int (value),
394 			etsm->priv->cursor_col, 0);
395 		break;
396 
397 	case PROP_CURSOR_COL:
398 		e_selection_model_do_something (
399 			esm, get_cursor_row (etsm),
400 			g_value_get_int (value), 0);
401 		break;
402 
403 	case PROP_MODEL:
404 		drop_model (etsm);
405 		add_model (etsm, E_TREE_MODEL (g_value_get_object (value)));
406 		break;
407 
408 	case PROP_ETTA:
409 		etsm->priv->etta =
410 			E_TREE_TABLE_ADAPTER (g_value_get_object (value));
411 		break;
412 	}
413 }
414 
415 static gboolean
416 etsm_is_path_selected (ETreeSelectionModel *etsm,
417                        ETreePath path)
418 {
419 	if (path && g_hash_table_lookup (etsm->priv->paths, path))
420 		return TRUE;
421 
422 	return FALSE;
423 }
424 
425 /**
426  * e_selection_model_is_row_selected
427  * @selection: #ESelectionModel to check
428  * @n: The row to check
429  *
430  * This routine calculates whether the given row is selected.
431  *
432  * Returns: %TRUE if the given row is selected
433  */
434 static gboolean
435 etsm_is_row_selected (ESelectionModel *selection,
436                       gint row)
437 {
438 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
439 	ETreePath path;
440 
441 	g_return_val_if_fail (
442 		row < e_table_model_row_count (
443 		E_TABLE_MODEL (etsm->priv->etta)), FALSE);
444 	g_return_val_if_fail (row >= 0, FALSE);
445 	g_return_val_if_fail (etsm != NULL, FALSE);
446 
447 	path = e_tree_table_adapter_node_at_row (etsm->priv->etta, row);
448 	return etsm_is_path_selected (etsm, path);
449 }
450 
451 typedef struct {
452 	ETreeSelectionModel *etsm;
453 	EForeachFunc callback;
454 	gpointer closure;
455 } ModelAndCallback;
456 
457 static void
458 etsm_row_foreach_cb (gpointer key,
459                      gpointer value,
460                      gpointer user_data)
461 {
462 	ETreePath path = key;
463 	ModelAndCallback *mac = user_data;
464 	gint row = e_tree_table_adapter_row_of_node (
465 		mac->etsm->priv->etta, path);
466 	if (row >= 0)
467 		mac->callback (row, mac->closure);
468 }
469 
470 /**
471  * e_selection_model_foreach
472  * @selection: #ESelectionModel to traverse
473  * @callback: The callback function to call back.
474  * @closure: The closure
475  *
476  * This routine calls the given callback function once for each
477  * selected row, passing closure as the closure.
478  */
479 static void
480 etsm_foreach (ESelectionModel *selection,
481               EForeachFunc callback,
482               gpointer closure)
483 {
484 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
485 	ModelAndCallback mac;
486 
487 	mac.etsm = etsm;
488 	mac.callback = callback;
489 	mac.closure = closure;
490 
491 	g_hash_table_foreach (etsm->priv->paths, etsm_row_foreach_cb, &mac);
492 }
493 
494 /**
495  * e_selection_model_clear
496  * @selection: #ESelectionModel to clear
497  *
498  * This routine clears the selection to no rows selected.
499  */
500 static void
501 etsm_clear (ESelectionModel *selection)
502 {
503 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
504 
505 	clear_selection (etsm);
506 
507 	etsm->priv->cursor_path = NULL;
508 	e_selection_model_selection_changed (E_SELECTION_MODEL (etsm));
509 	e_selection_model_cursor_changed (E_SELECTION_MODEL (etsm), -1, -1);
510 }
511 
512 /**
513  * e_selection_model_selected_count
514  * @selection: #ESelectionModel to count
515  *
516  * This routine calculates the number of rows selected.
517  *
518  * Returns: The number of rows selected in the given model.
519  */
520 static gint
521 etsm_selected_count (ESelectionModel *selection)
522 {
523 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
524 
525 	return g_hash_table_size (etsm->priv->paths);
526 }
527 
528 static gint
529 etsm_row_count (ESelectionModel *selection)
530 {
531 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
532 	return e_table_model_row_count (E_TABLE_MODEL (etsm->priv->etta));
533 }
534 
535 /**
536  * e_selection_model_select_all
537  * @selection: #ESelectionModel to select all
538  *
539  * This routine selects all the rows in the given
540  * #ESelectionModel.
541  */
542 static void
543 etsm_select_all (ESelectionModel *selection)
544 {
545 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
546 	ETreePath root;
547 
548 	root = e_tree_model_get_root (etsm->priv->model);
549 	if (root == NULL)
550 		return;
551 
552 	clear_selection (etsm);
553 	select_range (etsm, 0, etsm_row_count (selection) - 1);
554 
555 	if (etsm->priv->cursor_path == NULL)
556 		etsm->priv->cursor_path = e_tree_table_adapter_node_at_row (
557 			etsm->priv->etta, 0);
558 
559 	e_selection_model_selection_changed (E_SELECTION_MODEL (etsm));
560 
561 	e_selection_model_cursor_changed (
562 		E_SELECTION_MODEL (etsm),
563 		get_cursor_row (etsm), etsm->priv->cursor_col);
564 }
565 
566 /**
567  * e_selection_model_invert_selection
568  * @selection: #ESelectionModel to invert
569  *
570  * This routine inverts all the rows in the given
571  * #ESelectionModel.
572  */
573 static void
574 etsm_invert_selection (ESelectionModel *selection)
575 {
576 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
577 	gint count = etsm_row_count (selection);
578 	gint i;
579 
580 	for (i = 0; i < count; i++) {
581 		ETreePath path;
582 
583 		path = e_tree_table_adapter_node_at_row (etsm->priv->etta, i);
584 		if (!path)
585 			continue;
586 		if (g_hash_table_lookup (etsm->priv->paths, path))
587 			g_hash_table_remove (etsm->priv->paths, path);
588 		else
589 			g_hash_table_insert (etsm->priv->paths, path, path);
590 	}
591 
592 	etsm->priv->cursor_col = -1;
593 	etsm->priv->cursor_path = NULL;
594 	etsm->priv->start_path = NULL;
595 	e_selection_model_selection_changed (E_SELECTION_MODEL (etsm));
596 	e_selection_model_cursor_changed (E_SELECTION_MODEL (etsm), -1, -1);
597 }
598 
599 static void
600 etsm_change_one_row (ESelectionModel *selection,
601                      gint row,
602                      gboolean grow)
603 {
604 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
605 	ETreePath path;
606 
607 	g_return_if_fail (
608 		row < e_table_model_row_count (
609 		E_TABLE_MODEL (etsm->priv->etta)));
610 	g_return_if_fail (row >= 0);
611 	g_return_if_fail (selection != NULL);
612 
613 	path = e_tree_table_adapter_node_at_row (etsm->priv->etta, row);
614 
615 	if (!path)
616 		return;
617 
618 	change_one_path (etsm, path, grow);
619 }
620 
621 static void
622 etsm_change_cursor (ESelectionModel *selection,
623                     gint row,
624                     gint col)
625 {
626 	ETreeSelectionModel *etsm;
627 
628 	g_return_if_fail (selection != NULL);
629 	g_return_if_fail (E_IS_SELECTION_MODEL (selection));
630 
631 	etsm = E_TREE_SELECTION_MODEL (selection);
632 
633 	if (row == -1) {
634 		etsm->priv->cursor_path = NULL;
635 	} else {
636 		etsm->priv->cursor_path =
637 			e_tree_table_adapter_node_at_row (
638 			etsm->priv->etta, row);
639 	}
640 	etsm->priv->cursor_col = col;
641 }
642 
643 static gint
644 etsm_cursor_row (ESelectionModel *selection)
645 {
646 	return get_cursor_row (E_TREE_SELECTION_MODEL (selection));
647 }
648 
649 static gint
650 etsm_cursor_col (ESelectionModel *selection)
651 {
652 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
653 	return etsm->priv->cursor_col;
654 }
655 
656 static void
657 etsm_get_rows (gint row,
658                gpointer d)
659 {
660 	gint **rowp = d;
661 
662 	**rowp = row;
663 	(*rowp)++;
664 }
665 
666 static void
667 etsm_select_single_row (ESelectionModel *selection,
668                         gint row)
669 {
670 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
671 	ETreePath path;
672 	gint rows[5], *rowp = NULL, size;
673 
674 	path = e_tree_table_adapter_node_at_row (etsm->priv->etta, row);
675 	g_return_if_fail (path != NULL);
676 
677 	/* we really only care about the size=1 case (cursor changed),
678 	 * but this doesn't cost much */
679 	size = g_hash_table_size (etsm->priv->paths);
680 	if (size > 0 && size <= 5) {
681 		rowp = rows;
682 		etsm_foreach (selection, etsm_get_rows, &rowp);
683 	}
684 
685 	select_single_path (etsm, path);
686 
687 	if (size > 5) {
688 		e_selection_model_selection_changed (E_SELECTION_MODEL (etsm));
689 	} else {
690 		if (rowp) {
691 			gint *p = rows;
692 
693 			while (p < rowp)
694 				e_selection_model_selection_row_changed (
695 					(ESelectionModel *) etsm, *p++);
696 		}
697 		e_selection_model_selection_row_changed (
698 			(ESelectionModel *) etsm, row);
699 	}
700 }
701 
702 static void
703 etsm_toggle_single_row (ESelectionModel *selection,
704                         gint row)
705 {
706 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
707 	ETreePath path;
708 
709 	path = e_tree_table_adapter_node_at_row (etsm->priv->etta, row);
710 	g_return_if_fail (path);
711 
712 	if (g_hash_table_lookup (etsm->priv->paths, path))
713 		g_hash_table_remove (etsm->priv->paths, path);
714 	else
715 		g_hash_table_insert (etsm->priv->paths, path, path);
716 
717 	etsm->priv->start_path = NULL;
718 
719 	e_selection_model_selection_row_changed ((ESelectionModel *) etsm, row);
720 }
721 
722 static void
723 etsm_real_move_selection_end (ETreeSelectionModel *etsm,
724                               gint row)
725 {
726 	ETreePath end_path;
727 	gint start;
728 
729 	end_path = e_tree_table_adapter_node_at_row (etsm->priv->etta, row);
730 	g_return_if_fail (end_path);
731 
732 	start = e_tree_table_adapter_row_of_node (
733 		etsm->priv->etta, etsm->priv->start_path);
734 	clear_selection (etsm);
735 	select_range (etsm, start, row);
736 }
737 
738 static void
739 etsm_move_selection_end (ESelectionModel *selection,
740                          gint row)
741 {
742 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
743 
744 	g_return_if_fail (etsm->priv->cursor_path);
745 
746 	etsm_real_move_selection_end (etsm, row);
747 	e_selection_model_selection_changed (E_SELECTION_MODEL (selection));
748 }
749 
750 static void
751 etsm_set_selection_end (ESelectionModel *selection,
752                         gint row)
753 {
754 	ETreeSelectionModel *etsm = E_TREE_SELECTION_MODEL (selection);
755 
756 	g_return_if_fail (etsm->priv->cursor_path);
757 
758 	if (!etsm->priv->start_path)
759 		etsm->priv->start_path = etsm->priv->cursor_path;
760 	etsm_real_move_selection_end (etsm, row);
761 	e_selection_model_selection_changed (E_SELECTION_MODEL (etsm));
762 }
763 
764 struct foreach_path_t {
765 	ETreeForeachFunc callback;
766 	gpointer closure;
767 };
768 
769 static void
770 foreach_path (gpointer key,
771               gpointer value,
772               gpointer data)
773 {
774 	ETreePath path = key;
775 	struct foreach_path_t *c = data;
776 	c->callback (path, c->closure);
777 }
778 
779 void
780 e_tree_selection_model_foreach (ETreeSelectionModel *etsm,
781                                 ETreeForeachFunc callback,
782                                 gpointer closure)
783 {
784 	if (etsm->priv->paths) {
785 		struct foreach_path_t c;
786 		c.callback = callback;
787 		c.closure = closure;
788 		g_hash_table_foreach (etsm->priv->paths, foreach_path, &c);
789 		return;
790 	}
791 }
792 
793 void
794 e_tree_selection_model_select_single_path (ETreeSelectionModel *etsm,
795                                            ETreePath path)
796 {
797 	select_single_path (etsm, path);
798 
799 	e_selection_model_selection_changed (E_SELECTION_MODEL (etsm));
800 }
801 
802 void
803 e_tree_selection_model_select_paths (ETreeSelectionModel *etsm,
804                                      GPtrArray *paths)
805 {
806 	ETreePath path;
807 	gint i;
808 
809 	for (i = 0; i < paths->len; i++) {
810 		path = paths->pdata[i];
811 		change_one_path (etsm, path, TRUE);
812 	}
813 
814 	e_selection_model_selection_changed (E_SELECTION_MODEL (etsm));
815 }
816 
817 void
818 e_tree_selection_model_add_to_selection (ETreeSelectionModel *etsm,
819                                          ETreePath path)
820 {
821 	change_one_path (etsm, path, TRUE);
822 
823 	e_selection_model_selection_changed (E_SELECTION_MODEL (etsm));
824 }
825 
826 void
827 e_tree_selection_model_change_cursor (ETreeSelectionModel *etsm,
828                                       ETreePath path)
829 {
830 	gint row;
831 
832 	etsm->priv->cursor_path = path;
833 
834 	row = get_cursor_row (etsm);
835 
836 	E_SELECTION_MODEL (etsm)->old_selection = -1;
837 
838 	e_selection_model_cursor_changed (
839 		E_SELECTION_MODEL (etsm), row, etsm->priv->cursor_col);
840 	e_selection_model_cursor_activated (
841 		E_SELECTION_MODEL (etsm), row, etsm->priv->cursor_col);
842 }
843 
844 ETreePath
845 e_tree_selection_model_get_cursor (ETreeSelectionModel *etsm)
846 {
847 	return etsm->priv->cursor_path;
848 }
849 
850 static void
851 e_tree_selection_model_init (ETreeSelectionModel *etsm)
852 {
853 	etsm->priv = E_TREE_SELECTION_MODEL_GET_PRIVATE (etsm);
854 
855 	etsm->priv->paths = g_hash_table_new (NULL, NULL);
856 	etsm->priv->cursor_col = -1;
857 }
858 
859 static void
860 e_tree_selection_model_class_init (ETreeSelectionModelClass *class)
861 {
862 	GObjectClass *object_class;
863 	ESelectionModelClass *esm_class;
864 
865 	g_type_class_add_private (class, sizeof (ETreeSelectionModelPrivate));
866 
867 	object_class = G_OBJECT_CLASS (class);
868 	object_class->dispose = etsm_dispose;
869 	object_class->finalize = etsm_finalize;
870 	object_class->get_property = etsm_get_property;
871 	object_class->set_property = etsm_set_property;
872 
873 	esm_class = E_SELECTION_MODEL_CLASS (class);
874 	esm_class->is_row_selected = etsm_is_row_selected;
875 	esm_class->foreach = etsm_foreach;
876 	esm_class->clear = etsm_clear;
877 	esm_class->selected_count = etsm_selected_count;
878 	esm_class->select_all = etsm_select_all;
879 	esm_class->invert_selection = etsm_invert_selection;
880 	esm_class->row_count = etsm_row_count;
881 
882 	esm_class->change_one_row = etsm_change_one_row;
883 	esm_class->change_cursor = etsm_change_cursor;
884 	esm_class->cursor_row = etsm_cursor_row;
885 	esm_class->cursor_col = etsm_cursor_col;
886 
887 	esm_class->select_single_row = etsm_select_single_row;
888 	esm_class->toggle_single_row = etsm_toggle_single_row;
889 	esm_class->move_selection_end = etsm_move_selection_end;
890 	esm_class->set_selection_end = etsm_set_selection_end;
891 
892 	g_object_class_install_property (
893 		object_class,
894 		PROP_CURSOR_ROW,
895 		g_param_spec_int (
896 			"cursor_row",
897 			"Cursor Row",
898 			NULL,
899 			0, G_MAXINT, 0,
900 			G_PARAM_READWRITE));
901 
902 	g_object_class_install_property (
903 		object_class,
904 		PROP_CURSOR_COL,
905 		g_param_spec_int (
906 			"cursor_col",
907 			"Cursor Column",
908 			NULL,
909 			0, G_MAXINT, 0,
910 			G_PARAM_READWRITE));
911 
912 	g_object_class_install_property (
913 		object_class,
914 		PROP_MODEL,
915 		g_param_spec_object (
916 			"model",
917 			"Model",
918 			NULL,
919 			E_TYPE_TREE_MODEL,
920 			G_PARAM_READWRITE));
921 
922 	g_object_class_install_property (
923 		object_class,
924 		PROP_ETTA,
925 		g_param_spec_object (
926 			"etta",
927 			"ETTA",
928 			NULL,
929 			E_TYPE_TREE_TABLE_ADAPTER,
930 			G_PARAM_READWRITE));
931 
932 }
933 
934 ESelectionModel *
935 e_tree_selection_model_new (void)
936 {
937 	return g_object_new (E_TYPE_TREE_SELECTION_MODEL, NULL);
938 }