evolution-3.6.4/widgets/table/e-table-click-to-add.c

No issues found

  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  *
 19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 20  *
 21  */
 22 
 23 #ifdef HAVE_CONFIG_H
 24 #include <config.h>
 25 #endif
 26 
 27 #include <gdk/gdkkeysyms.h>
 28 #include <gtk/gtk.h>
 29 #include <libgnomecanvas/libgnomecanvas.h>
 30 #include <gdk-pixbuf/gdk-pixbuf.h>
 31 
 32 #include "gal-a11y-e-table-click-to-add.h"
 33 #include "text/e-text.h"
 34 #include <glib/gi18n.h>
 35 #include "e-util/e-util.h"
 36 #include "misc/e-canvas-utils.h"
 37 #include "misc/e-canvas.h"
 38 
 39 #include "e-table-click-to-add.h"
 40 #include "e-table-defines.h"
 41 #include "e-table-header.h"
 42 #include "e-table-one.h"
 43 
 44 enum {
 45 	CURSOR_CHANGE,
 46 	STYLE_SET,
 47 	LAST_SIGNAL
 48 };
 49 
 50 static guint etcta_signals[LAST_SIGNAL] = { 0 };
 51 
 52 /* workaround for avoiding APi breakage */
 53 #define etcta_get_type e_table_click_to_add_get_type
 54 G_DEFINE_TYPE (ETableClickToAdd, etcta, GNOME_TYPE_CANVAS_GROUP)
 55 
 56 enum {
 57 	PROP_0,
 58 	PROP_HEADER,
 59 	PROP_MODEL,
 60 	PROP_MESSAGE,
 61 	PROP_WIDTH,
 62 	PROP_HEIGHT
 63 };
 64 
 65 static void
 66 etcta_cursor_change (GObject *object,
 67                      gint row,
 68                      gint col,
 69                      ETableClickToAdd *etcta)
 70 {
 71 	g_signal_emit (
 72 		etcta,
 73 		etcta_signals[CURSOR_CHANGE], 0,
 74 		row, col);
 75 }
 76 
 77 static void
 78 etcta_style_set (ETableClickToAdd *etcta,
 79                  GtkStyle *previous_style)
 80 {
 81 	GtkWidget *widget;
 82 	GtkStyle *style;
 83 
 84 	widget = GTK_WIDGET (GNOME_CANVAS_ITEM (etcta)->canvas);
 85 	style = gtk_widget_get_style (widget);
 86 
 87 	if (etcta->rect)
 88 		gnome_canvas_item_set (
 89 			etcta->rect,
 90 			"outline_color_gdk", &style->fg[GTK_STATE_NORMAL],
 91 			"fill_color_gdk", &style->bg[GTK_STATE_NORMAL],
 92 			NULL);
 93 
 94 	if (etcta->text)
 95 		gnome_canvas_item_set (
 96 			etcta->text,
 97 			"fill_color_gdk", &style->text[GTK_STATE_NORMAL],
 98 			NULL);
 99 }
