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 }