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