100 
101 static void
102 etcta_add_table_header (ETableClickToAdd *etcta,
103                         ETableHeader *header)
104 {
105 	etcta->eth = header;
106 	if (etcta->eth)
107 		g_object_ref (etcta->eth);
108 	if (etcta->row)
109 		gnome_canvas_item_set (
110 			GNOME_CANVAS_ITEM (etcta->row),
111 			"ETableHeader", header,
112 			NULL);
113 }
114 
115 static void
116 etcta_drop_table_header (ETableClickToAdd *etcta)
117 {
118 	if (!etcta->eth)
119 		return;
120 
121 	g_object_unref (etcta->eth);
122 	etcta->eth = NULL;
123 }
124 
125 static void
126 etcta_add_one (ETableClickToAdd *etcta,
127                ETableModel *one)
128 {
129 	etcta->one = one;
130 	if (etcta->one)
131 		g_object_ref (etcta->one);
132 	if (etcta->row)
133 		gnome_canvas_item_set (
134 			GNOME_CANVAS_ITEM (etcta->row),
135 			"ETableModel", one,
136 			NULL);
137 	g_object_set (
138 		etcta->selection,
139 		"model", one,
140 		NULL);
141 }
142 
143 static void
144 etcta_drop_one (ETableClickToAdd *etcta)
145 {
146 	if (!etcta->one)
147 		return;
148 	g_object_unref (etcta->one);
149 	etcta->one = NULL;
150 	g_object_set (
151 		etcta->selection,
152 		"model", NULL,
153 		NULL);
154 }
155 
156 static void
157 etcta_add_model (ETableClickToAdd *etcta,
158                  ETableModel *model)
159 {
160 	etcta->model = model;
161 	if (etcta->model)
162 		g_object_ref (etcta->model);
163 }
164 
165 static void
166 etcta_drop_model (ETableClickToAdd *etcta)
167 {
168 	etcta_drop_one (etcta);
169 	if (!etcta->model)
170 		return;
171 	g_object_unref (etcta->model);
172 	etcta->model = NULL;
173 }
174 
175 static void
176 etcta_add_message (ETableClickToAdd *etcta,
177                    const gchar *message)
178 {
179 	etcta->message = g_strdup (message);
180 }
181 
182 static void
183 etcta_drop_message (ETableClickToAdd *etcta)
184 {
185 	g_free (etcta->message);
186 	etcta->message = NULL;
187 }
188 
189 static void
190 etcta_dispose (GObject *object)
191 {
192 	ETableClickToAdd *etcta = E_TABLE_CLICK_TO_ADD (object);
193 
194 	etcta_drop_table_header (etcta);
195 	etcta_drop_model (etcta);
196 	etcta_drop_message (etcta);
197 	if (etcta->selection)
198 		g_object_unref (etcta->selection);
199 	etcta->selection = NULL;
200 
201 	/* Chain up to parent's dispose() method. */
202 	G_OBJECT_CLASS (etcta_parent_class)->dispose (object);
203 }
204 
205 static void
206 etcta_set_property (GObject *object,
207                     guint property_id,
208                     const GValue *value,
209                     GParamSpec *pspec)
210 {
211 	GnomeCanvasItem *item;
212 	ETableClickToAdd *etcta;
213 
214 	item = GNOME_CANVAS_ITEM (object);
215 	etcta = E_TABLE_CLICK_TO_ADD (object);
216 
217 	switch (property_id) {
218 	case PROP_HEADER:
219 		etcta_drop_table_header (etcta);
220 		etcta_add_table_header (etcta, E_TABLE_HEADER (g_value_get_object (value)));
221 		break;
222 	case PROP_MODEL:
223 		etcta_drop_model (etcta);
224 		etcta_add_model (etcta, E_TABLE_MODEL (g_value_get_object (value)));
225 		break;
226 	case PROP_MESSAGE:
227 		etcta_drop_message (etcta);
228 		etcta_add_message (etcta, g_value_get_string (value));
229 		break;
230 	case PROP_WIDTH:
231 		etcta->width = g_value_get_double (value);
232 		if (etcta->row)
233 			gnome_canvas_item_set (
234 				etcta->row,
235 				"minimum_width", etcta->width,
236 				NULL);
237 		if (etcta->text)
238 			gnome_canvas_item_set (
239 				etcta->text,
240 				"width", (etcta->width < 4 ? 4 : etcta->width) - 4,
241 				NULL);
242 		if (etcta->rect)
243 			gnome_canvas_item_set (
244 				etcta->rect,
245 				"x2", etcta->width - 1,
246 				NULL);
247 		break;
248 	default:
249 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
250 		return;
251 
252 	}
253 	gnome_canvas_item_request_update (item);
254 }
255 
256 static void
257 create_rect_and_text (ETableClickToAdd *etcta)
258 {
259 	GtkWidget *widget;
260 	GtkStyle *style;
261 
262 	widget = GTK_WIDGET (GNOME_CANVAS_ITEM (etcta)->canvas);
263 	style = gtk_widget_get_style (widget);
264 
265 	if (!etcta->rect)
266 		etcta->rect = gnome_canvas_item_new (
267 			GNOME_CANVAS_GROUP (etcta),
268 			gnome_canvas_rect_get_type (),
269 			"x1", (gdouble) 0,
270 			"y1", (gdouble) 0,
271 			"x2", (gdouble) etcta->width - 1,
272 			"y2", (gdouble) etcta->height - 1,
273 			"outline_color_gdk", &style->fg[GTK_STATE_NORMAL],
274 			"fill_color_gdk", &style->bg[GTK_STATE_NORMAL],
275 			NULL);
276 
277 	if (!etcta->text)
278 		etcta->text = gnome_canvas_item_new (
279 			GNOME_CANVAS_GROUP (etcta),
280 			e_text_get_type (),
281 			"text", etcta->message ? etcta->message : "",
282 			"width", etcta->width - 4,
283 			"fill_color_gdk", &style->text[GTK_STATE_NORMAL],
284 			NULL);
285 }
286 
287 static void
288 etcta_get_property (GObject *object,
289                     guint property_id,
290                     GValue *value,
291                     GParamSpec *pspec)
292 {
293 	ETableClickToAdd *etcta;
294 
295 	etcta = E_TABLE_CLICK_TO_ADD (object);
296 
297 	switch (property_id) {
298 	case PROP_HEADER:
299 		g_value_set_object (value, etcta->eth);
300 		break;
301 	case PROP_MODEL:
302 		g_value_set_object (value, etcta->model);
303 		break;
304 	case PROP_MESSAGE:
305 		g_value_set_string (value, etcta->message);
306 		break;
307 	case PROP_WIDTH:
308 		g_value_set_double (value, etcta->width);
309 		break;
310 	case PROP_HEIGHT:
311 		g_value_set_double (value, etcta->height);
312 		break;
313 	default:
314 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
315 		break;
316 	}
317 }
318 
319 static void
320 etcta_realize (GnomeCanvasItem *item)
321 {
322 	ETableClickToAdd *etcta = E_TABLE_CLICK_TO_ADD (item);
323 
324 	create_rect_and_text (etcta);
325 	e_canvas_item_move_absolute (etcta->text, 2, 2);
326 
327 	if (GNOME_CANVAS_ITEM_CLASS (etcta_parent_class)->realize)
328 		(*GNOME_CANVAS_ITEM_CLASS (etcta_parent_class)->realize)(item);
329 
330 	e_canvas_item_request_reflow (item);
331 }
332 
333 static void
334 etcta_unrealize (GnomeCanvasItem *item)
335 {
336 	if (GNOME_CANVAS_ITEM_CLASS (etcta_parent_class)->unrealize)
337 		(*GNOME_CANVAS_ITEM_CLASS (etcta_parent_class)->unrealize)(item);
338 }
339 
340 static void finish_editing (ETableClickToAdd *etcta);
341 
342 static gint
343 item_key_press (ETableItem *item,
344                 gint row,
345                 gint col,
346                 GdkEvent *event,
347                 ETableClickToAdd *etcta)
348 {
349 	switch (event->key.keyval) {
350 		case GDK_KEY_Return:
351 		case GDK_KEY_KP_Enter:
352 		case GDK_KEY_ISO_Enter:
353 		case GDK_KEY_3270_Enter:
354 			finish_editing (etcta);
355 			return TRUE;
356 	}
357 	return FALSE;
358 }
359 
360 static void
361 set_initial_selection (ETableClickToAdd *etcta)
362 {
363 	e_selection_model_do_something (
364 		E_SELECTION_MODEL (etcta->selection),
365 		0, e_table_header_prioritized_column (etcta->eth),
366 		0);
367 }
368 
369 static void
370 finish_editing (ETableClickToAdd *etcta)
371 {
372 	if (etcta->row) {
373 		ETableModel *one;
374 
375 		e_table_item_leave_edit (E_TABLE_ITEM (etcta->row));
376 		e_table_one_commit (E_TABLE_ONE (etcta->one));
377 		etcta_drop_one (etcta);
378 		g_object_run_dispose (G_OBJECT (etcta->row));
379 		etcta->row = NULL;
380 
381 		one = e_table_one_new (etcta->model);
382 		etcta_add_one (etcta, one);
383 		g_object_unref (one);
384 
385 		e_selection_model_clear (E_SELECTION_MODEL (etcta->selection));
386 
387 		etcta->row = gnome_canvas_item_new (
388 			GNOME_CANVAS_GROUP (etcta),
389 			e_table_item_get_type (),
390 			"ETableHeader", etcta->eth,
391 			"ETableModel", etcta->one,
392 			"minimum_width", etcta->width,
393 			"horizontal_draw_grid", TRUE,
394 			"vertical_draw_grid", TRUE,
395 			"selection_model", etcta->selection,
396 			"cursor_mode", E_CURSOR_SPREADSHEET,
397 			NULL);
398 
399 		g_signal_connect (
400 			etcta->row, "key_press",
401 			G_CALLBACK (item_key_press), etcta);
402 
403 		set_initial_selection (etcta);
404 	}
405 }
406 
407 /* Handles the events on the ETableClickToAdd, particularly
408  * it creates the ETableItem and passes in some events. */
409 static gint
410 etcta_event (GnomeCanvasItem *item,
411              GdkEvent *e)
412 {
413 	ETableClickToAdd *etcta = E_TABLE_CLICK_TO_ADD (item);
414 
415 	switch (e->type) {
416 	case GDK_FOCUS_CHANGE:
417 		if (!e->focus_change.in)
418 			return TRUE;
419 
420 	case GDK_BUTTON_PRESS:
421 		if (etcta->text) {
422 			g_object_run_dispose (G_OBJECT (etcta->text));
423 			etcta->text = NULL;
424 		}
425 		if (etcta->rect) {
426 			g_object_run_dispose (G_OBJECT (etcta->rect));
427 			etcta->rect = NULL;
428 		}
429 		if (!etcta->row) {
430 			ETableModel *one;
431 
432 			one = e_table_one_new (etcta->model);
433 			etcta_add_one (etcta, one);
434 			g_object_unref (one);
435 
436 			e_selection_model_clear (E_SELECTION_MODEL (etcta->selection));
437 
438 			etcta->row = gnome_canvas_item_new (
439 				GNOME_CANVAS_GROUP (item),
440 				e_table_item_get_type (),
441 				"ETableHeader", etcta->eth,
442 				"ETableModel", etcta->one,
443 				"minimum_width", etcta->width,
444 				"horizontal_draw_grid", TRUE,
445 				"vertical_draw_grid", TRUE,
446 				"selection_model", etcta->selection,
447 				"cursor_mode", E_CURSOR_SPREADSHEET,
448 				NULL);
449 
450 			g_signal_connect (
451 				etcta->row, "key_press",
452 				G_CALLBACK (item_key_press), etcta);
453 
454 			e_canvas_item_grab_focus (GNOME_CANVAS_ITEM (etcta->row), TRUE);
455 
456 			set_initial_selection (etcta);
457 		}
458 		break;
459 
460 	case GDK_KEY_PRESS:
461 		switch (e->key.keyval) {
462 		case GDK_KEY_Tab:
463 		case GDK_KEY_KP_Tab:
464 		case GDK_KEY_ISO_Left_Tab:
465 			finish_editing (etcta);
466 			break;
467 		default:
468 			return FALSE;
469 		case GDK_KEY_Escape:
470 			if (etcta->row) {
471 				e_table_item_leave_edit (E_TABLE_ITEM (etcta->row));
472 				etcta_drop_one (etcta);
473 				g_object_run_dispose (G_OBJECT (etcta->row));
474 				etcta->row = NULL;
475 				create_rect_and_text (etcta);
476 				e_canvas_item_move_absolute (etcta->text, 3, 3);
477 			}
478 			break;
479 		}
480 		break;
481 
482 	default:
483 		return FALSE;
484 	}
485 	return TRUE;
486 }
487 
488 static void
489 etcta_reflow (GnomeCanvasItem *item,
490               gint flags)
491 {
492 	ETableClickToAdd *etcta = E_TABLE_CLICK_TO_ADD (item);
493 
494 	gdouble old_height = etcta->height;
495 
496 	if (etcta->text) {
497 		g_object_get (
498 			etcta->text,
499 			"height", &etcta->height,
500 			NULL);
501 		etcta->height += 6;
502 	}
503 	if (etcta->row) {
504 		g_object_get (
505 			etcta->row,
506 			"height", &etcta->height,
507 			NULL);
508 	}
509 
510 	if (etcta->rect) {
511 		g_object_set (
512 			etcta->rect,
513 			"y2", etcta->height - 1,
514 			NULL);
515 	}
516 
517 	if (old_height != etcta->height)
518 		e_canvas_item_request_parent_reflow (item);
519 }
520 
521 static void
522 etcta_class_init (ETableClickToAddClass *class)
523 {
524 	GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS (class);
525 	GObjectClass *object_class = G_OBJECT_CLASS (class);
526 
527 	class->cursor_change = NULL;
528 	class->style_set     = etcta_style_set;
529 
530 	object_class->dispose      = etcta_dispose;
531 	object_class->set_property = etcta_set_property;
532 	object_class->get_property = etcta_get_property;
533 
534 	item_class->realize     = etcta_realize;
535 	item_class->unrealize   = etcta_unrealize;
536 	item_class->event       = etcta_event;
537 
538 	g_object_class_install_property (
539 		object_class,
540 		PROP_HEADER,
541 		g_param_spec_object (
542 			"header",
543 			"Header",
544 			NULL,
545 			E_TYPE_TABLE_HEADER,
546 			G_PARAM_READWRITE));
547 
548 	g_object_class_install_property (
549 		object_class,
550 		PROP_MODEL,
551 		g_param_spec_object (
552 			"model",
553 			"Model",
554 			NULL,
555 			E_TYPE_TABLE_MODEL,
556 			G_PARAM_READWRITE));
557 
558 	g_object_class_install_property (
559 		object_class,
560 		PROP_MESSAGE,
561 		g_param_spec_string (
562 			"message",
563 			"Message",
564 			NULL,
565 			NULL,
566 			G_PARAM_READWRITE));
567 
568 	g_object_class_install_property (
569 		object_class,
570 		PROP_WIDTH,
571 		g_param_spec_double (
572 			"width",
573 			"Width",
574 			NULL,
575 			0.0, G_MAXDOUBLE, 0.0,
576 			G_PARAM_READWRITE |
577 			G_PARAM_LAX_VALIDATION));
578 
579 	g_object_class_install_property (
580 		object_class,
581 		PROP_HEIGHT,
582 		g_param_spec_double (
583 			"height",
584 			"Height",
585 			NULL,
586 			0.0, G_MAXDOUBLE, 0.0,
587 			G_PARAM_READABLE |
588 			G_PARAM_LAX_VALIDATION));
589 
590 	etcta_signals[CURSOR_CHANGE] = g_signal_new (
591 		"cursor_change",
592 		G_OBJECT_CLASS_TYPE (object_class),
593 		G_SIGNAL_RUN_LAST,
594 		G_STRUCT_OFFSET (ETableClickToAddClass, cursor_change),
595 		NULL, NULL,
596 		e_marshal_VOID__INT_INT,
597 		G_TYPE_NONE, 2,
598 		G_TYPE_INT,
599 		G_TYPE_INT);
600 
601 	etcta_signals[STYLE_SET] = g_signal_new (
602 		"style_set",
603 		G_OBJECT_CLASS_TYPE (object_class),
604 		G_SIGNAL_RUN_LAST,
605 		G_STRUCT_OFFSET (ETableClickToAddClass, style_set),
606 		NULL, NULL,
607 		g_cclosure_marshal_VOID__OBJECT,
608 		G_TYPE_NONE, 1,
609 		GTK_TYPE_STYLE);
610 
611 	gal_a11y_e_table_click_to_add_init ();
612 }
613 
614 static void
615 etcta_init (ETableClickToAdd *etcta)
616 {
617 	AtkObject *a11y;
618 
619 	etcta->one = NULL;
620 	etcta->model = NULL;
621 	etcta->eth = NULL;
622 
623 	etcta->message = NULL;
624 
625 	etcta->row = NULL;
626 	etcta->text = NULL;
627 	etcta->rect = NULL;
628 
629 	/* Pick some arbitrary defaults. */
630 	etcta->width = 12;
631 	etcta->height = 6;
632 
633 	etcta->selection = e_table_selection_model_new ();
634 	g_signal_connect (
635 		etcta->selection, "cursor_changed",
636 		G_CALLBACK (etcta_cursor_change), etcta);
637 
638 	e_canvas_item_set_reflow_callback (GNOME_CANVAS_ITEM (etcta), etcta_reflow);
639 
640 	/* create its a11y object at this time if accessibility is enabled*/
641 	if (atk_get_root () != NULL) {
642 		a11y = atk_gobject_accessible_for_object (G_OBJECT (etcta));
643 		atk_object_set_name (a11y, _("click to add"));
644 	}
645 }
646 
647 /* The colors in this need to be themefied. */
648 /**
649  * e_table_click_to_add_commit:
650  * @etcta: The %ETableClickToAdd to commit.
651  *
652  * This routine commits the current thing being edited and returns to
653  * just displaying the click to add message.
654  **/
655 void
656 e_table_click_to_add_commit (ETableClickToAdd *etcta)
657 {
658 	if (etcta->row) {
659 		e_table_one_commit (E_TABLE_ONE (etcta->one));
660 		etcta_drop_one (etcta);
661 		g_object_run_dispose (G_OBJECT (etcta->row));
662 		etcta->row = NULL;
663 	}
664 	create_rect_and_text (etcta);
665 	e_canvas_item_move_absolute (etcta->text, 3, 3);
666 }