1 /*
2  * Copyright (C) 2011-2013 Jiri Techet <techet@gmail.com>
3  *
4  * This library 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.1 of the License, or (at your option) any later version.
8  *
9  * This library 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 this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 /**
20  * SECTION:champlain-license
21  * @short_description: An actor that displays license text.
22  *
23  * An actor that displays license text.
24  */
25 
26 #include "config.h"
27 
28 #include "champlain-license.h"
29 #include "champlain-defines.h"
30 #include "champlain-private.h"
31 #include "champlain-enum-types.h"
32 #include "champlain-view.h"
33 
34 #include <clutter/clutter.h>
35 #include <glib.h>
36 #include <glib-object.h>
37 #include <cairo.h>
38 #include <math.h>
39 #include <string.h>
40 
41 
42 enum
43 {
44   /* normal signals */
45   LAST_SIGNAL
46 };
47 
48 enum
49 {
50   PROP_0,
51   PROP_LICENSE_EXTRA,
52   PROP_ALIGNMENT,
53 };
54 
55 /* static guint champlain_license_signals[LAST_SIGNAL] = { 0, }; */
56 
57 struct _ChamplainLicensePrivate
58 {
59   gchar *extra_text; /* Extra license text */
60   ClutterActor *license_actor;
61   PangoAlignment alignment;
62 
63   ChamplainView *view;
64 };
65 
G_DEFINE_TYPE_WITH_PRIVATE(ChamplainLicense,champlain_license,CLUTTER_TYPE_ACTOR)66 G_DEFINE_TYPE_WITH_PRIVATE (ChamplainLicense, champlain_license, CLUTTER_TYPE_ACTOR)
67 
68 #define WIDTH_PADDING 10
69 #define HEIGHT_PADDING 7
70 
71 
72 static void
73 champlain_license_get_property (GObject *object,
74     guint prop_id,
75     GValue *value,
76     GParamSpec *pspec)
77 {
78   ChamplainLicensePrivate *priv = CHAMPLAIN_LICENSE (object)->priv;
79 
80   switch (prop_id)
81     {
82     case PROP_LICENSE_EXTRA:
83       g_value_set_string (value, priv->extra_text);
84       break;
85 
86     case PROP_ALIGNMENT:
87       g_value_set_enum (value, priv->alignment);
88       break;
89 
90     default:
91       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
92     }
93 }
94 
95 
96 static void
champlain_license_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)97 champlain_license_set_property (GObject *object,
98     guint prop_id,
99     const GValue *value,
100     GParamSpec *pspec)
101 {
102   ChamplainLicense *license = CHAMPLAIN_LICENSE (object);
103 
104   switch (prop_id)
105     {
106     case PROP_LICENSE_EXTRA:
107       champlain_license_set_extra_text (license, g_value_get_string (value));
108       break;
109 
110     case PROP_ALIGNMENT:
111       champlain_license_set_alignment (license, g_value_get_enum (value));
112       break;
113 
114     default:
115       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
116     }
117 }
118 
119 
120 static void
redraw_license(ChamplainLicense * license)121 redraw_license (ChamplainLicense *license)
122 {
123   ChamplainLicensePrivate *priv = license->priv;
124   gchar *text;
125   gfloat width, height;
126   ChamplainMapSource *map_source;
127   GList *overlay_sources, *iter;
128 
129   if (!priv->view)
130     return;
131 
132   map_source = champlain_view_get_map_source (priv->view);
133 
134   if (!map_source)
135     return;
136 
137   if (priv->extra_text)
138     text = g_strjoin ("\n",
139           priv->extra_text,
140           champlain_map_source_get_license (map_source),
141           NULL);
142   else
143     text = g_strdup (champlain_map_source_get_license (map_source));
144 
145   overlay_sources = champlain_view_get_overlay_sources (priv->view);
146   for (iter = overlay_sources; iter; iter = iter->next)
147     {
148       ChamplainMapSource *map_source = iter->data;
149       const gchar *overlay_license = champlain_map_source_get_license (map_source);
150 
151       if (g_strrstr (text, overlay_license) == NULL)
152         {
153           gchar *old_text = text;
154           text = g_strjoin ("\n",
155                 text,
156                 champlain_map_source_get_license (map_source),
157                 NULL);
158           g_free (old_text);
159         }
160     }
161   g_list_free (overlay_sources);
162 
163   clutter_text_set_text (CLUTTER_TEXT (priv->license_actor), text);
164   clutter_actor_get_size (priv->license_actor, &width, &height);
165   clutter_actor_set_size (CLUTTER_ACTOR (license), width + 2 * WIDTH_PADDING, height + 2 * HEIGHT_PADDING);
166   clutter_actor_set_position (priv->license_actor, WIDTH_PADDING, HEIGHT_PADDING);
167 
168   g_free (text);
169 }
170 
171 
172 static void
redraw_license_cb(G_GNUC_UNUSED GObject * gobject,G_GNUC_UNUSED GParamSpec * arg1,ChamplainLicense * license)173 redraw_license_cb (G_GNUC_UNUSED GObject *gobject,
174     G_GNUC_UNUSED GParamSpec *arg1,
175     ChamplainLicense *license)
176 {
177   redraw_license (license);
178 }
179 
180 
181 static void
champlain_license_dispose(GObject * object)182 champlain_license_dispose (GObject *object)
183 {
184   ChamplainLicensePrivate *priv = CHAMPLAIN_LICENSE (object)->priv;
185 
186   priv->license_actor = NULL;
187 
188   if (priv->view)
189     {
190       champlain_license_disconnect_view (CHAMPLAIN_LICENSE (object));
191       priv->view = NULL;
192     }
193 
194   G_OBJECT_CLASS (champlain_license_parent_class)->dispose (object);
195 }
196 
197 
198 static void
champlain_license_finalize(GObject * object)199 champlain_license_finalize (GObject *object)
200 {
201   ChamplainLicensePrivate *priv = CHAMPLAIN_LICENSE (object)->priv;
202 
203   g_free (priv->extra_text);
204 
205   G_OBJECT_CLASS (champlain_license_parent_class)->finalize (object);
206 }
207 
208 
209 static void
champlain_license_class_init(ChamplainLicenseClass * klass)210 champlain_license_class_init (ChamplainLicenseClass *klass)
211 {
212   GObjectClass *object_class = G_OBJECT_CLASS (klass);
213 
214   object_class->finalize = champlain_license_finalize;
215   object_class->dispose = champlain_license_dispose;
216   object_class->get_property = champlain_license_get_property;
217   object_class->set_property = champlain_license_set_property;
218 
219   /**
220    * ChamplainLicense:extra-text:
221    *
222    * Sets additional text to be displayed in the license area.  The map's
223    * license will be added below it. Your text can have multiple lines, just use
224    * "\n" in between.
225    *
226    * Since: 0.10
227    */
228   g_object_class_install_property (object_class,
229       PROP_LICENSE_EXTRA,
230       g_param_spec_string ("extra-text",
231           "Additional license",
232           "Additional license text",
233           "",
234           CHAMPLAIN_PARAM_READWRITE));
235 
236   /**
237    * ChamplainLicense:alignment:
238    *
239    * The license's alignment
240    *
241    * Since: 0.10
242    */
243   g_object_class_install_property (object_class,
244       PROP_ALIGNMENT,
245       g_param_spec_enum ("alignment",
246           "Alignment",
247           "The license's alignment",
248           PANGO_TYPE_ALIGNMENT,
249           PANGO_ALIGN_LEFT,
250           CHAMPLAIN_PARAM_READWRITE));
251 }
252 
253 
254 static void
champlain_license_init(ChamplainLicense * license)255 champlain_license_init (ChamplainLicense *license)
256 {
257   ChamplainLicensePrivate *priv = champlain_license_get_instance_private (license);
258 
259   license->priv = priv;
260   priv->extra_text = NULL;
261   priv->view = NULL;
262   priv->alignment = PANGO_ALIGN_RIGHT;
263 
264   priv->license_actor = clutter_text_new ();
265   clutter_text_set_font_name (CLUTTER_TEXT (priv->license_actor), "sans 8");
266   clutter_text_set_line_alignment (CLUTTER_TEXT (priv->license_actor), priv->alignment);
267   clutter_actor_set_opacity (priv->license_actor, 128);
268   clutter_actor_add_child (CLUTTER_ACTOR (license), priv->license_actor);
269 }
270 
271 
272 /**
273  * champlain_license_new:
274  *
275  * Creates an instance of #ChamplainLicense.
276  *
277  * Returns: a new #ChamplainLicense.
278  *
279  * Since: 0.10
280  */
281 ClutterActor *
champlain_license_new(void)282 champlain_license_new (void)
283 {
284   return CLUTTER_ACTOR (g_object_new (CHAMPLAIN_TYPE_LICENSE, NULL));
285 }
286 
287 
288 /**
289  * champlain_license_connect_view:
290  * @license: The license
291  * @view: a #ChamplainView
292  *
293  * This method connects to the necessary signals of #ChamplainView to make the
294  * license change automatically when the map source changes.
295  *
296  * Since: 0.10
297  */
298 void
champlain_license_connect_view(ChamplainLicense * license,ChamplainView * view)299 champlain_license_connect_view (ChamplainLicense *license,
300     ChamplainView *view)
301 {
302   g_return_if_fail (CHAMPLAIN_IS_LICENSE (license));
303 
304   license->priv->view = g_object_ref (view);
305 
306   g_signal_connect (view, "notify::map-source",
307       G_CALLBACK (redraw_license_cb), license);
308   g_signal_connect (view, "notify::width",
309       G_CALLBACK (redraw_license_cb), license);
310   g_signal_connect (view, "notify::height",
311       G_CALLBACK (redraw_license_cb), license);
312   redraw_license (license);
313 }
314 
315 
316 /**
317  * champlain_license_disconnect_view:
318  * @license: The license
319  *
320  * This method disconnects from the signals previously connected by champlain_license_connect_view().
321  *
322  * Since: 0.10
323  */
324 void
champlain_license_disconnect_view(ChamplainLicense * license)325 champlain_license_disconnect_view (ChamplainLicense *license)
326 {
327   g_return_if_fail (CHAMPLAIN_IS_LICENSE (license));
328 
329   g_signal_handlers_disconnect_by_func (license->priv->view,
330       redraw_license_cb,
331       license);
332   g_object_unref (license->priv->view);
333   license->priv->view = NULL;
334 }
335 
336 
337 /**
338  * champlain_license_set_extra_text:
339  * @license: a #ChamplainLicense
340  * @text: the additional license text
341  *
342  * Show the additional license text on the map view.  The text will preceed the
343  * map's licence when displayed. Use "\n" to separate the lines.
344  *
345  * Since: 0.10
346  */
347 void
champlain_license_set_extra_text(ChamplainLicense * license,const gchar * text)348 champlain_license_set_extra_text (ChamplainLicense *license,
349     const gchar *text)
350 {
351   g_return_if_fail (CHAMPLAIN_IS_LICENSE (license));
352 
353   ChamplainLicensePrivate *priv = license->priv;
354 
355   if (priv->extra_text)
356     g_free (priv->extra_text);
357 
358   priv->extra_text = g_strdup (text);
359   g_object_notify (G_OBJECT (license), "extra-text");
360   redraw_license (license);
361 }
362 
363 
364 /**
365  * champlain_license_get_extra_text:
366  * @license: a #ChamplainLicense
367  *
368  * Gets the additional license text.
369  *
370  * Returns: the additional license text
371  *
372  * Since: 0.10
373  */
374 const gchar *
champlain_license_get_extra_text(ChamplainLicense * license)375 champlain_license_get_extra_text (ChamplainLicense *license)
376 {
377   g_return_val_if_fail (CHAMPLAIN_IS_LICENSE (license), FALSE);
378 
379   return license->priv->extra_text;
380 }
381 
382 
383 /**
384  * champlain_license_set_alignment:
385  * @license: a #ChamplainLicense
386  * @alignment: The license's text alignment
387  *
388  * Set the license's text alignment.
389  *
390  * Since: 0.10
391  */
392 void
champlain_license_set_alignment(ChamplainLicense * license,PangoAlignment alignment)393 champlain_license_set_alignment (ChamplainLicense *license,
394     PangoAlignment alignment)
395 {
396   g_return_if_fail (CHAMPLAIN_IS_LICENSE (license));
397 
398   license->priv->alignment = alignment;
399   clutter_text_set_line_alignment (CLUTTER_TEXT (license->priv->license_actor), alignment);
400   g_object_notify (G_OBJECT (license), "alignment");
401 }
402 
403 
404 /**
405  * champlain_license_get_alignment:
406  * @license: The license
407  *
408  * Get the license's text alignment.
409  *
410  * Returns: the license's text alignment.
411  *
412  * Since: 0.10
413  */
414 PangoAlignment
champlain_license_get_alignment(ChamplainLicense * license)415 champlain_license_get_alignment (ChamplainLicense *license)
416 {
417   g_return_val_if_fail (CHAMPLAIN_IS_LICENSE (license), FALSE);
418 
419   return license->priv->alignment;
420 }
421