No issues found
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 |
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 */