evolution-3.6.4/widgets/misc/e-contact-marker.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-contact-marker.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-contact-marker.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /*
  2  * e-contact-marker.c
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  * Copyright (C) 2008 Pierre-Luc Beaudoin <pierre-luc@pierlux.com>
 18  * Copyright (C) 2011 Jiri Techet <techet@gmail.com>
 19  * Copyright (C) 2011 Dan Vratil <dvratil@redhat.com>
 20  *
 21  */
 22 
 23 #ifdef HAVE_CONFIG_H
 24 #include <config.h>
 25 #endif
 26 
 27 #ifdef WITH_CONTACT_MAPS
 28 
 29 #include "e-contact-marker.h"
 30 
 31 #include <champlain/champlain.h>
 32 #include <gtk/gtk.h>
 33 #include <clutter/clutter.h>
 34 #include <glib.h>
 35 #include <glib/gi18n.h>
 36 #include <glib-object.h>
 37 #include <cairo.h>
 38 #include <math.h>
 39 #include <string.h>
 40 
 41 #define E_CONTACT_MARKER_GET_PRIVATE(obj) \
 42         (G_TYPE_INSTANCE_GET_PRIVATE \
 43         ((obj), E_TYPE_CONTACT_MARKER, EContactMarkerPrivate))
 44 
 45 G_DEFINE_TYPE (EContactMarker, e_contact_marker, CHAMPLAIN_TYPE_LABEL);
 46 
 47 struct _EContactMarkerPrivate
 48 {
 49 	gchar *contact_uid;
 50 
 51 	ClutterActor *image;
 52 	ClutterActor *text_actor;
 53 
 54 	ClutterActor *shadow;
 55 	ClutterActor *background;
 56 
 57 	guint total_width;
 58 	guint total_height;
 59 
 60 	ClutterGroup *content_group;
 61 
 62 	guint redraw_id;
 63 };
 64 
 65 enum {
 66 	DOUBLE_CLICKED,
 67 	LAST_SIGNAL
 68 };
 69 
 70 static gint signals[LAST_SIGNAL] = {0};
 71 
 72 #define DEFAULT_FONT_NAME "Serif 9"
 73 
 74 static ClutterColor DEFAULT_COLOR = { 0x33, 0x33, 0x33, 0xff };
 75 
 76 #define RADIUS 10
 77 #define PADDING (RADIUS / 2)
 78 
 79 static gboolean
 80 contact_marker_clicked_cb (ClutterActor *actor,
 81                            ClutterEvent *event,
 82                            gpointer user_data)
 83 {
 84 	gint click_count = clutter_event_get_click_count (event);
 85 
 86 	if (click_count == 2)
 87 		g_signal_emit (E_CONTACT_MARKER (actor), signals[DOUBLE_CLICKED], 0);
 88 
 89 	return TRUE;
 90 }
 91 
 92 static ClutterActor *
 93 texture_new_from_pixbuf (GdkPixbuf *pixbuf,
 94                          GError **error)
 95 {
 96 	ClutterActor *texture = NULL;
 97 	const guchar *data;
 98 	gboolean has_alpha, success;
 99 	gint width, height, rowstride;
100 	ClutterTextureFlags flags = 0;
101 
102 	data = gdk_pixbuf_get_pixels (pixbuf);
103 	width = gdk_pixbuf_get_width (pixbuf);
104 	height = gdk_pixbuf_get_height (pixbuf);
105 	has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
106 	rowstride = gdk_pixbuf_get_rowstride (pixbuf);
107 
108 	texture = clutter_texture_new ();
109 	success = clutter_texture_set_from_rgb_data (
110 		CLUTTER_TEXTURE (texture),
111 		data, has_alpha, width, height, rowstride,
112 		(has_alpha ? 4: 3), flags, NULL);
113 
114 	if (!success) {
115 		clutter_actor_destroy (CLUTTER_ACTOR (texture));
116 		texture = NULL;
117 	}
118 
119 	return texture;
120 }
121 
122 static ClutterActor *
123 contact_photo_to_texture (EContactPhoto *photo)
124 {
125 	GdkPixbuf *pixbuf;
126 
127 	if  (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
128 		GError *error = NULL;
129 
130 		GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
131 		gdk_pixbuf_loader_write (
132 			loader, photo->data.inlined.data,
133 			photo->data.inlined.length, NULL);
134 		gdk_pixbuf_loader_close (loader, NULL);
135 		pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
136 		if (pixbuf)
137 			g_object_ref (pixbuf);
138 		g_object_unref (loader);
139 
140 		if (error) {
141 			g_error_free (error);
142 			return NULL;
143 		}
144 	} else if (photo->type == E_CONTACT_PHOTO_TYPE_URI) {
145 		GError *error = NULL;
146 
147 		pixbuf = gdk_pixbuf_new_from_file (photo->data.uri, &error);
148 
149 		if (error) {
150 			g_error_free (error);
151 			return NULL;
152 		}
153 	} else
154 		return NULL;
155 
156 	if (pixbuf) {
157 		ClutterActor *texture;
158 		GError *error = NULL;
159 
160 		texture = texture_new_from_pixbuf (pixbuf, &error);
161 		if (error) {
162 			g_error_free (error);
163 			g_object_unref (pixbuf);
164 			return NULL;
165 		}
166 
167 		g_object_unref (pixbuf);
168 		return texture;
169 	}
170 
171 	return NULL;
172 }
173 
174 static void
175 draw_box (cairo_t *cr,
176           gint width,
177           gint height,
178           gint point)
179 {
180       cairo_move_to (cr, RADIUS, 0);
181       cairo_line_to (cr, width - RADIUS, 0);
182       cairo_arc (cr, width - RADIUS, RADIUS, RADIUS - 1, 3 * M_PI / 2.0, 0);
183       cairo_line_to (cr, width, height - RADIUS);
184       cairo_arc (cr, width - RADIUS, height - RADIUS, RADIUS - 1, 0, M_PI / 2.0);
185       cairo_line_to (cr, point, height);
186       cairo_line_to (cr, 0, height + point);
187       cairo_arc (cr, RADIUS, RADIUS, RADIUS - 1, M_PI, 3 * M_PI / 2.0);
188       cairo_close_path (cr);
189 }
190 
191 static void
192 draw_shadow (EContactMarker *marker,
193              gint width,
194              gint height,
195              gint point)
196 {
197 	EContactMarkerPrivate *priv = marker->priv;
198 	ClutterActor *shadow = NULL;
199 	cairo_t *cr;
200 	gdouble slope;
201 	gdouble scaling;
202 	gint x;
203 	cairo_matrix_t matrix;
204 
205 	slope = -0.3;
206 	scaling = 0.65;
207 	x = -40 * slope;
208 
209 	shadow = clutter_cairo_texture_new (width + x, (height + point));
210 	cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (shadow));
211 
212 	cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
213 	cairo_paint (cr);
214 	cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
215 
216 	cairo_matrix_init (&matrix, 1, 0, slope, scaling, x, 0);
217 	cairo_set_matrix (cr, &matrix);
218 
219 	draw_box (cr, width, height, point);
220 
221 	cairo_set_source_rgba (cr, 0, 0, 0, 0.15);
222 	cairo_fill (cr);
223 
224 	cairo_destroy (cr);
225 
226 	clutter_actor_set_position (shadow, 0, height / 2.0);
227 
228 	clutter_container_add_actor (CLUTTER_CONTAINER (priv->content_group), shadow);
229 
230 	if (priv->shadow != NULL) {
231 		clutter_container_remove_actor (
232 			CLUTTER_CONTAINER (priv->content_group),
233 			priv->shadow);
234 	}
235 
236 	priv->shadow = shadow;
237 }
238 
239 static void
240 draw_background (EContactMarker *marker,
241                  gint width,
242                  gint height,
243                  gint point)
244 {
245 	EContactMarkerPrivate *priv = marker->priv;
246 	ClutterActor *bg = NULL;
247 	const ClutterColor *color;
248 	ClutterColor darker_color;
249 	cairo_t *cr;
250 
251 	bg = clutter_cairo_texture_new (width, height + point);
252 	cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (bg));
253 
254 	cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
255 	cairo_paint (cr);
256 	cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
257 
258   	/* If selected, add the selection color to the marker's color */
259 	if (champlain_marker_get_selected (CHAMPLAIN_MARKER (marker)))
260 		color = champlain_marker_get_selection_color ();
261 	else
262 		color = &DEFAULT_COLOR;
263 
264 	draw_box (cr, width, height, point);
265 
266 	clutter_color_darken (color, &darker_color);
267 
268 	cairo_set_source_rgba (
269 		cr,
270 		color->red / 255.0,
271 		color->green / 255.0,
272 		color->blue / 255.0,
273 		color->alpha / 255.0);
274 	cairo_fill_preserve (cr);
275 
276 	cairo_set_line_width (cr, 1.0);
277 	cairo_set_source_rgba (
278 		cr,
279 		darker_color.red / 255.0,
280 		darker_color.green / 255.0,
281 		darker_color.blue / 255.0,
282 		darker_color.alpha / 255.0);
283 	cairo_stroke (cr);
284 	cairo_destroy (cr);
285 
286 	clutter_container_add_actor (CLUTTER_CONTAINER (priv->content_group), bg);
287 
288 	if (priv->background != NULL) {
289 		clutter_container_remove_actor (
290 			CLUTTER_CONTAINER (priv->content_group),
291 			priv->background);
292 	}
293 
294 	priv->background = bg;
295 }
296 
297 static void
298 draw_marker (EContactMarker *marker)
299 {
300 	EContactMarkerPrivate *priv = marker->priv;
301 	ChamplainLabel *label = CHAMPLAIN_LABEL (marker);
302 	guint height = 0, point = 0;
303 	guint total_width = 0, total_height = 0;
304 	ClutterText *text;
305 
306 	if (priv->image) {
307 		clutter_actor_set_position (priv->image, 2 *PADDING, 2 *PADDING);
308 		if (clutter_actor_get_parent (priv->image) == NULL)
309 			clutter_container_add_actor (
310 				CLUTTER_CONTAINER (priv->content_group),
311 				priv->image);
312 	}
313 
314 	if (priv->text_actor == NULL) {
315 		priv->text_actor = clutter_text_new_with_text (
316 			"Serif 8",
317 			champlain_label_get_text (label));
318 		champlain_label_set_font_name (label, "Serif 8");
319 	}
320 
321 	text = CLUTTER_TEXT (priv->text_actor);
322 	clutter_text_set_text (
323 		text,
324 		champlain_label_get_text (label));
325 	clutter_text_set_font_name (
326 		text,
327 		champlain_label_get_font_name (label));
328 	clutter_text_set_line_alignment (text, PANGO_ALIGN_CENTER);
329 	clutter_text_set_line_wrap (text, TRUE);
330 	clutter_text_set_line_wrap_mode (text, PANGO_WRAP_WORD);
331 	clutter_text_set_ellipsize (
332 		text,
333 		champlain_label_get_ellipsize (label));
334 	clutter_text_set_attributes (
335 		text,
336 		champlain_label_get_attributes (label));
337 	clutter_text_set_use_markup (
338 		text,
339 		champlain_label_get_use_markup (label));
340 
341 	if (priv->image) {
342 		clutter_actor_set_width (
343 			priv->text_actor,
344 			clutter_actor_get_width (priv->image));
345 		total_height = clutter_actor_get_height (priv->image) + 2 *PADDING +
346 			       clutter_actor_get_height (priv->text_actor) + 2 *PADDING;
347 		total_width = clutter_actor_get_width (priv->image) + 4 *PADDING;
348 		clutter_actor_set_position (
349 			priv->text_actor, PADDING,
350 			clutter_actor_get_height (priv->image) + 2 *PADDING + 3);
351 	} else {
352 		total_height = clutter_actor_get_height (priv->text_actor) + 2 *PADDING;
353 		total_width = clutter_actor_get_width (priv->text_actor) + 4 *PADDING;
354 		clutter_actor_set_position (priv->text_actor, 2 * PADDING, PADDING);
355 	}
356 
357 	height += 2 * PADDING;
358 	if (height > total_height)
359 		total_height = height;
360 
361 	clutter_text_set_color (
362 		CLUTTER_TEXT (priv->text_actor),
363 		(champlain_marker_get_selected (CHAMPLAIN_MARKER (marker)) ?
364 			champlain_marker_get_selection_text_color () :
365 			champlain_label_get_text_color (CHAMPLAIN_LABEL (marker))));
366 	if (clutter_actor_get_parent (priv->text_actor) == NULL)
367 		clutter_container_add_actor (
368 			CLUTTER_CONTAINER (priv->content_group),
369 			priv->text_actor);
370 
371 	if (priv->text_actor == NULL && priv->image == NULL) {
372 		total_width = 6 * PADDING;
373 		total_height = 6 * PADDING;
374 	}
375 
376 	point = (total_height + 2 * PADDING) / 4.0;
377 	priv->total_width = total_width;
378 	priv->total_height = total_height;
379 
380 	draw_shadow (marker, total_width, total_height, point);
381 	draw_background (marker, total_width, total_height, point);
382 
383 	if (priv->text_actor != NULL && priv->background != NULL)
384 		clutter_actor_raise (priv->text_actor, priv->background);
385 	if (priv->image != NULL && priv->background != NULL)
386 		clutter_actor_raise (priv->image, priv->background);
387 
388 	clutter_actor_set_anchor_point (CLUTTER_ACTOR (marker), 0, total_height + point);
389 }
390 
391 static gboolean
392 redraw_on_idle (gpointer gobject)
393 {
394 	EContactMarker *marker = E_CONTACT_MARKER (gobject);
395 
396 	draw_marker (marker);
397 	marker->priv->redraw_id = 0;
398 	return FALSE;
399 }
400 
401 static void
402 queue_redraw (EContactMarker *marker)
403 {
404 	EContactMarkerPrivate *priv = marker->priv;
405 
406 	if (!priv->redraw_id) {
407 		priv->redraw_id = g_idle_add_full (
408 			G_PRIORITY_DEFAULT,
409 			(GSourceFunc) redraw_on_idle,
410 			g_object_ref (marker),
411 			(GDestroyNotify) g_object_unref);
412 	}
413 }
414 
415 static void
416 allocate (ClutterActor *self,
417           const ClutterActorBox *box,
418           ClutterAllocationFlags flags)
419 {
420 	ClutterActorBox child_box;
421 	EContactMarkerPrivate *priv = E_CONTACT_MARKER (self)->priv;
422 
423 	CLUTTER_ACTOR_CLASS (e_contact_marker_parent_class)->allocate (self, box, flags);
424 
425 	child_box.x1 = 0;
426 	child_box.x2 = box->x2 - box->x1;
427 	child_box.y1 = 0;
428 	child_box.y2 = box->y2 - box->y1;
429 	clutter_actor_allocate (CLUTTER_ACTOR (priv->content_group), &child_box, flags);
430 }
431 
432 static void
433 paint (ClutterActor *self)
434 {
435 	EContactMarkerPrivate *priv = E_CONTACT_MARKER (self)->priv;
436 
437 	clutter_actor_paint (CLUTTER_ACTOR (priv->content_group));
438 }
439 
440 static void
441 map (ClutterActor *self)
442 {
443 	EContactMarkerPrivate *priv = E_CONTACT_MARKER (self)->priv;
444 
445 	CLUTTER_ACTOR_CLASS (e_contact_marker_parent_class)->map (self);
446 
447 	clutter_actor_map (CLUTTER_ACTOR (priv->content_group));
448 }
449 
450 static void
451 unmap (ClutterActor *self)
452 {
453 	EContactMarkerPrivate *priv = E_CONTACT_MARKER (self)->priv;
454 
455 	CLUTTER_ACTOR_CLASS (e_contact_marker_parent_class)->unmap (self);
456 
457 	clutter_actor_unmap (CLUTTER_ACTOR (priv->content_group));
458 }
459 
460 static void
461 pick (ClutterActor *self,
462       const ClutterColor *color)
463 {
464 	EContactMarkerPrivate *priv = E_CONTACT_MARKER (self)->priv;
465 	gfloat width, height;
466 
467 	if (!clutter_actor_should_pick_paint (self))
468 		return;
469 
470 	width = priv->total_width;
471 	height = priv->total_height;
472 
473 	cogl_path_new ();
474 
475 	cogl_set_source_color4ub (
476 		color->red,
477 		color->green,
478 		color->blue,
479 		color->alpha);
480 
481 	cogl_path_move_to (RADIUS, 0);
482 	cogl_path_line_to (width - RADIUS, 0);
483 	cogl_path_arc (width - RADIUS, RADIUS, RADIUS, RADIUS, -90, 0);
484 	cogl_path_line_to (width, height - RADIUS);
485 	cogl_path_arc (width - RADIUS, height - RADIUS, RADIUS, RADIUS, 0, 90);
486 	cogl_path_line_to (RADIUS, height);
487 	cogl_path_arc (RADIUS, height - RADIUS, RADIUS, RADIUS, 90, 180);
488 	cogl_path_line_to (0, RADIUS);
489 	cogl_path_arc (RADIUS, RADIUS, RADIUS, RADIUS, 180, 270);
490 	cogl_path_close ();
491 	cogl_path_fill ();
492 }
493 
494 static void
495 notify_selected (GObject *gobject,
496                  G_GNUC_UNUSED GParamSpec *pspec,
497                  G_GNUC_UNUSED gpointer user_data)
498 {
499 	queue_redraw (E_CONTACT_MARKER (gobject));
500 }
501 
502 static void
503 e_contact_marker_finalize (GObject *object)
504 {
505 	EContactMarkerPrivate *priv = E_CONTACT_MARKER (object)->priv;
506 
507 	if (priv->contact_uid) {
508 		g_free (priv->contact_uid);
509 		priv->contact_uid = NULL;
510 	}
511 
512 	if (priv->redraw_id) {
513 		g_source_remove (priv->redraw_id);
514 		priv->redraw_id = 0;
515 	}
516 
517 	G_OBJECT_CLASS (e_contact_marker_parent_class)->finalize (object);
518 }
519 
520 static void
521 e_contact_marker_dispose (GObject *object)
522 {
523 	EContactMarkerPrivate *priv = E_CONTACT_MARKER (object)->priv;
524 
525 	priv->background = NULL;
526 	priv->shadow = NULL;
527 	priv->text_actor = NULL;
528 
529 	if (priv->content_group) {
530 		clutter_actor_unparent (CLUTTER_ACTOR (priv->content_group));
531 		priv->content_group = NULL;
532 	}
533 
534 	G_OBJECT_CLASS (e_contact_marker_parent_class)->dispose (object);
535 }
536 
537 static void
538 e_contact_marker_class_init (EContactMarkerClass *class)
539 {
540 	ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (class);
541 	GObjectClass *object_class = G_OBJECT_CLASS (class);
542 
543 	g_type_class_add_private (class, sizeof (EContactMarkerPrivate));
544 
545 	object_class->dispose = e_contact_marker_dispose;
546 	object_class->finalize = e_contact_marker_finalize;
547 
548 	actor_class->paint = paint;
549 	actor_class->allocate = allocate;
550 	actor_class->map = map;
551 	actor_class->unmap = unmap;
552 	actor_class->pick = pick;
553 
554 	signals[DOUBLE_CLICKED] = g_signal_new (
555 		"double-clicked",
556 		G_TYPE_FROM_CLASS (class),
557 		G_SIGNAL_RUN_FIRST,
558 		G_STRUCT_OFFSET (EContactMarkerClass, double_clicked),
559 		NULL, NULL,
560 		g_cclosure_marshal_VOID__VOID,
561 		G_TYPE_NONE, 0);
562 }
563 
564 static void
565 e_contact_marker_init (EContactMarker *marker)
566 {
567 	EContactMarkerPrivate *priv;
568 
569 	priv = E_CONTACT_MARKER_GET_PRIVATE (marker);
570 
571 	marker->priv = priv;
572 	priv->contact_uid = NULL;
573 	priv->image = NULL;
574 	priv->background = NULL;
575 	priv->shadow = NULL;
576 	priv->text_actor = NULL;
577 	priv->content_group = CLUTTER_GROUP (clutter_group_new ());
578 	priv->redraw_id = 0;
579 
580 	clutter_actor_set_parent (
581 		CLUTTER_ACTOR (priv->content_group), CLUTTER_ACTOR (marker));
582 	clutter_actor_queue_relayout (CLUTTER_ACTOR (marker));
583 
584 	priv->total_width = 0;
585 	priv->total_height = 0;
586 
587 	g_signal_connect (
588 		marker, "notify::selected",
589 		G_CALLBACK (notify_selected), NULL);
590 	g_signal_connect (
591 		marker, "button-release-event",
592 		G_CALLBACK (contact_marker_clicked_cb), NULL);
593 }
594 
595 ClutterActor *
596 e_contact_marker_new (const gchar *name,
597                       const gchar *contact_uid,
598                       EContactPhoto *photo)
599 {
600 	ClutterActor *marker = CLUTTER_ACTOR (g_object_new (E_TYPE_CONTACT_MARKER, NULL));
601 	EContactMarkerPrivate *priv = E_CONTACT_MARKER (marker)->priv;
602 
603 	g_return_val_if_fail (name && *name, NULL);
604 	g_return_val_if_fail (contact_uid && *contact_uid, NULL);
605 
606 	champlain_label_set_text (CHAMPLAIN_LABEL (marker), name);
607 	priv->contact_uid = g_strdup (contact_uid);
608 	if (photo)
609 		priv->image = contact_photo_to_texture (photo);
610 
611 	queue_redraw (E_CONTACT_MARKER (marker));
612 
613 	return marker;
614 }
615 
616 const gchar *
617 e_contact_marker_get_contact_uid (EContactMarker *marker)
618 {
619 	g_return_val_if_fail (marker && E_IS_CONTACT_MARKER (marker), NULL);
620 
621 	return marker->priv->contact_uid;
622 }
623 
624 #endif /* WITH_CONTACT_MAPS */