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 <string.h>
28
29 #include <gtk/gtk.h>
30 #include <libgnomecanvas/libgnomecanvas.h>
31 #include <gdk-pixbuf/gdk-pixbuf.h>
32
33 #include <glib/gi18n.h>
34 #include "e-util/e-util.h"
35 #include "libevolution-utils/e-xml-utils.h"
36 #include "misc/e-canvas.h"
37
38 #include "e-table-col-dnd.h"
39 #include "e-table-defines.h"
40 #include "e-table-field-chooser-item.h"
41 #include "e-table-header-utils.h"
42 #include "e-table-header.h"
43
44 #define d(x)
45
46 #if 0
47 enum {
48 BUTTON_PRESSED,
49 LAST_SIGNAL
50 };
51
52 static guint etfci_signals[LAST_SIGNAL] = { 0, };
53 #endif
54
55 /* workaround for avoiding API breakage */
56 #define etfci_get_type e_table_field_chooser_item_get_type
57 G_DEFINE_TYPE (ETableFieldChooserItem, etfci, GNOME_TYPE_CANVAS_ITEM)
58
59 static void etfci_drop_table_header (ETableFieldChooserItem *etfci);
60 static void etfci_drop_full_header (ETableFieldChooserItem *etfci);
61
62 enum {
63 PROP_0,
64 PROP_FULL_HEADER,
65 PROP_HEADER,
66 PROP_DND_CODE,
67 PROP_WIDTH,
68 PROP_HEIGHT
69 };
70
71 static void
72 etfci_dispose (GObject *object)
73 {
74 ETableFieldChooserItem *etfci = E_TABLE_FIELD_CHOOSER_ITEM (object);
75
76 etfci_drop_table_header (etfci);
77 etfci_drop_full_header (etfci);
78
79 if (etfci->combined_header)
80 g_object_unref (etfci->combined_header);
81 etfci->combined_header = NULL;
82
83 if (etfci->font_desc)
84 pango_font_description_free (etfci->font_desc);
85 etfci->font_desc = NULL;
86
87 /* Chain up to parent's dispose() method. */
88 G_OBJECT_CLASS (etfci_parent_class)->dispose (object);
89 }
90
91 static gint
92 etfci_find_button (ETableFieldChooserItem *etfci,
93 gdouble loc)
94 {
95 gint i;
96 gint count;
97 gdouble height = 0;
98
99 count = e_table_header_count (etfci->combined_header);
100 for (i = 0; i < count; i++) {
101 ETableCol *ecol;
102
103 ecol = e_table_header_get_column (etfci->combined_header, i);
104 if (ecol->disabled)
105 continue;
106 height += e_table_header_compute_height (
107 ecol, GTK_WIDGET (GNOME_CANVAS_ITEM (etfci)->canvas));
108 if (height > loc)
109 return i;
110 }
111 return MAX (0, count - 1);
112 }
113
114 static void
115 etfci_rebuild_combined (ETableFieldChooserItem *etfci)
116 {
117 gint count;
118 GHashTable *hash;
119 gint i;
120
121 if (etfci->combined_header != NULL)
122 g_object_unref (etfci->combined_header);
123
124 etfci->combined_header = e_table_header_new ();
125
126 hash = g_hash_table_new (NULL, NULL);
127
128 count = e_table_header_count (etfci->header);
129 for (i = 0; i < count; i++) {
130 ETableCol *ecol = e_table_header_get_column (etfci->header, i);
131 if (ecol->disabled)
132 continue;
133 g_hash_table_insert (
134 hash, GINT_TO_POINTER (ecol->col_idx),
135 GINT_TO_POINTER (1));
136 }
137
138 count = e_table_header_count (etfci->full_header);
139 for (i = 0; i < count; i++) {
140 ETableCol *ecol = e_table_header_get_column (etfci->full_header, i);
141 if (ecol->disabled)
142 continue;
143 if (!(GPOINTER_TO_INT (g_hash_table_lookup (
144 hash, GINT_TO_POINTER (ecol->col_idx)))))
145 e_table_header_add_column (etfci->combined_header, ecol, -1);
146 }
147
148 g_hash_table_destroy (hash);
149 }
150
151 static void
152 etfci_reflow (GnomeCanvasItem *item,
153 gint flags)
154 {
155 ETableFieldChooserItem *etfci = E_TABLE_FIELD_CHOOSER_ITEM (item);
156 gdouble old_height;
157 gint i;
158 gint count;
159 gdouble height = 0;
160
161 etfci_rebuild_combined (etfci);
162
163 old_height = etfci->height;
164
165 count = e_table_header_count (etfci->combined_header);
166 for (i = 0; i < count; i++) {
167 ETableCol *ecol;
168
169 ecol = e_table_header_get_column (etfci->combined_header, i);
170 if (ecol->disabled)
171 continue;
172 height += e_table_header_compute_height (
173 ecol, GTK_WIDGET (GNOME_CANVAS_ITEM (etfci)->canvas));
174 }
175
176 etfci->height = height;
177
178 if (old_height != etfci->height)
179 e_canvas_item_request_parent_reflow (item);
180
181 gnome_canvas_item_request_update (item);
182 }
183
184 static void
185 etfci_update (GnomeCanvasItem *item,
186 const cairo_matrix_t *i2c,
187 gint flags)
188 {
189 ETableFieldChooserItem *etfci = E_TABLE_FIELD_CHOOSER_ITEM (item);
190 gdouble x1, y1, x2, y2;
191
192 if (GNOME_CANVAS_ITEM_CLASS (etfci_parent_class)->update)
193 GNOME_CANVAS_ITEM_CLASS (etfci_parent_class)->update (
194 item, i2c, flags);
195
196 x1 = y1 = 0;
197 x2 = etfci->width;
198 y2 = etfci->height;
199
200 gnome_canvas_matrix_transform_rect (i2c, &x1, &y1, &x2, &y2);
201
202 if (item->x1 != x1 ||
203 item->y1 != y1 ||
204 item->x2 != x2 ||
205 item->y2 != y2)
206 {
207 gnome_canvas_request_redraw (
208 item->canvas, item->x1,
209 item->y1, item->x2, item->y2);
210 item->x1 = x1;
211 item->y1 = y1;
212 item->x2 = x2;
213 item->y2 = y2;
214 /* FIXME: Group Child bounds !? */
215 #if 0
216 gnome_canvas_group_child_bounds (
217 GNOME_CANVAS_GROUP (item->parent), item);
218 #endif
219 }
220 gnome_canvas_request_redraw (
221 item->canvas, item->x1, item->y1, item->x2, item->y2);
222 }
223
224 static void
225 etfci_font_load (ETableFieldChooserItem *etfci)
226 {
227 GtkWidget *widget;
228 GtkStyle *style;
229
230 if (etfci->font_desc)
231 pango_font_description_free (etfci->font_desc);
232
233 widget = GTK_WIDGET (GNOME_CANVAS_ITEM (etfci)->canvas);
234 style = gtk_widget_get_style (widget);
235 etfci->font_desc = pango_font_description_copy (style->font_desc);
236 }
237
238 static void
239 etfci_drop_full_header (ETableFieldChooserItem *etfci)
240 {
241 GObject *header;
242
243 if (!etfci->full_header)
244 return;
245
246 header = G_OBJECT (etfci->full_header);
247 if (etfci->full_header_structure_change_id)
248 g_signal_handler_disconnect (
249 header, etfci->full_header_structure_change_id);
250 if (etfci->full_header_dimension_change_id)
251 g_signal_handler_disconnect (
252 header, etfci->full_header_dimension_change_id);
253 etfci->full_header_structure_change_id = 0;
254 etfci->full_header_dimension_change_id = 0;
255
256 if (header)
257 g_object_unref (header);
258 etfci->full_header = NULL;
259 etfci->height = 0;
260 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etfci));
261 }
262
263 static void
264 full_header_structure_changed (ETableHeader *header,
265 ETableFieldChooserItem *etfci)
266 {
267 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etfci));
268 }
269
270 static void
271 full_header_dimension_changed (ETableHeader *header,
272 gint col,
273 ETableFieldChooserItem *etfci)
274 {
275 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etfci));
276 }
277
278 static void
279 etfci_add_full_header (ETableFieldChooserItem *etfci,
280 ETableHeader *header)
281 {
282 etfci->full_header = header;
283 g_object_ref (etfci->full_header);
284
285 etfci->full_header_structure_change_id = g_signal_connect (
286 header, "structure_change",
287 G_CALLBACK (full_header_structure_changed), etfci);
288 etfci->full_header_dimension_change_id = g_signal_connect (
289 header, "dimension_change",
290 G_CALLBACK (full_header_dimension_changed), etfci);
291 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etfci));
292 }
293
294 static void
295 etfci_drop_table_header (ETableFieldChooserItem *etfci)
296 {
297 GObject *header;
298
299 if (!etfci->header)
300 return;
301
302 header = G_OBJECT (etfci->header);
303 if (etfci->table_header_structure_change_id)
304 g_signal_handler_disconnect (
305 header, etfci->table_header_structure_change_id);
306 if (etfci->table_header_dimension_change_id)
307 g_signal_handler_disconnect (
308 header, etfci->table_header_dimension_change_id);
309 etfci->table_header_structure_change_id = 0;
310 etfci->table_header_dimension_change_id = 0;
311
312 if (header)
313 g_object_unref (header);
314 etfci->header = NULL;
315 etfci->height = 0;
316 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etfci));
317 }
318
319 static void
320 table_header_structure_changed (ETableHeader *header,
321 ETableFieldChooserItem *etfci)
322 {
323 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etfci));
324 }
325
326 static void
327 table_header_dimension_changed (ETableHeader *header,
328 gint col,
329 ETableFieldChooserItem *etfci)
330 {
331 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etfci));
332 }
333
334 static void
335 etfci_add_table_header (ETableFieldChooserItem *etfci,
336 ETableHeader *header)
337 {
338 etfci->header = header;
339 g_object_ref (etfci->header);
340
341 etfci->table_header_structure_change_id = g_signal_connect (
342 header, "structure_change",
343 G_CALLBACK (table_header_structure_changed), etfci);
344 etfci->table_header_dimension_change_id = g_signal_connect (
345 header, "dimension_change",
346 G_CALLBACK (table_header_dimension_changed), etfci);
347 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etfci));
348 }
349
350 static void
351 etfci_set_property (GObject *object,
352 guint property_id,
353 const GValue *value,
354 GParamSpec *pspec)
355 {
356 GnomeCanvasItem *item;
357 ETableFieldChooserItem *etfci;
358
359 item = GNOME_CANVAS_ITEM (object);
360 etfci = E_TABLE_FIELD_CHOOSER_ITEM (object);
361
362 switch (property_id) {
363 case PROP_FULL_HEADER:
364 etfci_drop_full_header (etfci);
365 if (g_value_get_object (value))
366 etfci_add_full_header (
367 etfci, E_TABLE_HEADER (
368 g_value_get_object (value)));
369 break;
370
371 case PROP_HEADER:
372 etfci_drop_table_header (etfci);
373 if (g_value_get_object (value))
374 etfci_add_table_header (
375 etfci, E_TABLE_HEADER (
376 g_value_get_object (value)));
377 break;
378
379 case PROP_DND_CODE:
380 g_free (etfci->dnd_code);
381 etfci->dnd_code = g_strdup (g_value_get_string (value));
382 break;
383
384 case PROP_WIDTH:
385 etfci->width = g_value_get_double (value);
386 gnome_canvas_item_request_update (item);
387 break;
388 }
389 }
390
391 static void
392 etfci_get_property (GObject *object,
393 guint property_id,
394 GValue *value,
395 GParamSpec *pspec)
396 {
397 ETableFieldChooserItem *etfci;
398
399 etfci = E_TABLE_FIELD_CHOOSER_ITEM (object);
400
401 switch (property_id) {
402
403 case PROP_DND_CODE:
404 g_value_set_string (value, etfci->dnd_code);
405 break;
406 case PROP_WIDTH:
407 g_value_set_double (value, etfci->width);
408 break;
409 case PROP_HEIGHT:
410 g_value_set_double (value, etfci->height);
411 break;
412 default:
413 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
414 break;
415 }
416 }
417
418 static void
419 etfci_drag_data_get (GtkWidget *widget,
420 GdkDragContext *context,
421 GtkSelectionData *selection_data,
422 guint info,
423 guint time,
424 ETableFieldChooserItem *etfci)
425 {
426 if (etfci->drag_col != -1) {
427 gchar *string = g_strdup_printf ("%d", etfci->drag_col);
428 gtk_selection_data_set (
429 selection_data,
430 GDK_SELECTION_TYPE_STRING,
431 sizeof (string[0]),
432 (guchar *) string,
433 strlen (string));
434 g_free (string);
435 }
436 }
437
438 static void
439 etfci_drag_end (GtkWidget *canvas,
440 GdkDragContext *context,
441 ETableFieldChooserItem *etfci)
442 {
443 etfci->drag_col = -1;
444 }
445
446 static void
447 etfci_realize (GnomeCanvasItem *item)
448 {
449 ETableFieldChooserItem *etfci = E_TABLE_FIELD_CHOOSER_ITEM (item);
450
451 if (GNOME_CANVAS_ITEM_CLASS (etfci_parent_class)-> realize)
452 (*GNOME_CANVAS_ITEM_CLASS (etfci_parent_class)->realize)(item);
453
454 if (!etfci->font_desc)
455 etfci_font_load (etfci);
456
457 etfci->drag_end_id = g_signal_connect (
458 item->canvas, "drag_end",
459 G_CALLBACK (etfci_drag_end), etfci);
460 etfci->drag_data_get_id = g_signal_connect (
461 item->canvas, "drag_data_get",
462 G_CALLBACK (etfci_drag_data_get), etfci);
463 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etfci));
464 }
465
466 static void
467 etfci_unrealize (GnomeCanvasItem *item)
468 {
469 ETableFieldChooserItem *etfci = E_TABLE_FIELD_CHOOSER_ITEM (item);
470
471 if (etfci->font_desc)
472 pango_font_description_free (etfci->font_desc);
473 etfci->font_desc = NULL;
474
475 g_signal_handler_disconnect (item->canvas, etfci->drag_end_id);
476 etfci->drag_end_id = 0;
477 g_signal_handler_disconnect (item->canvas, etfci->drag_data_get_id);
478 etfci->drag_data_get_id = 0;
479
480 if (GNOME_CANVAS_ITEM_CLASS (etfci_parent_class)->unrealize)
481 (*GNOME_CANVAS_ITEM_CLASS (etfci_parent_class)->unrealize)(item);
482 }
483
484 static void
485 etfci_draw (GnomeCanvasItem *item,
486 cairo_t *cr,
487 gint x,
488 gint y,
489 gint width,
490 gint height)
491 {
492 ETableFieldChooserItem *etfci = E_TABLE_FIELD_CHOOSER_ITEM (item);
493 GnomeCanvas *canvas = item->canvas;
494 gint rows;
495 gint y1, y2;
496 gint row;
497
498 if (etfci->combined_header == NULL)
499 return;
500
501 rows = e_table_header_count (etfci->combined_header);
502
503 y1 = y2 = 0;
504 for (row = 0; row < rows; row++, y1 = y2) {
505 ETableCol *ecol;
506
507 ecol = e_table_header_get_column (etfci->combined_header, row);
508
509 if (ecol->disabled)
510 continue;
511
512 y2 += e_table_header_compute_height (ecol, GTK_WIDGET (canvas));
513
514 if (y1 > (y + height))
515 break;
516
517 if (y2 < y)
518 continue;
519
520 cairo_save (cr);
521
522 e_table_header_draw_button (
523 cr, ecol,
524 GTK_WIDGET (canvas),
525 -x, y1 - y,
526 width, height,
527 etfci->width, y2 - y1,
528 E_TABLE_COL_ARROW_NONE);
529
530 cairo_restore (cr);
531 }
532 }
533
534 static GnomeCanvasItem *
535 etfci_point (GnomeCanvasItem *item,
536 gdouble x,
537 gdouble y,
538 gint cx,
539 gint cy)
540 {
541 return item;
542 }
543
544 static gboolean
545 etfci_maybe_start_drag (ETableFieldChooserItem *etfci,
546 gint x,
547 gint y)
548 {
549 if (!etfci->maybe_drag)
550 return FALSE;
551
552 if (MAX (abs (etfci->click_x - x),
553 abs (etfci->click_y - y)) <= 3)
554 return FALSE;
555
556 return TRUE;
557 }
558
559 static void
560 etfci_start_drag (ETableFieldChooserItem *etfci,
561 GdkEvent *event,
562 gdouble x,
563 gdouble y)
564 {
565 GtkWidget *widget = GTK_WIDGET (GNOME_CANVAS_ITEM (etfci)->canvas);
566 GtkTargetList *list;
567 GdkDragContext *context;
568 ETableCol *ecol;
569 cairo_surface_t *cs;
570 cairo_t *cr;
571 gint drag_col;
572 gint button_height;
573
574 GtkTargetEntry etfci_drag_types[] = {
575 { (gchar *) TARGET_ETABLE_COL_TYPE, 0, TARGET_ETABLE_COL_HEADER },
576 };
577
578 if (etfci->combined_header == NULL)
579 return;
580
581 drag_col = etfci_find_button (etfci, y);
582
583 if (drag_col < 0 || drag_col > e_table_header_count (etfci->combined_header))
584 return;
585
586 ecol = e_table_header_get_column (etfci->combined_header, drag_col);
587
588 if (ecol->disabled)
589 return;
590
591 etfci->drag_col = ecol->col_idx;
592
593 etfci_drag_types[0].target = g_strdup_printf (
594 "%s-%s", etfci_drag_types[0].target, etfci->dnd_code);
595 d (g_print ("etfci - %s\n", etfci_drag_types[0].target));
596 list = gtk_target_list_new (etfci_drag_types, G_N_ELEMENTS (etfci_drag_types));
597 context = gtk_drag_begin (widget, list, GDK_ACTION_MOVE, 1, event);
598 g_free ((gpointer) etfci_drag_types[0].target);
599
600 button_height = e_table_header_compute_height (ecol, widget);
601 cs = cairo_image_surface_create (
602 CAIRO_FORMAT_ARGB32,
603 etfci->width, button_height);
604 cr = cairo_create (cs);
605
606 e_table_header_draw_button (
607 cr, ecol,
608 widget, 0, 0,
609 etfci->width, button_height,
610 etfci->width, button_height,
611 E_TABLE_COL_ARROW_NONE);
612
613 gtk_drag_set_icon_surface (context, cs);
614
615 cairo_surface_destroy (cs);
616 cairo_destroy (cr);
617 etfci->maybe_drag = FALSE;
618 }
619
620 /*
621 * Handles the events on the ETableFieldChooserItem
622 */
623 static gint
624 etfci_event (GnomeCanvasItem *item,
625 GdkEvent *e)
626 {
627 ETableFieldChooserItem *etfci = E_TABLE_FIELD_CHOOSER_ITEM (item);
628 GnomeCanvas *canvas = item->canvas;
629 gint x, y;
630
631 switch (e->type) {
632 case GDK_MOTION_NOTIFY:
633 gnome_canvas_w2c (canvas, e->motion.x, e->motion.y, &x, &y);
634
635 if (etfci_maybe_start_drag (etfci, x, y))
636 etfci_start_drag (etfci, e, x, y);
637 break;
638
639 case GDK_BUTTON_PRESS:
640 gnome_canvas_w2c (canvas, e->button.x, e->button.y, &x, &y);
641
642 if (e->button.button == 1) {
643 etfci->click_x = x;
644 etfci->click_y = y;
645 etfci->maybe_drag = TRUE;
646 }
647 break;
648
649 case GDK_BUTTON_RELEASE: {
650 etfci->maybe_drag = FALSE;
651 break;
652 }
653
654 default:
655 return FALSE;
656 }
657 return TRUE;
658 }
659
660 static void
661 etfci_class_init (ETableFieldChooserItemClass *class)
662 {
663 GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS (class);
664 GObjectClass *object_class = G_OBJECT_CLASS (class);
665
666 object_class->dispose = etfci_dispose;
667 object_class->set_property = etfci_set_property;
668 object_class->get_property = etfci_get_property;
669
670 item_class->update = etfci_update;
671 item_class->realize = etfci_realize;
672 item_class->unrealize = etfci_unrealize;
673 item_class->draw = etfci_draw;
674 item_class->point = etfci_point;
675 item_class->event = etfci_event;
676
677 g_object_class_install_property (
678 object_class,
679 PROP_DND_CODE,
680 g_param_spec_string (
681 "dnd_code",
682 "DnD code",
683 NULL,
684 NULL,
685 G_PARAM_READWRITE));
686
687 g_object_class_install_property (
688 object_class,
689 PROP_FULL_HEADER,
690 g_param_spec_object (
691 "full_header",
692 "Full Header",
693 NULL,
694 E_TYPE_TABLE_HEADER,
695 G_PARAM_READWRITE));
696
697 g_object_class_install_property (
698 object_class,
699 PROP_HEADER,
700 g_param_spec_object (
701 "header",
702 "Header",
703 NULL,
704 E_TYPE_TABLE_HEADER,
705 G_PARAM_READWRITE));
706
707 g_object_class_install_property (
708 object_class,
709 PROP_WIDTH,
710 g_param_spec_double (
711 "width",
712 "Width",
713 NULL,
714 0, G_MAXDOUBLE, 0,
715 G_PARAM_READWRITE));
716
717 g_object_class_install_property (
718 object_class,
719 PROP_HEIGHT,
720 g_param_spec_double (
721 "height",
722 "Height",
723 NULL,
724 0, G_MAXDOUBLE, 0,
725 G_PARAM_READABLE));
726 }
727
728 static void
729 etfci_init (ETableFieldChooserItem *etfci)
730 {
731 etfci->full_header = NULL;
732 etfci->header = NULL;
733 etfci->combined_header = NULL;
734
735 etfci->height = etfci->width = 0;
736
737 etfci->font_desc = NULL;
738
739 etfci->full_header_structure_change_id = 0;
740 etfci->full_header_dimension_change_id = 0;
741 etfci->table_header_structure_change_id = 0;
742 etfci->table_header_dimension_change_id = 0;
743
744 etfci->dnd_code = NULL;
745
746 etfci->maybe_drag = 0;
747 etfci->drag_end_id = 0;
748
749 e_canvas_item_set_reflow_callback (GNOME_CANVAS_ITEM (etfci), etfci_reflow);
750 }