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 <libedataserver/libedataserver.h>
26
27 #include "e-canvas.h"
28
29 #define d(x)
30
31 enum {
32 REFLOW,
33 LAST_SIGNAL
34 };
35
36 static guint signals[LAST_SIGNAL];
37
G_DEFINE_TYPE(ECanvas,e_canvas,GNOME_TYPE_CANVAS)38 G_DEFINE_TYPE (
39 ECanvas,
40 e_canvas,
41 GNOME_TYPE_CANVAS)
42
43 /* Emits an event for an item in the canvas, be it the current
44 * item, grabbed item, or focused item, as appropriate. */
45 static gint
46 canvas_emit_event (GnomeCanvas *canvas,
47 GdkEvent *event)
48 {
49 GdkEvent *ev;
50 gint finished;
51 GnomeCanvasItem *item;
52 GnomeCanvasItem *parent;
53 guint mask;
54
55 /* Choose where we send the event */
56
57 item = canvas->current_item;
58
59 if (canvas->focused_item &&
60 ((event->type == GDK_KEY_PRESS) ||
61 (event->type == GDK_KEY_RELEASE) ||
62 (event->type == GDK_FOCUS_CHANGE)))
63 item = canvas->focused_item;
64
65 if (canvas->grabbed_item)
66 item = canvas->grabbed_item;
67
68 /* Perform checks for grabbed items */
69
70 if (canvas->grabbed_item) {
71 switch (event->type) {
72 case GDK_ENTER_NOTIFY:
73 mask = GDK_ENTER_NOTIFY_MASK;
74 break;
75
76 case GDK_LEAVE_NOTIFY:
77 mask = GDK_LEAVE_NOTIFY_MASK;
78 break;
79
80 case GDK_MOTION_NOTIFY:
81 mask = GDK_POINTER_MOTION_MASK;
82 break;
83
84 case GDK_BUTTON_PRESS:
85 case GDK_2BUTTON_PRESS:
86 case GDK_3BUTTON_PRESS:
87 mask = GDK_BUTTON_PRESS_MASK;
88 break;
89
90 case GDK_BUTTON_RELEASE:
91 mask = GDK_BUTTON_RELEASE_MASK;
92 break;
93
94 case GDK_KEY_PRESS:
95 mask = GDK_KEY_PRESS_MASK;
96 break;
97
98 case GDK_KEY_RELEASE:
99 mask = GDK_KEY_RELEASE_MASK;
100 break;
101
102 default:
103 mask = 0;
104 break;
105 }
106
107 if (!(mask & canvas->grabbed_event_mask))
108 return FALSE;
109 }
110
111 /* Convert to world coordinates -- we have two cases because of
112 * different offsets of the fields in the event structures. */
113
114 ev = gdk_event_copy (event);
115
116 switch (ev->type) {
117 case GDK_ENTER_NOTIFY:
118 case GDK_LEAVE_NOTIFY:
119 gnome_canvas_window_to_world (
120 canvas,
121 ev->crossing.x, ev->crossing.y,
122 &ev->crossing.x, &ev->crossing.y);
123 break;
124
125 case GDK_MOTION_NOTIFY:
126 case GDK_BUTTON_PRESS:
127 case GDK_2BUTTON_PRESS:
128 case GDK_3BUTTON_PRESS:
129 case GDK_BUTTON_RELEASE:
130 gnome_canvas_window_to_world (
131 canvas,
132 ev->motion.x, ev->motion.y,
133 &ev->motion.x, &ev->motion.y);
134 break;
135
136 default:
137 break;
138 }
139
140 /* The event is propagated up the hierarchy (for if someone connected
141 * to a group instead of a leaf event), and emission is stopped if a
142 * handler returns TRUE, just like for GtkWidget events. */
143
144 finished = FALSE;
145
146 while (item && !finished) {
147 g_object_ref (item);
148
149 g_signal_emit_by_name (item, "event", ev, &finished);
150
151 parent = item->parent;
152 g_object_unref (item);
153
154 item = parent;
155 }
156
157 gdk_event_free (ev);
158
159 return finished;
160 }
161
162 /* This routine invokes the point method of the item. The argument x, y
163 * should be in the parent's item-relative coordinate system. This routine
164 * applies the inverse of the item's transform, maintaining the affine
165 * invariant. */
166 static GnomeCanvasItem *
gnome_canvas_item_invoke_point(GnomeCanvasItem * item,gdouble x,gdouble y,gint cx,gint cy)167 gnome_canvas_item_invoke_point (GnomeCanvasItem *item,
168 gdouble x,
169 gdouble y,
170 gint cx,
171 gint cy)
172 {
173 cairo_matrix_t inverse;
174
175 /* Calculate x & y in item local coordinates */
176 inverse = item->matrix;
177 if (cairo_matrix_invert (&inverse) != CAIRO_STATUS_SUCCESS)
178 return NULL;
179
180 cairo_matrix_transform_point (&inverse, &x, &y);
181
182 if (GNOME_CANVAS_ITEM_GET_CLASS (item)->point)
183 return GNOME_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy);
184
185 return NULL;
186 }
187
188 /* Re-picks the current item in the canvas, based on the event's coordinates.
189 * Also emits enter/leave events for items as appropriate.
190 */
191 #define DISPLAY_X1(canvas) (GNOME_CANVAS (canvas)->layout.xoffset)
192 #define DISPLAY_Y1(canvas) (GNOME_CANVAS (canvas)->layout.yoffset)
193 static gint
pick_current_item(GnomeCanvas * canvas,GdkEvent * event)194 pick_current_item (GnomeCanvas *canvas,
195 GdkEvent *event)
196 {
197 gint button_down;
198 gdouble x, y;
199 gint cx, cy;
200 gint retval;
201
202 retval = FALSE;
203
204 /* If a button is down, we'll perform enter and leave events on the
205 * current item, but not enter on any other item. This is more or less
206 * like X pointer grabbing for canvas items.
207 */
208 button_down = canvas->state & (GDK_BUTTON1_MASK
209 | GDK_BUTTON2_MASK
210 | GDK_BUTTON3_MASK
211 | GDK_BUTTON4_MASK
212 | GDK_BUTTON5_MASK);
213 if (!button_down)
214 canvas->left_grabbed_item = FALSE;
215
216 /* Save the event in the canvas. This is used to synthesize enter and
217 * leave events in case the current item changes. It is also used to
218 * re-pick the current item if the current one gets deleted. Also,
219 * synthesize an enter event.
220 */
221 if (event != &canvas->pick_event) {
222 if ((event->type == GDK_MOTION_NOTIFY) ||
223 (event->type == GDK_BUTTON_RELEASE)) {
224 /* these fields have the same offsets in both types of events */
225
226 canvas->pick_event.crossing.type = GDK_ENTER_NOTIFY;
227 canvas->pick_event.crossing.window = event->motion.window;
228 canvas->pick_event.crossing.send_event = event->motion.send_event;
229 canvas->pick_event.crossing.subwindow = NULL;
230 canvas->pick_event.crossing.x = event->motion.x;
231 canvas->pick_event.crossing.y = event->motion.y;
232 canvas->pick_event.crossing.mode = GDK_CROSSING_NORMAL;
233 canvas->pick_event.crossing.detail = GDK_NOTIFY_NONLINEAR;
234 canvas->pick_event.crossing.focus = FALSE;
235 canvas->pick_event.crossing.state = event->motion.state;
236
237 /* these fields don't have the same offsets in both types of events */
238
239 if (event->type == GDK_MOTION_NOTIFY) {
240 canvas->pick_event.crossing.x_root = event->motion.x_root;
241 canvas->pick_event.crossing.y_root = event->motion.y_root;
242 } else {
243 canvas->pick_event.crossing.x_root = event->button.x_root;
244 canvas->pick_event.crossing.y_root = event->button.y_root;
245 }
246 } else
247 canvas->pick_event = *event;
248 }
249
250 /* Don't do anything else if this is a recursive call */
251
252 if (canvas->in_repick)
253 return retval;
254
255 /* LeaveNotify means that there is no current item, so we don't look for one */
256
257 if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
258 /* these fields don't have the same offsets in both types of events */
259
260 if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
261 x = canvas->pick_event.crossing.x +
262 canvas->scroll_x1 - canvas->zoom_xofs;
263 y = canvas->pick_event.crossing.y +
264 canvas->scroll_y1 - canvas->zoom_yofs;
265 } else {
266 x = canvas->pick_event.motion.x +
267 canvas->scroll_x1 - canvas->zoom_xofs;
268 y = canvas->pick_event.motion.y +
269 canvas->scroll_y1 - canvas->zoom_yofs;
270 }
271
272 /* canvas pixel coords */
273
274 cx = (gint) (x + 0.5);
275 cy = (gint) (y + 0.5);
276
277 /* world coords */
278
279 x = canvas->scroll_x1 + x;
280 y = canvas->scroll_y1 + y;
281
282 /* find the closest item */
283
284 if (canvas->root->flags & GNOME_CANVAS_ITEM_VISIBLE)
285 canvas->new_current_item =
286 gnome_canvas_item_invoke_point (
287 canvas->root, x, y, cx, cy);
288 else
289 canvas->new_current_item = NULL;
290 } else
291 canvas->new_current_item = NULL;
292
293 if ((canvas->new_current_item == canvas->current_item) &&
294 !canvas->left_grabbed_item)
295 return retval; /* current item did not change */
296
297 /* Synthesize events for old and new current items */
298
299 if ((canvas->new_current_item != canvas->current_item)
300 && (canvas->current_item != NULL)
301 && !canvas->left_grabbed_item) {
302 GdkEvent new_event = { 0 };
303
304 new_event = canvas->pick_event;
305 new_event.type = GDK_LEAVE_NOTIFY;
306
307 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
308 new_event.crossing.subwindow = NULL;
309 canvas->in_repick = TRUE;
310 retval = canvas_emit_event (canvas, &new_event);
311 canvas->in_repick = FALSE;
312 }
313
314 /* new_current_item may have been set to NULL during
315 * the call to canvas_emit_event() above. */
316
317 if ((canvas->new_current_item != canvas->current_item) && button_down) {
318 canvas->left_grabbed_item = TRUE;
319 return retval;
320 }
321
322 /* Handle the rest of cases */
323
324 canvas->left_grabbed_item = FALSE;
325 canvas->current_item = canvas->new_current_item;
326
327 if (canvas->current_item != NULL) {
328 GdkEvent new_event = { 0 };
329
330 new_event = canvas->pick_event;
331 new_event.type = GDK_ENTER_NOTIFY;
332 new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
333 new_event.crossing.subwindow = NULL;
334 retval = canvas_emit_event (canvas, &new_event);
335 }
336
337 return retval;
338 }
339
340 static void
canvas_style_updated_recursive(GnomeCanvasItem * item)341 canvas_style_updated_recursive (GnomeCanvasItem *item)
342 {
343 guint signal_id = g_signal_lookup ("style_updated", G_OBJECT_TYPE (item));
344 if (signal_id >= 1) {
345 GSignalQuery query;
346 g_signal_query (signal_id, &query);
347 if (query.return_type == G_TYPE_NONE &&
348 query.n_params == 0) {
349 g_signal_emit (item, signal_id, 0);
350 }
351 }
352
353 if (GNOME_IS_CANVAS_GROUP (item)) {
354 GList *items = GNOME_CANVAS_GROUP (item)->item_list;
355 for (; items; items = items->next)
356 canvas_style_updated_recursive (items->data);
357 }
358 }
359
360 static void
canvas_dispose(GObject * object)361 canvas_dispose (GObject *object)
362 {
363 ECanvas *canvas = E_CANVAS (object);
364
365 if (canvas->idle_id)
366 g_source_remove (canvas->idle_id);
367 canvas->idle_id = 0;
368
369 if (canvas->grab_cancelled_check_id)
370 g_source_remove (canvas->grab_cancelled_check_id);
371 canvas->grab_cancelled_check_id = 0;
372
373 if (canvas->toplevel) {
374 if (canvas->visibility_notify_id)
375 g_signal_handler_disconnect (
376 canvas->toplevel,
377 canvas->visibility_notify_id);
378 canvas->visibility_notify_id = 0;
379
380 g_object_unref (canvas->toplevel);
381 canvas->toplevel = NULL;
382 }
383
384 g_clear_object (&canvas->im_context);
385
386 /* Chain up to parent's dispose() method. */
387 G_OBJECT_CLASS (e_canvas_parent_class)->dispose (object);
388 }
389
390 static void
canvas_realize(GtkWidget * widget)391 canvas_realize (GtkWidget *widget)
392 {
393 ECanvas *ecanvas = E_CANVAS (widget);
394 GdkWindow *window;
395
396 /* Chain up to parent's realize() method. */
397 GTK_WIDGET_CLASS (e_canvas_parent_class)->realize (widget);
398
399 window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));
400 gdk_window_set_background_pattern (window, NULL);
401
402 window = gtk_widget_get_window (widget);
403 gtk_im_context_set_client_window (ecanvas->im_context, window);
404 }
405
406 static void
canvas_unrealize(GtkWidget * widget)407 canvas_unrealize (GtkWidget *widget)
408 {
409 ECanvas * ecanvas = E_CANVAS (widget);
410
411 if (ecanvas->idle_id) {
412 g_source_remove (ecanvas->idle_id);
413 ecanvas->idle_id = 0;
414 }
415
416 gtk_im_context_set_client_window (ecanvas->im_context, NULL);
417
418 /* Chain up to parent's unrealize() method. */
419 GTK_WIDGET_CLASS (e_canvas_parent_class)->unrealize (widget);
420 }
421
422 static void
canvas_style_updated(GtkWidget * widget)423 canvas_style_updated (GtkWidget *widget)
424 {
425 GTK_WIDGET_CLASS (e_canvas_parent_class)->style_updated (widget);
426
427 canvas_style_updated_recursive (
428 GNOME_CANVAS_ITEM (gnome_canvas_root (
429 GNOME_CANVAS (widget))));
430 }
431
432 static gint
canvas_button_event(GtkWidget * widget,GdkEventButton * event)433 canvas_button_event (GtkWidget *widget,
434 GdkEventButton *event)
435 {
436 GnomeCanvas *canvas;
437 GdkWindow *bin_window;
438 gint mask;
439 gint retval;
440
441 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
442 g_return_val_if_fail (event != NULL, FALSE);
443
444 retval = FALSE;
445
446 canvas = GNOME_CANVAS (widget);
447 bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (canvas));
448
449 d (
450 g_print ("button %d, event type %d, grabbed=%p, current=%p\n",
451 event->button,
452 event->type,
453 canvas->grabbed_item,
454 canvas->current_item));
455
456 /* dispatch normally regardless of the event's window if an item has
457 has a pointer grab in effect */
458 if (!canvas->grabbed_item && event->window != bin_window)
459 return retval;
460
461 switch (event->button) {
462 case 1:
463 mask = GDK_BUTTON1_MASK;
464 break;
465 case 2:
466 mask = GDK_BUTTON2_MASK;
467 break;
468 case 3:
469 mask = GDK_BUTTON3_MASK;
470 break;
471 case 4:
472 mask = GDK_BUTTON4_MASK;
473 break;
474 case 5:
475 mask = GDK_BUTTON5_MASK;
476 break;
477 default:
478 mask = 0;
479 }
480
481 switch (event->type) {
482 case GDK_BUTTON_PRESS:
483 case GDK_2BUTTON_PRESS:
484 case GDK_3BUTTON_PRESS:
485 /* Pick the current item as if the button were not
486 * pressed, and then process the event. */
487 canvas->state = event->state;
488 pick_current_item (canvas, (GdkEvent *) event);
489 canvas->state ^= mask;
490 retval = canvas_emit_event (canvas, (GdkEvent *) event);
491 break;
492
493 case GDK_BUTTON_RELEASE:
494 /* Process the event as if the button were pressed,
495 * then repick after the button has been released. */
496 canvas->state = event->state;
497 retval = canvas_emit_event (canvas, (GdkEvent *) event);
498 event->state ^= mask;
499 canvas->state = event->state;
500 pick_current_item (canvas, (GdkEvent *) event);
501 event->state ^= mask;
502 break;
503
504 default:
505 g_return_val_if_reached (0);
506 }
507
508 return retval;
509 }
510
511 static gint
canvas_key_event(GtkWidget * widget,GdkEventKey * event)512 canvas_key_event (GtkWidget *widget,
513 GdkEventKey *event)
514 {
515 GnomeCanvas *canvas;
516 GdkEvent full_event = { 0 };
517
518 g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
519 g_return_val_if_fail (event != NULL, FALSE);
520
521 canvas = GNOME_CANVAS (widget);
522
523 full_event.type = event->type;
524 full_event.key = *event;
525
526 return canvas_emit_event (canvas, &full_event);
527 }
528
529 static gint
canvas_focus_in_event(GtkWidget * widget,GdkEventFocus * event)530 canvas_focus_in_event (GtkWidget *widget,
531 GdkEventFocus *event)
532 {
533 GnomeCanvas *canvas;
534 ECanvas *ecanvas;
535 GdkEvent full_event = { 0 };
536
537 canvas = GNOME_CANVAS (widget);
538 ecanvas = E_CANVAS (widget);
539
540 /* XXX Can't access flags directly anymore, but is it really needed?
541 * If so, could we call gtk_widget_send_focus_change() instead? */
542 #if 0
543 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
544 #endif
545
546 gtk_im_context_focus_in (ecanvas->im_context);
547
548 if (canvas->focused_item) {
549 full_event.type = event->type;
550 full_event.focus_change = *event;
551 return canvas_emit_event (canvas, &full_event);
552 } else {
553 return FALSE;
554 }
555 }
556
557 static gint
canvas_focus_out_event(GtkWidget * widget,GdkEventFocus * event)558 canvas_focus_out_event (GtkWidget *widget,
559 GdkEventFocus *event)
560 {
561 GnomeCanvas *canvas;
562 ECanvas *ecanvas;
563 GdkEvent full_event = { 0 };
564
565 canvas = GNOME_CANVAS (widget);
566 ecanvas = E_CANVAS (widget);
567
568 /* XXX Can't access flags directly anymore, but is it really needed?
569 * If so, could we call gtk_widget_send_focus_change() instead? */
570 #if 0
571 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
572 #endif
573
574 gtk_im_context_focus_out (ecanvas->im_context);
575
576 if (canvas->focused_item) {
577 full_event.type = event->type;
578 full_event.focus_change = *event;
579 return canvas_emit_event (canvas, &full_event);
580 } else {
581 return FALSE;
582 }
583 }
584
585 static void
canvas_reflow(ECanvas * canvas)586 canvas_reflow (ECanvas *canvas)
587 {
588 /* Placeholder so subclasses can safely chain up. */
589 }
590
591 static void
e_canvas_class_init(ECanvasClass * class)592 e_canvas_class_init (ECanvasClass *class)
593 {
594 GObjectClass *object_class;
595 GtkWidgetClass *widget_class;
596
597 object_class = G_OBJECT_CLASS (class);
598 object_class->dispose = canvas_dispose;
599
600 widget_class = GTK_WIDGET_CLASS (class);
601 widget_class->realize = canvas_realize;
602 widget_class->unrealize = canvas_unrealize;
603 widget_class->style_updated = canvas_style_updated;
604 widget_class->button_press_event = canvas_button_event;
605 widget_class->button_release_event = canvas_button_event;
606 widget_class->key_press_event = canvas_key_event;
607 widget_class->key_release_event = canvas_key_event;
608 widget_class->focus_in_event = canvas_focus_in_event;
609 widget_class->focus_out_event = canvas_focus_out_event;
610
611 class->reflow = canvas_reflow;
612
613 signals[REFLOW] = g_signal_new (
614 "reflow",
615 G_OBJECT_CLASS_TYPE (object_class),
616 G_SIGNAL_RUN_LAST,
617 G_STRUCT_OFFSET (ECanvasClass, reflow),
618 NULL, NULL,
619 g_cclosure_marshal_VOID__VOID,
620 G_TYPE_NONE, 0);
621 }
622
623 static void
e_canvas_init(ECanvas * canvas)624 e_canvas_init (ECanvas *canvas)
625 {
626 canvas->im_context = gtk_im_multicontext_new ();
627 }
628
629 GtkWidget *
e_canvas_new(void)630 e_canvas_new (void)
631 {
632 return g_object_new (E_TYPE_CANVAS, NULL);
633 }
634
635 /**
636 * e_canvas_item_grab_focus:
637 * @item: A canvas item.
638 * @widget_too: Whether or not to grab the widget-level focus too
639 *
640 * Makes the specified item take the keyboard focus, so all keyboard
641 * events will be sent to it. If the canvas widget itself did not have
642 * the focus and @widget_too is %TRUE, it grabs that focus as well.
643 **/
644 void
e_canvas_item_grab_focus(GnomeCanvasItem * item,gboolean widget_too)645 e_canvas_item_grab_focus (GnomeCanvasItem *item,
646 gboolean widget_too)
647 {
648 GnomeCanvasItem *focused_item;
649 GdkWindow *bin_window;
650 GdkEvent ev = { 0 };
651
652 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
653 g_return_if_fail (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)));
654
655 bin_window = gtk_layout_get_bin_window (GTK_LAYOUT (item->canvas));
656
657 focused_item = item->canvas->focused_item;
658
659 if (focused_item) {
660 ev.type = GDK_FOCUS_CHANGE;
661 ev.focus_change.type = GDK_FOCUS_CHANGE;
662 ev.focus_change.window = bin_window;
663 ev.focus_change.send_event = FALSE;
664 ev.focus_change.in = FALSE;
665
666 canvas_emit_event (item->canvas, &ev);
667 }
668
669 item->canvas->focused_item = item;
670
671 if (widget_too && !gtk_widget_has_focus (GTK_WIDGET (item->canvas))) {
672 gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
673 }
674
675 ev.focus_change.type = GDK_FOCUS_CHANGE;
676 ev.focus_change.window = bin_window;
677 ev.focus_change.send_event = FALSE;
678 ev.focus_change.in = TRUE;
679
680 canvas_emit_event (item->canvas, &ev);
681 }
682
683 static void
e_canvas_item_invoke_reflow(GnomeCanvasItem * item,gint flags)684 e_canvas_item_invoke_reflow (GnomeCanvasItem *item,
685 gint flags)
686 {
687 GnomeCanvasGroup *group;
688 GList *list;
689 GnomeCanvasItem *child;
690
691 if (GNOME_IS_CANVAS_GROUP (item)) {
692 group = GNOME_CANVAS_GROUP (item);
693 for (list = group->item_list; list; list = list->next) {
694 child = GNOME_CANVAS_ITEM (list->data);
695 if (child->flags & E_CANVAS_ITEM_DESCENDENT_NEEDS_REFLOW)
696 e_canvas_item_invoke_reflow (child, flags);
697 }
698 }
699
700 if (item->flags & E_CANVAS_ITEM_NEEDS_REFLOW) {
701 ECanvasItemReflowFunc func;
702 func = (ECanvasItemReflowFunc)
703 g_object_get_data (
704 G_OBJECT (item),
705 "ECanvasItem::reflow_callback");
706 if (func)
707 func (item, flags);
708 }
709
710 item->flags &= ~E_CANVAS_ITEM_NEEDS_REFLOW;
711 item->flags &= ~E_CANVAS_ITEM_DESCENDENT_NEEDS_REFLOW;
712 }
713
714 static void
do_reflow(ECanvas * canvas)715 do_reflow (ECanvas *canvas)
716 {
717 if (GNOME_CANVAS (canvas)->root->flags & E_CANVAS_ITEM_DESCENDENT_NEEDS_REFLOW)
718 e_canvas_item_invoke_reflow (GNOME_CANVAS (canvas)->root, 0);
719 }
720
721 /* Idle handler for the e-canvas. It deals with pending reflows. */
722 static gint
idle_handler(gpointer data)723 idle_handler (gpointer data)
724 {
725 ECanvas *canvas;
726
727 canvas = E_CANVAS (data);
728 do_reflow (canvas);
729
730 /* Reset idle id */
731 canvas->idle_id = 0;
732
733 g_signal_emit (canvas, signals[REFLOW], 0);
734
735 return FALSE;
736 }
737
738 /* Convenience function to add an idle handler to a canvas */
739 static void
add_idle(ECanvas * canvas)740 add_idle (ECanvas *canvas)
741 {
742 if (canvas->idle_id != 0)
743 return;
744
745 canvas->idle_id = g_idle_add_full (
746 G_PRIORITY_HIGH_IDLE, idle_handler, (gpointer) canvas, NULL);
747 }
748
749 static void
e_canvas_item_descendent_needs_reflow(GnomeCanvasItem * item)750 e_canvas_item_descendent_needs_reflow (GnomeCanvasItem *item)
751 {
752 if (item->flags & E_CANVAS_ITEM_DESCENDENT_NEEDS_REFLOW)
753 return;
754
755 item->flags |= E_CANVAS_ITEM_DESCENDENT_NEEDS_REFLOW;
756 if (item->parent)
757 e_canvas_item_descendent_needs_reflow (item->parent);
758 }
759
760 void
e_canvas_item_request_reflow(GnomeCanvasItem * item)761 e_canvas_item_request_reflow (GnomeCanvasItem *item)
762 {
763 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
764
765 if (item->flags & GNOME_CANVAS_ITEM_REALIZED) {
766 item->flags |= E_CANVAS_ITEM_NEEDS_REFLOW;
767 e_canvas_item_descendent_needs_reflow (item);
768 add_idle (E_CANVAS (item->canvas));
769 }
770 }
771
772 void
e_canvas_item_request_parent_reflow(GnomeCanvasItem * item)773 e_canvas_item_request_parent_reflow (GnomeCanvasItem *item)
774 {
775 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
776
777 e_canvas_item_request_reflow (item->parent);
778 }
779
780 void
e_canvas_item_set_reflow_callback(GnomeCanvasItem * item,ECanvasItemReflowFunc func)781 e_canvas_item_set_reflow_callback (GnomeCanvasItem *item,
782 ECanvasItemReflowFunc func)
783 {
784 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
785 g_return_if_fail (func != NULL);
786
787 g_object_set_data (
788 G_OBJECT (item), "ECanvasItem::reflow_callback",
789 (gpointer) func);
790 }
791
792 static gboolean
grab_cancelled_check(gpointer data)793 grab_cancelled_check (gpointer data)
794 {
795 ECanvas *canvas = data;
796
797 if (GNOME_CANVAS (canvas)->grabbed_item == NULL) {
798 canvas->grab_cancelled_cb = NULL;
799 canvas->grab_cancelled_check_id = 0;
800 canvas->grab_cancelled_time = 0;
801 canvas->grab_cancelled_data = NULL;
802 return FALSE;
803 }
804
805 if (gtk_grab_get_current ()) {
806 gnome_canvas_item_ungrab (
807 GNOME_CANVAS (canvas)->grabbed_item,
808 canvas->grab_cancelled_time);
809 if (canvas->grab_cancelled_cb)
810 canvas->grab_cancelled_cb (
811 canvas, GNOME_CANVAS (canvas)->grabbed_item,
812 canvas->grab_cancelled_data);
813 canvas->grab_cancelled_cb = NULL;
814 canvas->grab_cancelled_check_id = 0;
815 canvas->grab_cancelled_time = 0;
816 canvas->grab_cancelled_data = NULL;
817 return FALSE;
818 }
819 return TRUE;
820 }
821
822 gint
e_canvas_item_grab(ECanvas * canvas,GnomeCanvasItem * item,guint event_mask,GdkCursor * cursor,GdkDevice * device,guint32 etime,ECanvasItemGrabCancelled cancelled_cb,gpointer cancelled_data)823 e_canvas_item_grab (ECanvas *canvas,
824 GnomeCanvasItem *item,
825 guint event_mask,
826 GdkCursor *cursor,
827 GdkDevice *device,
828 guint32 etime,
829 ECanvasItemGrabCancelled cancelled_cb,
830 gpointer cancelled_data)
831 {
832 GdkGrabStatus grab_status;
833
834 g_return_val_if_fail (E_IS_CANVAS (canvas), -1);
835 g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (item), -1);
836 g_return_val_if_fail (GDK_IS_DEVICE (device), -1);
837
838 if (gtk_grab_get_current ())
839 return GDK_GRAB_ALREADY_GRABBED;
840
841 grab_status = gnome_canvas_item_grab (
842 item, event_mask, cursor, device, etime);
843 if (grab_status == GDK_GRAB_SUCCESS) {
844 canvas->grab_cancelled_cb = cancelled_cb;
845 canvas->grab_cancelled_check_id = e_named_timeout_add_full (
846 G_PRIORITY_LOW, 100,
847 grab_cancelled_check, canvas, NULL);
848 canvas->grab_cancelled_time = etime;
849 canvas->grab_cancelled_data = cancelled_data;
850 }
851
852 return grab_status;
853 }
854
855 void
e_canvas_item_ungrab(ECanvas * canvas,GnomeCanvasItem * item,guint32 etime)856 e_canvas_item_ungrab (ECanvas *canvas,
857 GnomeCanvasItem *item,
858 guint32 etime)
859 {
860 g_return_if_fail (E_IS_CANVAS (canvas));
861 g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
862
863 if (canvas->grab_cancelled_check_id) {
864 g_source_remove (canvas->grab_cancelled_check_id);
865 canvas->grab_cancelled_cb = NULL;
866 canvas->grab_cancelled_check_id = 0;
867 canvas->grab_cancelled_time = 0;
868 canvas->grab_cancelled_data = NULL;
869 gnome_canvas_item_ungrab (item, etime);
870 }
871 }
872