1 /*
2  * This program is free software; you can redistribute it and/or modify it
3  * under the terms of the GNU Lesser General Public License as published by
4  * the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful, but
7  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
9  * for more details.
10  *
11  * You should have received a copy of the GNU Lesser General Public License
12  * along with this program; if not, see <http://www.gnu.org/licenses/>.
13  *
14  *
15  * Authors:
16  *		Chris Lahey <clahey@ximian.com>
17  *
18  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19  *
20  */
21 
22 #include "evolution-config.h"
23 
24 #include <gtk/gtk.h>
25 #include <libgnomecanvas/libgnomecanvas.h>
26 
27 #include "e-table-group.h"
28 #include "e-table-group-container.h"
29 #include "e-table-group-leaf.h"
30 #include "e-table-item.h"
31 
32 G_DEFINE_TYPE (
33 	ETableGroup,
34 	e_table_group,
35 	GNOME_TYPE_CANVAS_GROUP)
36 
37 #define ETG_CLASS(e) (E_TABLE_GROUP_CLASS(G_OBJECT_GET_CLASS(e)))
38 
39 enum {
40 	CURSOR_CHANGE,
41 	CURSOR_ACTIVATED,
42 	DOUBLE_CLICK,
43 	RIGHT_CLICK,
44 	CLICK,
45 	KEY_PRESS,
46 	START_DRAG,
47 	LAST_SIGNAL
48 };
49 
50 enum {
51 	PROP_0,
52 	PROP_IS_EDITING
53 };
54 
55 static guint etg_signals[LAST_SIGNAL] = { 0, };
56 
57 static gboolean etg_get_focus (ETableGroup *table_group);
58 
59 static void
etg_dispose(GObject * object)60 etg_dispose (GObject *object)
61 {
62 	ETableGroup *table_group = E_TABLE_GROUP (object);
63 
64 	g_clear_object (&table_group->header);
65 	g_clear_object (&table_group->full_header);
66 	g_clear_object (&table_group->model);
67 
68 	/* Chain up to parent's dispose() method. */
69 	G_OBJECT_CLASS (e_table_group_parent_class)->dispose (object);
70 }
71 
72 static void
etg_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)73 etg_get_property (GObject *object,
74                   guint property_id,
75                   GValue *value,
76                   GParamSpec *pspec)
77 {
78 	ETableGroup *etg = E_TABLE_GROUP (object);
79 
80 	switch (property_id) {
81 	case PROP_IS_EDITING:
82 		g_value_set_boolean (value, e_table_group_is_editing (etg));
83 		break;
84 	default:
85 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
86 		break;
87 	}
88 }
89 
90 /**
91  * e_table_group_new
92  * @parent: The %GnomeCanvasGroup to create a child of.
93  * @full_header: The full header of the #ETable.
94  * @header: The current header of the #ETable.
95  * @model: The #ETableModel of the #ETable.
96  * @sort_info: The #ETableSortInfo of the #ETable.
97  * @n: The grouping information object to group by.
98  *
99  * #ETableGroup is a collection of rows of an #ETable.  It's a
100  * %GnomeCanvasItem.  There are two different forms.  If n < the
101  * number of groupings in the given #ETableSortInfo, then the
102  * #ETableGroup will need to contain other #ETableGroups, thus it
103  * creates an #ETableGroupContainer.  Otherwise, it will just contain
104  * an #ETableItem, and thus it creates an #ETableGroupLeaf.
105  *
106  * Returns: The new #ETableGroup.
107  */
108 ETableGroup *
e_table_group_new(GnomeCanvasGroup * parent,ETableHeader * full_header,ETableHeader * header,ETableModel * model,ETableSortInfo * sort_info,gint n)109 e_table_group_new (GnomeCanvasGroup *parent,
110                    ETableHeader *full_header,
111                    ETableHeader *header,
112                    ETableModel *model,
113                    ETableSortInfo *sort_info,
114                    gint n)
115 {
116 	g_return_val_if_fail (model != NULL, NULL);
117 
118 	if (n < e_table_sort_info_grouping_get_count (sort_info)) {
119 		return e_table_group_container_new (
120 			parent, full_header, header, model, sort_info, n);
121 	} else {
122 		return e_table_group_leaf_new (
123 			parent, full_header, header, model, sort_info);
124 	}
125 }
126 
127 /**
128  * e_table_group_construct
129  * @parent: The %GnomeCanvasGroup to create a child of.
130  * @table_group: The #ETableGroup to construct.
131  * @full_header: The full header of the #ETable.
132  * @header: The current header of the #ETable.
133  * @model: The #ETableModel of the #ETable.
134  *
135  * This routine does the base construction of the #ETableGroup.
136  */
137 void
e_table_group_construct(GnomeCanvasGroup * parent,ETableGroup * table_group,ETableHeader * full_header,ETableHeader * header,ETableModel * model)138 e_table_group_construct (GnomeCanvasGroup *parent,
139                          ETableGroup *table_group,
140                          ETableHeader *full_header,
141                          ETableHeader *header,
142                          ETableModel *model)
143 {
144 	table_group->full_header = g_object_ref (full_header);
145 	table_group->header = g_object_ref (header);
146 	table_group->model = g_object_ref (model);
147 	g_object_set (table_group, "parent", parent, NULL);
148 }
149 
150 /**
151  * e_table_group_add
152  * @table_group: The #ETableGroup to add a row to
153  * @row: The row to add.
154  *
155  * This routine adds the given row from the #ETableModel to this set
156  * of rows.
157  */
158 void
e_table_group_add(ETableGroup * table_group,gint row)159 e_table_group_add (ETableGroup *table_group,
160                    gint row)
161 {
162 	g_return_if_fail (E_IS_TABLE_GROUP (table_group));
163 
164 	g_return_if_fail (ETG_CLASS (table_group)->add != NULL);
165 	ETG_CLASS (table_group)->add (table_group, row);
166 }
167 
168 /**
169  * e_table_group_add_array
170  * @table_group: The #ETableGroup to add to
171  * @array: The array to add.
172  * @count: The number of times to add
173  *
174  * This routine adds all the rows in the array to this set of rows.
175  * It assumes that the array is already sorted properly.
176  */
177 void
e_table_group_add_array(ETableGroup * table_group,const gint * array,gint count)178 e_table_group_add_array (ETableGroup *table_group,
179                          const gint *array,
180                          gint count)
181 {
182 	g_return_if_fail (E_IS_TABLE_GROUP (table_group));
183 
184 	g_return_if_fail (ETG_CLASS (table_group)->add_array != NULL);
185 	ETG_CLASS (table_group)->add_array (table_group, array, count);
186 }
187 
188 /**
189  * e_table_group_add_all
190  * @table_group: The #ETableGroup to add to
191  *
192  * This routine adds all the rows from the #ETableModel to this set
193  * of rows.
194  */
195 void
e_table_group_add_all(ETableGroup * table_group)196 e_table_group_add_all (ETableGroup *table_group)
197 {
198 	g_return_if_fail (E_IS_TABLE_GROUP (table_group));
199 
200 	g_return_if_fail (ETG_CLASS (table_group)->add_all != NULL);
201 	ETG_CLASS (table_group)->add_all (table_group);
202 }
203 
204 /**
205  * e_table_group_remove
206  * @table_group: The #ETableGroup to remove a row from
207  * @row: The row to remove.
208  *
209  * This routine removes the given row from the #ETableModel from this
210  * set of rows.
211  *
212  * Returns: TRUE if the row was deleted and FALSE if the row was not
213  * found.
214  */
215 gboolean
e_table_group_remove(ETableGroup * table_group,gint row)216 e_table_group_remove (ETableGroup *table_group,
217                       gint row)
218 {
219 	g_return_val_if_fail (E_IS_TABLE_GROUP (table_group), FALSE);
220 
221 	g_return_val_if_fail (ETG_CLASS (table_group)->remove != NULL, FALSE);
222 	return ETG_CLASS (table_group)->remove (table_group, row);
223 }
224 
225 /**
226  * e_table_group_increment
227  * @table_group: The #ETableGroup to increment
228  * @position: The position to increment from
229  * @amount: The amount to increment.
230  *
231  * This routine adds amount to all rows greater than or equal to
232  * position.  This is to handle when a row gets inserted into the
233  * model.
234  */
235 void
e_table_group_increment(ETableGroup * table_group,gint position,gint amount)236 e_table_group_increment (ETableGroup *table_group,
237                          gint position,
238                          gint amount)
239 {
240 	g_return_if_fail (E_IS_TABLE_GROUP (table_group));
241 
242 	g_return_if_fail (ETG_CLASS (table_group)->increment != NULL);
243 	ETG_CLASS (table_group)->increment (table_group, position, amount);
244 }
245 
246 /**
247  * e_table_group_increment
248  * @table_group: The #ETableGroup to decrement
249  * @position: The position to decrement from
250  * @amount: The amount to decrement
251  *
252  * This routine removes amount from all rows greater than or equal to
253  * position.  This is to handle when a row gets deleted from the
254  * model.
255  */
256 void
e_table_group_decrement(ETableGroup * table_group,gint position,gint amount)257 e_table_group_decrement (ETableGroup *table_group,
258                          gint position,
259                          gint amount)
260 {
261 	g_return_if_fail (E_IS_TABLE_GROUP (table_group));
262 
263 	g_return_if_fail (ETG_CLASS (table_group)->decrement != NULL);
264 	ETG_CLASS (table_group)->decrement (table_group, position, amount);
265 }
266 
267 /**
268  * e_table_group_increment
269  * @table_group: The #ETableGroup to count
270  *
271  * This routine calculates the number of rows shown in this group.
272  *
273  * Returns: The number of rows.
274  */
275 gint
e_table_group_row_count(ETableGroup * table_group)276 e_table_group_row_count (ETableGroup *table_group)
277 {
278 	g_return_val_if_fail (E_IS_TABLE_GROUP (table_group), -1);
279 
280 	g_return_val_if_fail (ETG_CLASS (table_group)->row_count != NULL, -1);
281 	return ETG_CLASS (table_group)->row_count (table_group);
282 }
283 
284 /**
285  * e_table_group_set_focus
286  * @table_group: The #ETableGroup to set
287  * @direction: The direction the focus is coming from.
288  * @view_col: The column to set the focus in.
289  *
290  * Sets the focus to this widget.  Places the focus in the view column
291  * coming from direction direction.
292  */
293 void
e_table_group_set_focus(ETableGroup * table_group,EFocus direction,gint view_col)294 e_table_group_set_focus (ETableGroup *table_group,
295                          EFocus direction,
296                          gint view_col)
297 {
298 	g_return_if_fail (E_IS_TABLE_GROUP (table_group));
299 
300 	g_return_if_fail (ETG_CLASS (table_group)->set_focus != NULL);
301 	ETG_CLASS (table_group)->set_focus (table_group, direction, view_col);
302 }
303 
304 /**
305  * e_table_group_get_focus
306  * @table_group: The #ETableGroup to check
307  *
308  * Calculates if this group has the focus.
309  *
310  * Returns: TRUE if this group has the focus.
311  */
312 gboolean
e_table_group_get_focus(ETableGroup * table_group)313 e_table_group_get_focus (ETableGroup *table_group)
314 {
315 	g_return_val_if_fail (E_IS_TABLE_GROUP (table_group), FALSE);
316 
317 	g_return_val_if_fail (ETG_CLASS (table_group)->get_focus != NULL, FALSE);
318 	return ETG_CLASS (table_group)->get_focus (table_group);
319 }
320 
321 /**
322  * e_table_group_get_focus_column
323  * @table_group: The #ETableGroup to check
324  *
325  * Calculates which column in this group has the focus.
326  *
327  * Returns: The column index (view column).
328  */
329 gint
e_table_group_get_focus_column(ETableGroup * table_group)330 e_table_group_get_focus_column (ETableGroup *table_group)
331 {
332 	g_return_val_if_fail (E_IS_TABLE_GROUP (table_group), -1);
333 
334 	g_return_val_if_fail (ETG_CLASS (table_group)->get_focus_column != NULL, -1);
335 	return ETG_CLASS (table_group)->get_focus_column (table_group);
336 }
337 
338 /**
339  * e_table_group_get_printable
340  * @table_group: #ETableGroup which will be printed
341  *
342  * This routine creates and returns an %EPrintable that can be used to
343  * print the given #ETableGroup.
344  *
345  * Returns: The %EPrintable.
346  */
347 EPrintable *
e_table_group_get_printable(ETableGroup * table_group)348 e_table_group_get_printable (ETableGroup *table_group)
349 {
350 	g_return_val_if_fail (E_IS_TABLE_GROUP (table_group), NULL);
351 
352 	g_return_val_if_fail (ETG_CLASS (table_group)->get_printable != NULL, NULL);
353 	return ETG_CLASS (table_group)->get_printable (table_group);
354 }
355 
356 /**
357  * e_table_group_compute_location
358  * @table_group: #ETableGroup to look in.
359  * @x: A pointer to the x location to find in the #ETableGroup.
360  * @y: A pointer to the y location to find in the #ETableGroup.
361  * @row: A pointer to the location to store the found row in.
362  * @col: A pointer to the location to store the found col in.
363  *
364  * This routine locates the pixel location (*x, *y) in the
365  * #ETableGroup.  If that location is in the #ETableGroup, *row and
366  * *col are set to the view row and column where it was found.  If
367  * that location is not in the #ETableGroup, the height of the
368  * #ETableGroup is removed from the value y points to.
369  */
370 void
e_table_group_compute_location(ETableGroup * table_group,gint * x,gint * y,gint * row,gint * col)371 e_table_group_compute_location (ETableGroup *table_group,
372                                 gint *x,
373                                 gint *y,
374                                 gint *row,
375                                 gint *col)
376 {
377 	g_return_if_fail (E_IS_TABLE_GROUP (table_group));
378 
379 	g_return_if_fail (ETG_CLASS (table_group)->compute_location != NULL);
380 	ETG_CLASS (table_group)->compute_location (table_group, x, y, row, col);
381 }
382 
383 void
e_table_group_get_mouse_over(ETableGroup * table_group,gint * row,gint * col)384 e_table_group_get_mouse_over (ETableGroup *table_group,
385                               gint *row,
386                               gint *col)
387 {
388 	g_return_if_fail (E_IS_TABLE_GROUP (table_group));
389 
390 	g_return_if_fail (ETG_CLASS (table_group)->get_mouse_over != NULL);
391 	ETG_CLASS (table_group)->get_mouse_over (table_group, row, col);
392 }
393 
394 /**
395  * e_table_group_get_position
396  * @table_group: #ETableGroup to look in.
397  * @x: A pointer to the location to store the found x location in.
398  * @y: A pointer to the location to store the found y location in.
399  * @row: A pointer to the row number to find.
400  * @col: A pointer to the col number to find.
401  *
402  * This routine finds the view cell (row, col) in the #ETableGroup.
403  * If that location is in the #ETableGroup *@x and *@y are set to the
404  * upper left hand corner of the cell found.  If that location is not
405  * in the #ETableGroup, the number of rows in the #ETableGroup is
406  * removed from the value row points to.
407  */
408 void
e_table_group_get_cell_geometry(ETableGroup * table_group,gint * row,gint * col,gint * x,gint * y,gint * width,gint * height)409 e_table_group_get_cell_geometry (ETableGroup *table_group,
410                                  gint *row,
411                                  gint *col,
412                                  gint *x,
413                                  gint *y,
414                                  gint *width,
415                                  gint *height)
416 {
417 	g_return_if_fail (E_IS_TABLE_GROUP (table_group));
418 
419 	g_return_if_fail (ETG_CLASS (table_group)->get_cell_geometry != NULL);
420 	ETG_CLASS (table_group)->get_cell_geometry (table_group, row, col, x, y, width, height);
421 }
422 
423 /**
424  * e_table_group_cursor_change
425  * @table_group: #ETableGroup to emit the signal on
426  * @row: The new cursor row (model row)
427  *
428  * This routine emits the "cursor_change" signal.
429  */
430 void
e_table_group_cursor_change(ETableGroup * e_table_group,gint row)431 e_table_group_cursor_change (ETableGroup *e_table_group,
432                              gint row)
433 {
434 	g_return_if_fail (e_table_group != NULL);
435 	g_return_if_fail (E_IS_TABLE_GROUP (e_table_group));
436 
437 	g_signal_emit (
438 		e_table_group,
439 		etg_signals[CURSOR_CHANGE], 0,
440 		row);
441 }
442 
443 /**
444  * e_table_group_cursor_activated
445  * @table_group: #ETableGroup to emit the signal on
446  * @row: The cursor row (model row)
447  *
448  * This routine emits the "cursor_activated" signal.
449  */
450 void
e_table_group_cursor_activated(ETableGroup * e_table_group,gint row)451 e_table_group_cursor_activated (ETableGroup *e_table_group,
452                                 gint row)
453 {
454 	g_return_if_fail (e_table_group != NULL);
455 	g_return_if_fail (E_IS_TABLE_GROUP (e_table_group));
456 
457 	g_signal_emit (
458 		e_table_group,
459 		etg_signals[CURSOR_ACTIVATED], 0,
460 		row);
461 }
462 
463 /**
464  * e_table_group_double_click
465  * @table_group: #ETableGroup to emit the signal on
466  * @row: The row clicked on (model row)
467  * @col: The col clicked on (model col)
468  * @event: The event that caused this signal
469  *
470  * This routine emits the "double_click" signal.
471  */
472 void
e_table_group_double_click(ETableGroup * e_table_group,gint row,gint col,GdkEvent * event)473 e_table_group_double_click (ETableGroup *e_table_group,
474                             gint row,
475                             gint col,
476                             GdkEvent *event)
477 {
478 	g_return_if_fail (e_table_group != NULL);
479 	g_return_if_fail (E_IS_TABLE_GROUP (e_table_group));
480 
481 	g_signal_emit (
482 		e_table_group,
483 		etg_signals[DOUBLE_CLICK], 0,
484 		row, col, event);
485 }
486 
487 /**
488  * e_table_group_right_click
489  * @table_group: #ETableGroup to emit the signal on
490  * @row: The row clicked on (model row)
491  * @col: The col clicked on (model col)
492  * @event: The event that caused this signal
493  *
494  * This routine emits the "right_click" signal.
495  */
496 gboolean
e_table_group_right_click(ETableGroup * e_table_group,gint row,gint col,GdkEvent * event)497 e_table_group_right_click (ETableGroup *e_table_group,
498                            gint row,
499                            gint col,
500                            GdkEvent *event)
501 {
502 	gboolean return_val = FALSE;
503 
504 	g_return_val_if_fail (e_table_group != NULL, FALSE);
505 	g_return_val_if_fail (E_IS_TABLE_GROUP (e_table_group), FALSE);
506 
507 	g_signal_emit (
508 		e_table_group,
509 		etg_signals[RIGHT_CLICK], 0,
510 		row, col, event, &return_val);
511 
512 	return return_val;
513 }
514 
515 /**
516  * e_table_group_click
517  * @table_group: #ETableGroup to emit the signal on
518  * @row: The row clicked on (model row)
519  * @col: The col clicked on (model col)
520  * @event: The event that caused this signal
521  *
522  * This routine emits the "click" signal.
523  */
524 gboolean
e_table_group_click(ETableGroup * table_group,gint row,gint col,GdkEvent * event)525 e_table_group_click (ETableGroup *table_group,
526                      gint row,
527                      gint col,
528                      GdkEvent *event)
529 {
530 	gboolean return_val = FALSE;
531 
532 	g_return_val_if_fail (E_IS_TABLE_GROUP (table_group), FALSE);
533 
534 	g_signal_emit (
535 		table_group,
536 		etg_signals[CLICK], 0,
537 		row, col, event, &return_val);
538 
539 	return return_val;
540 }
541 
542 /**
543  * e_table_group_key_press
544  * @table_group: #ETableGroup to emit the signal on
545  * @row: The cursor row (model row)
546  * @col: The cursor col (model col)
547  * @event: The event that caused this signal
548  *
549  * This routine emits the "key_press" signal.
550  */
551 gboolean
e_table_group_key_press(ETableGroup * e_table_group,gint row,gint col,GdkEvent * event)552 e_table_group_key_press (ETableGroup *e_table_group,
553                          gint row,
554                          gint col,
555                          GdkEvent *event)
556 {
557 	gboolean return_val = FALSE;
558 
559 	g_return_val_if_fail (e_table_group != NULL, FALSE);
560 	g_return_val_if_fail (E_IS_TABLE_GROUP (e_table_group), FALSE);
561 
562 	g_signal_emit (
563 		e_table_group,
564 		etg_signals[KEY_PRESS], 0,
565 		row, col, event, &return_val);
566 
567 	return return_val;
568 }
569 
570 /**
571  * e_table_group_start_drag
572  * @table_group: #ETableGroup to emit the signal on
573  * @row: The cursor row (model row)
574  * @col: The cursor col (model col)
575  * @event: The event that caused this signal
576  *
577  * This routine emits the "start_drag" signal.
578  */
579 gboolean
e_table_group_start_drag(ETableGroup * e_table_group,gint row,gint col,GdkEvent * event)580 e_table_group_start_drag (ETableGroup *e_table_group,
581                           gint row,
582                           gint col,
583                           GdkEvent *event)
584 {
585 	gboolean return_val = FALSE;
586 
587 	g_return_val_if_fail (e_table_group != NULL, FALSE);
588 	g_return_val_if_fail (E_IS_TABLE_GROUP (e_table_group), FALSE);
589 
590 	g_signal_emit (
591 		e_table_group,
592 		etg_signals[START_DRAG], 0,
593 		row, col, event, &return_val);
594 
595 	return return_val;
596 }
597 
598 /**
599  * e_table_group_get_header
600  * @table_group: #ETableGroup to check
601  *
602  * This routine returns the #ETableGroup's header.
603  *
604  * Returns: The #ETableHeader.
605  */
606 ETableHeader *
e_table_group_get_header(ETableGroup * table_group)607 e_table_group_get_header (ETableGroup *table_group)
608 {
609 	g_return_val_if_fail (E_IS_TABLE_GROUP (table_group), NULL);
610 
611 	return table_group->header;
612 }
613 
614 static gint
etg_event(GnomeCanvasItem * item,GdkEvent * event)615 etg_event (GnomeCanvasItem *item,
616            GdkEvent *event)
617 {
618 	ETableGroup *table_group = E_TABLE_GROUP (item);
619 	gboolean return_val = TRUE;
620 
621 	switch (event->type) {
622 
623 	case GDK_FOCUS_CHANGE:
624 		table_group->has_focus = event->focus_change.in;
625 		return_val = FALSE;
626 		break;
627 
628 	default:
629 		return_val = FALSE;
630 	}
631 	if (return_val == FALSE) {
632 		if (GNOME_CANVAS_ITEM_CLASS (e_table_group_parent_class)->event)
633 			return GNOME_CANVAS_ITEM_CLASS (e_table_group_parent_class)->event (item, event);
634 	}
635 	return return_val;
636 
637 }
638 
639 static gboolean
etg_get_focus(ETableGroup * table_group)640 etg_get_focus (ETableGroup *table_group)
641 {
642 	return table_group->has_focus;
643 }
644 
645 static void
e_table_group_class_init(ETableGroupClass * class)646 e_table_group_class_init (ETableGroupClass *class)
647 {
648 	GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS (class);
649 	GObjectClass *object_class = G_OBJECT_CLASS (class);
650 
651 	object_class->dispose = etg_dispose;
652 	object_class->get_property = etg_get_property;
653 
654 	item_class->event = etg_event;
655 
656 	class->cursor_change = NULL;
657 	class->cursor_activated = NULL;
658 	class->double_click = NULL;
659 	class->right_click = NULL;
660 	class->click = NULL;
661 	class->key_press = NULL;
662 	class->start_drag = NULL;
663 
664 	class->add = NULL;
665 	class->add_array = NULL;
666 	class->add_all = NULL;
667 	class->remove = NULL;
668 	class->row_count = NULL;
669 	class->increment = NULL;
670 	class->decrement = NULL;
671 	class->set_focus = NULL;
672 	class->get_focus = etg_get_focus;
673 	class->get_printable = NULL;
674 	class->compute_location = NULL;
675 	class->get_mouse_over = NULL;
676 	class->get_cell_geometry = NULL;
677 
678 	g_object_class_install_property (
679 		object_class,
680 		PROP_IS_EDITING,
681 		g_param_spec_boolean (
682 			"is-editing",
683 			"Whether is in an editing mode",
684 			"Whether is in an editing mode",
685 			FALSE,
686 			G_PARAM_READABLE));
687 
688 	etg_signals[CURSOR_CHANGE] = g_signal_new (
689 		"cursor_change",
690 		G_OBJECT_CLASS_TYPE (object_class),
691 		G_SIGNAL_RUN_LAST,
692 		G_STRUCT_OFFSET (ETableGroupClass, cursor_change),
693 		NULL, NULL,
694 		g_cclosure_marshal_VOID__INT,
695 		G_TYPE_NONE, 1,
696 		G_TYPE_INT);
697 
698 	etg_signals[CURSOR_ACTIVATED] = g_signal_new (
699 		"cursor_activated",
700 		G_OBJECT_CLASS_TYPE (object_class),
701 		G_SIGNAL_RUN_LAST,
702 		G_STRUCT_OFFSET (ETableGroupClass, cursor_activated),
703 		NULL, NULL,
704 		g_cclosure_marshal_VOID__INT,
705 		G_TYPE_NONE, 1,
706 		G_TYPE_INT);
707 
708 	etg_signals[DOUBLE_CLICK] = g_signal_new (
709 		"double_click",
710 		G_OBJECT_CLASS_TYPE (object_class),
711 		G_SIGNAL_RUN_LAST,
712 		G_STRUCT_OFFSET (ETableGroupClass, double_click),
713 		NULL, NULL,
714 		e_marshal_VOID__INT_INT_BOXED,
715 		G_TYPE_NONE, 3,
716 		G_TYPE_INT,
717 		G_TYPE_INT,
718 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
719 
720 	etg_signals[RIGHT_CLICK] = g_signal_new (
721 		"right_click",
722 		G_OBJECT_CLASS_TYPE (object_class),
723 		G_SIGNAL_RUN_LAST,
724 		G_STRUCT_OFFSET (ETableGroupClass, right_click),
725 		g_signal_accumulator_true_handled, NULL,
726 		e_marshal_BOOLEAN__INT_INT_BOXED,
727 		G_TYPE_BOOLEAN, 3,
728 		G_TYPE_INT,
729 		G_TYPE_INT,
730 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
731 
732 	etg_signals[CLICK] = g_signal_new (
733 		"click",
734 		G_OBJECT_CLASS_TYPE (object_class),
735 		G_SIGNAL_RUN_LAST,
736 		G_STRUCT_OFFSET (ETableGroupClass, click),
737 		g_signal_accumulator_true_handled, NULL,
738 		e_marshal_BOOLEAN__INT_INT_BOXED,
739 		G_TYPE_BOOLEAN, 3,
740 		G_TYPE_INT,
741 		G_TYPE_INT,
742 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
743 
744 	etg_signals[KEY_PRESS] = g_signal_new (
745 		"key_press",
746 		G_OBJECT_CLASS_TYPE (object_class),
747 		G_SIGNAL_RUN_LAST,
748 		G_STRUCT_OFFSET (ETableGroupClass, key_press),
749 		g_signal_accumulator_true_handled, NULL,
750 		e_marshal_BOOLEAN__INT_INT_BOXED,
751 		G_TYPE_BOOLEAN, 3,
752 		G_TYPE_INT,
753 		G_TYPE_INT,
754 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
755 
756 	etg_signals[START_DRAG] = g_signal_new (
757 		"start_drag",
758 		G_OBJECT_CLASS_TYPE (object_class),
759 		G_SIGNAL_RUN_LAST,
760 		G_STRUCT_OFFSET (ETableGroupClass, start_drag),
761 		g_signal_accumulator_true_handled, NULL,
762 		e_marshal_BOOLEAN__INT_INT_BOXED,
763 		G_TYPE_BOOLEAN, 3,
764 		G_TYPE_INT,
765 		G_TYPE_INT,
766 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
767 }
768 
769 static void
e_table_group_init(ETableGroup * table_group)770 e_table_group_init (ETableGroup *table_group)
771 {
772 	/* nothing to do */
773 }
774 
775 gboolean
e_table_group_is_editing(ETableGroup * table_group)776 e_table_group_is_editing (ETableGroup *table_group)
777 {
778 	static gboolean in = FALSE;
779 	gboolean is_editing = FALSE;
780 
781 	g_return_val_if_fail (E_IS_TABLE_GROUP (table_group), FALSE);
782 
783 	/* this should be called from the main thread only,
784 	 * and each descendant overrides the property,
785 	 * thus might cause no call recursion */
786 	if (in) {
787 		g_warn_if_reached ();
788 		return FALSE;
789 	}
790 
791 	in = TRUE;
792 
793 	g_object_get (G_OBJECT (table_group), "is-editing", &is_editing, NULL);
794 
795 	in = FALSE;
796 
797 	return is_editing;
798 }
799