1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4 
5 #include <cairo.h>
6 #include <sys/time.h>
7 #include <gtk/gtk.h>
8 #include <math.h>
9 
10 #include "clock.h"
11 #include "clock-map.h"
12 #include "clock-sunpos.h"
13 #include "clock-marshallers.h"
14 
15 enum {
16 	NEED_LOCATIONS,
17 	LAST_SIGNAL
18 };
19 
20 enum {
21         MARKER_NORMAL = 0,
22         MARKER_HILIGHT,
23         MARKER_CURRENT,
24         MARKER_NB
25 };
26 
27 static char *marker_files[MARKER_NB] = {
28         "clock-map-location-marker.png",
29         "clock-map-location-hilight.png",
30         "clock-map-location-current.png"
31 };
32 
33 static guint signals[LAST_SIGNAL] = { 0 };
34 
35 typedef struct {
36         time_t last_refresh;
37 
38         gint width;
39         gint height;
40 
41 	guint highlight_timeout_id;
42 
43         GdkPixbuf *stock_map_pixbuf;
44         GdkPixbuf *location_marker_pixbuf[MARKER_NB];
45 
46         GdkPixbuf *location_map_pixbuf;
47 
48         /* The shadow itself */
49         GdkPixbuf *shadow_pixbuf;
50 
51         /* The map with the shadow composited onto it */
52         GdkPixbuf *shadow_map_pixbuf;
53 } ClockMapPrivate;
54 
55 G_DEFINE_TYPE_WITH_PRIVATE (ClockMap, clock_map, GTK_TYPE_WIDGET)
56 
57 static void clock_map_finalize (GObject *);
58 static void clock_map_size_allocate (GtkWidget *this,
59 					 GtkAllocation *allocation);
60 static gboolean clock_map_draw (GtkWidget *this,
61 				      cairo_t *cr);
62 static void clock_map_get_preferred_width (GtkWidget *widget,
63 					   gint *minimum_width,
64 					   gint *natural_width);
65 static void clock_map_get_preferred_height (GtkWidget *widget,
66 					    gint *minimum_height,
67 					    gint *natural_height);
68 static void clock_map_place_locations (ClockMap *this);
69 static void clock_map_render_shadow (ClockMap *this);
70 static void clock_map_display (ClockMap *this);
71 
72 ClockMap *
clock_map_new(void)73 clock_map_new (void)
74 {
75         ClockMap *this;
76 
77         this = g_object_new (CLOCK_MAP_TYPE, NULL);
78 
79         return this;
80 }
81 
82 static void
clock_map_class_init(ClockMapClass * this_class)83 clock_map_class_init (ClockMapClass *this_class)
84 {
85         GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class);
86         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (g_obj_class);
87 
88         g_obj_class->finalize = clock_map_finalize;
89 
90         /* GtkWidget signals */
91 
92         widget_class->size_allocate = clock_map_size_allocate;
93         widget_class->draw = clock_map_draw;
94 	widget_class->get_preferred_width = clock_map_get_preferred_width;
95 	widget_class->get_preferred_height = clock_map_get_preferred_height;
96 
97 	/**
98 	 * ClockMap::need-locations
99 	 *
100 	 * The map widget emits this signal when it needs to know which
101 	 * locations to display.
102 	 *
103 	 * Returns: the handler should return a (GSList *) of (ClockLocation *).
104 	 * The map widget will not modify this list, so the caller should keep
105 	 * it alive.
106 	 */
107 	signals[NEED_LOCATIONS] = g_signal_new ("need-locations",
108 						G_TYPE_FROM_CLASS (g_obj_class),
109 						G_SIGNAL_RUN_LAST,
110 						G_STRUCT_OFFSET (ClockMapClass, need_locations),
111 						NULL,
112 						NULL,
113 						_clock_marshal_POINTER__VOID,
114 						G_TYPE_POINTER, 0);
115 }
116 
117 static void
clock_map_init(ClockMap * this)118 clock_map_init (ClockMap *this)
119 {
120         int i;
121         ClockMapPrivate *priv = clock_map_get_instance_private (this);
122 
123         gtk_widget_set_has_window (GTK_WIDGET (this), FALSE);
124 
125 	priv->last_refresh = 0;
126 	priv->width = 0;
127 	priv->height = 0;
128 	priv->highlight_timeout_id = 0;
129         priv->stock_map_pixbuf = NULL;
130 
131         g_assert (sizeof (marker_files)/sizeof (char *) == MARKER_NB);
132 
133         for (i = 0; i < MARKER_NB; i++) {
134                 char *resource;
135 
136                 resource = g_strconcat (CLOCK_RESOURCE_PATH "icons/", marker_files[i], NULL);
137                 priv->location_marker_pixbuf[i] = gdk_pixbuf_new_from_resource (resource, NULL);
138                 g_free (resource);
139         }
140 }
141 
142 static void
clock_map_finalize(GObject * g_obj)143 clock_map_finalize (GObject *g_obj)
144 {
145         ClockMapPrivate *priv = clock_map_get_instance_private (CLOCK_MAP(g_obj));
146 	int i;
147 
148 	if (priv->highlight_timeout_id) {
149 		g_source_remove (priv->highlight_timeout_id);
150 		priv->highlight_timeout_id = 0;
151 	}
152 
153         if (priv->stock_map_pixbuf) {
154                 g_object_unref (priv->stock_map_pixbuf);
155                 priv->stock_map_pixbuf = NULL;
156         }
157 
158 	for (i = 0; i < MARKER_NB; i++) {
159         	if (priv->location_marker_pixbuf[i]) {
160                 	g_object_unref (priv->location_marker_pixbuf[i]);
161                 	priv->location_marker_pixbuf[i] = NULL;
162         	}
163 	}
164 
165         if (priv->location_map_pixbuf) {
166                 g_object_unref (priv->location_map_pixbuf);
167                 priv->location_map_pixbuf = NULL;
168         }
169 
170         if (priv->shadow_pixbuf) {
171                 g_object_unref (priv->shadow_pixbuf);
172                 priv->shadow_pixbuf = NULL;
173         }
174 
175         if (priv->shadow_map_pixbuf) {
176                 g_object_unref (priv->shadow_map_pixbuf);
177                 priv->shadow_map_pixbuf = NULL;
178         }
179 
180         G_OBJECT_CLASS (clock_map_parent_class)->finalize (g_obj);
181 }
182 
183 void
clock_map_refresh(ClockMap * this)184 clock_map_refresh (ClockMap *this)
185 {
186     ClockMapPrivate *priv = clock_map_get_instance_private (this);
187 	GtkWidget *widget = GTK_WIDGET (this);
188 	GtkAllocation allocation;
189 
190 	gtk_widget_get_allocation (widget, &allocation);
191 
192         /* Only do something if we have some space allocated.
193          * Note that 1x1 is not really some space... */
194         if (allocation.width <= 1 || allocation.height <= 1)
195                 return;
196 
197         /* Allocation changed => we reload the map */
198 	if (priv->width != allocation.width ||
199 	    priv->height != allocation.height) {
200                 if (priv->stock_map_pixbuf) {
201                         g_object_unref (priv->stock_map_pixbuf);
202                         priv->stock_map_pixbuf = NULL;
203                 }
204 
205                 priv->width = allocation.width;
206                 priv->height = allocation.height;
207         }
208 
209         if (!priv->stock_map_pixbuf) {
210                 priv->stock_map_pixbuf = gdk_pixbuf_new_from_resource_at_scale (CLOCK_RESOURCE_PATH "icons/clock-map.png",
211                                                                                 priv->width, priv->height,
212                                                                                 FALSE,
213                                                                                 NULL);
214         }
215 
216         clock_map_place_locations (this);
217 
218         clock_map_display (this);
219 }
220 
221 static gboolean
clock_map_draw(GtkWidget * this,cairo_t * cr)222 clock_map_draw (GtkWidget *this, cairo_t *cr)
223 {
224     ClockMapPrivate *priv = clock_map_get_instance_private (CLOCK_MAP(this));
225 	int width, height;
226 	GtkStyleContext *context;
227 	GdkRGBA color;
228 
229 	context = gtk_widget_get_style_context (this);
230 	gtk_style_context_get_color (context, GTK_STATE_FLAG_ACTIVE, &color);
231 
232 	if (!priv->shadow_map_pixbuf) {
233                 g_warning ("Needed to refresh the map in expose event.");
234 		clock_map_refresh (CLOCK_MAP (this));
235         }
236 
237 	width = gdk_pixbuf_get_width (priv->shadow_map_pixbuf);
238 	height = gdk_pixbuf_get_height (priv->shadow_map_pixbuf);
239 
240 	gdk_cairo_set_source_pixbuf (cr, priv->shadow_map_pixbuf, 0, 0);
241 	cairo_rectangle (cr, 0, 0, width, height);
242 	cairo_paint (cr);
243 
244         /* draw a simple outline */
245 	cairo_rectangle (cr, 0.5, 0.5, width - 1, height - 1);
246 	gdk_cairo_set_source_rgba (cr, &color);
247 
248         cairo_set_line_width (cr, 1.0);
249         cairo_stroke (cr);
250 
251 	return FALSE;
252 }
253 
254 static void
clock_map_get_preferred_width(GtkWidget * this,gint * minimum_width,gint * natural_width)255 clock_map_get_preferred_width (GtkWidget *this,
256                                 gint *minimum_width,
257                                 gint *natural_width)
258 {
259         *minimum_width = *natural_width = 250;
260 }
261 
262 static void
clock_map_get_preferred_height(GtkWidget * this,gint * minimum_height,gint * natural_height)263 clock_map_get_preferred_height (GtkWidget *this,
264                                  gint *minimum_height,
265                                  gint *natural_height)
266 {
267         *minimum_height = *natural_height = 125;
268 }
269 
270 static void
clock_map_size_allocate(GtkWidget * this,GtkAllocation * allocation)271 clock_map_size_allocate (GtkWidget *this, GtkAllocation *allocation)
272 {
273         ClockMapPrivate *priv = clock_map_get_instance_private (CLOCK_MAP(this));
274 
275 	if (GTK_WIDGET_CLASS (clock_map_parent_class)->size_allocate)
276 		GTK_WIDGET_CLASS (clock_map_parent_class)->size_allocate (this, allocation);
277 
278 	if (priv->width != allocation->width ||
279 	    priv->height != allocation->height)
280                 clock_map_refresh (CLOCK_MAP (this));
281 }
282 
283 static void
clock_map_mark(ClockMap * this,gfloat latitude,gfloat longitude,gint mark)284 clock_map_mark (ClockMap *this, gfloat latitude, gfloat longitude, gint mark)
285 {
286         ClockMapPrivate *priv = clock_map_get_instance_private (this);
287         GdkPixbuf *marker = priv->location_marker_pixbuf[mark];
288         GdkPixbuf *partial = NULL;
289 
290         int x, y;
291         int width, height;
292         int marker_width, marker_height;
293         int dest_x, dest_y, dest_width, dest_height;
294 
295         width = gdk_pixbuf_get_width (priv->location_map_pixbuf);
296         height = gdk_pixbuf_get_height (priv->location_map_pixbuf);
297 
298         x = (width / 2.0 + (width / 2.0) * longitude / 180.0);
299         y = (height / 2.0 - (height / 2.0) * latitude / 90.0);
300 
301         marker_width = gdk_pixbuf_get_width (marker);
302         marker_height = gdk_pixbuf_get_height (marker);
303 
304         dest_x = x - marker_width / 2;
305         dest_y = y - marker_height / 2;
306         dest_width = marker_width;
307         dest_height = marker_height;
308 
309         /* create a small partial pixbuf if the mark is too close to
310            the north or south pole */
311         if (dest_y < 0) {
312                 partial = gdk_pixbuf_new_subpixbuf
313                         (marker, 0, dest_y + marker_height,
314                          marker_width, -dest_y);
315 
316                 dest_y = 0.0;
317                 marker_height = gdk_pixbuf_get_height (partial);
318         } else if (dest_y + dest_height > height) {
319                 partial = gdk_pixbuf_new_subpixbuf
320                         (marker, 0, 0, marker_width, height - dest_y);
321                 marker_height = gdk_pixbuf_get_height (partial);
322         }
323 
324         if (partial) {
325                 marker = partial;
326         }
327 
328         /* handle the cases where the marker needs to be placed across
329            the 180 degree longitude line */
330         if (dest_x < 0) {
331                 /* split into our two pixbufs for the left and right edges */
332                 GdkPixbuf *lhs = NULL;
333                 GdkPixbuf *rhs = NULL;
334 
335                 lhs = gdk_pixbuf_new_subpixbuf
336                         (marker, -dest_x, 0, marker_width + dest_x, marker_height);
337 
338                 gdk_pixbuf_composite (lhs, priv->location_map_pixbuf,
339                                       0, dest_y,
340                                       gdk_pixbuf_get_width (lhs),
341                                       gdk_pixbuf_get_height (lhs),
342                                       0, dest_y,
343                                       1.0, 1.0, GDK_INTERP_NEAREST, 0xFF);
344 
345                 rhs = gdk_pixbuf_new_subpixbuf
346                         (marker, 0, 0, -dest_x, marker_height);
347 
348                 gdk_pixbuf_composite (rhs, priv->location_map_pixbuf,
349                                       width - gdk_pixbuf_get_width (rhs) - 1,
350                                       dest_y,
351                                       gdk_pixbuf_get_width (rhs),
352                                       gdk_pixbuf_get_height (rhs),
353                                       width - gdk_pixbuf_get_width (rhs) - 1,
354                                       dest_y,
355                                       1.0, 1.0, GDK_INTERP_NEAREST, 0xFF);
356 
357                 g_object_unref (lhs);
358                 g_object_unref (rhs);
359         } else if (dest_x + dest_width > width) {
360                 /* split into our two pixbufs for the left and right edges */
361                 GdkPixbuf *lhs = NULL;
362                 GdkPixbuf *rhs = NULL;
363 
364                 lhs = gdk_pixbuf_new_subpixbuf
365                         (marker, width - dest_x, 0, marker_width - width + dest_x, marker_height);
366 
367                 gdk_pixbuf_composite (lhs, priv->location_map_pixbuf,
368                                       0, dest_y,
369                                       gdk_pixbuf_get_width (lhs),
370                                       gdk_pixbuf_get_height (lhs),
371                                       0, dest_y,
372                                       1.0, 1.0, GDK_INTERP_NEAREST, 0xFF);
373 
374                 rhs = gdk_pixbuf_new_subpixbuf
375                         (marker, 0, 0, width - dest_x, marker_height);
376 
377                 gdk_pixbuf_composite (rhs, priv->location_map_pixbuf,
378                                       width - gdk_pixbuf_get_width (rhs) - 1,
379                                       dest_y,
380                                       gdk_pixbuf_get_width (rhs),
381                                       gdk_pixbuf_get_height (rhs),
382                                       width - gdk_pixbuf_get_width (rhs) - 1,
383                                       dest_y,
384                                       1.0, 1.0, GDK_INTERP_NEAREST, 0xFF);
385 
386                 g_object_unref (lhs);
387                 g_object_unref (rhs);
388         } else {
389                 gdk_pixbuf_composite (marker, priv->location_map_pixbuf,
390                                       dest_x, dest_y,
391                                       gdk_pixbuf_get_width (marker),
392                                       gdk_pixbuf_get_height (marker),
393                                       dest_x, dest_y,
394                                       1.0, 1.0, GDK_INTERP_NEAREST, 0xFF);
395         }
396 
397         if (partial != NULL) {
398                 g_object_unref (partial);
399         }
400 }
401 
402 /**
403  * Return value: %TRUE if @loc can be placed on the map, %FALSE otherwise.
404  **/
405 static gboolean
clock_map_place_location(ClockMap * this,ClockLocation * loc,gboolean hilight)406 clock_map_place_location (ClockMap *this, ClockLocation *loc, gboolean hilight)
407 {
408         gfloat latitude, longitude;
409 	gint marker;
410 
411         clock_location_get_coords (loc, &latitude, &longitude);
412         /* 0/0 means unset, basically */
413         if (latitude == 0 && longitude == 0)
414                 return FALSE;
415 
416 	if (hilight)
417 		marker = MARKER_HILIGHT;
418 	else if (clock_location_is_current (loc))
419 		marker = MARKER_CURRENT;
420 	else
421 		marker = MARKER_NORMAL;
422 
423         clock_map_mark (this, latitude, longitude, marker);
424 
425         return TRUE;
426 }
427 
428 static void
clock_map_place_locations(ClockMap * this)429 clock_map_place_locations (ClockMap *this)
430 {
431         ClockMapPrivate *priv = clock_map_get_instance_private (this);
432         GSList *locs;
433 
434         if (priv->location_map_pixbuf) {
435                 g_object_unref (priv->location_map_pixbuf);
436                 priv->location_map_pixbuf = NULL;
437         }
438 
439         priv->location_map_pixbuf = gdk_pixbuf_copy (priv->stock_map_pixbuf);
440 
441 	locs = NULL;
442 	g_signal_emit (this, signals[NEED_LOCATIONS], 0, &locs);
443 
444         while (locs) {
445                 ClockLocation *loc = CLOCK_LOCATION (locs->data);
446                 clock_map_place_location (this, loc, FALSE);
447                 locs = locs->next;
448         }
449 
450 #if 0
451         /* map_mark test suite for the edge cases */
452 
453         /* points around longitude 180 */
454         clock_map_mark (this, 0.0, 180.0);
455         clock_map_mark (this, -15.0, -178.0);
456         clock_map_mark (this, -30.0, -176.0);
457         clock_map_mark (this, 15.0, 178.0);
458         clock_map_mark (this, 30.0, 176.0);
459 
460         clock_map_mark (this, 90.0, 180.0);
461         clock_map_mark (this, -90.0, 180.0);
462 
463         /* north pole & friends */
464         clock_map_mark (this, 90.0, 0.0);
465         clock_map_mark (this, 88.0, -15.0);
466         clock_map_mark (this, 92.0, 15.0);
467 
468         /* south pole & friends */
469         clock_map_mark (this, -90.0, 0.0);
470         clock_map_mark (this, -88.0, -15.0);
471         clock_map_mark (this, -92.0, 15.0);
472 #endif
473 }
474 
475 static void
clock_map_compute_vector(gdouble lat,gdouble lon,gdouble * vec)476 clock_map_compute_vector (gdouble lat, gdouble lon, gdouble *vec)
477 {
478         gdouble lat_rad, lon_rad;
479         lat_rad = lat * (M_PI/180.0);
480         lon_rad = lon * (M_PI/180.0);
481 
482         vec[0] = sin(lon_rad) * cos(lat_rad);
483         vec[1] = sin(lat_rad);
484         vec[2] = cos(lon_rad) * cos(lat_rad);
485 }
486 
487 static guchar
clock_map_is_sunlit(gdouble pos_lat,gdouble pos_long,gdouble sun_lat,gdouble sun_long)488 clock_map_is_sunlit (gdouble pos_lat, gdouble pos_long,
489                          gdouble sun_lat, gdouble sun_long)
490 {
491         gdouble pos_vec[3];
492         gdouble sun_vec[3];
493         gdouble dot;
494 
495         /* twilight */
496         gdouble epsilon = 0.01;
497 
498         clock_map_compute_vector (pos_lat, pos_long, pos_vec);
499         clock_map_compute_vector (sun_lat, sun_long, sun_vec);
500 
501         /* compute the dot product of the two */
502         dot = pos_vec[0]*sun_vec[0] + pos_vec[1]*sun_vec[1]
503                 + pos_vec[2]*sun_vec[2];
504 
505         if (dot > epsilon) {
506                 return 0x00;
507         }
508 
509         if (dot < -epsilon) {
510                 return 0xFF;
511         }
512 
513         return (guchar)(-128 * ((dot / epsilon) - 1));
514 }
515 
516 static void
clock_map_render_shadow_pixbuf(GdkPixbuf * pixbuf)517 clock_map_render_shadow_pixbuf (GdkPixbuf *pixbuf)
518 {
519         int x, y;
520         int height, width;
521         int n_channels, rowstride;
522         guchar *pixels, *p;
523         gdouble sun_lat, sun_lon;
524         time_t now = time (NULL);
525 
526         n_channels = gdk_pixbuf_get_n_channels (pixbuf);
527         rowstride = gdk_pixbuf_get_rowstride (pixbuf);
528         pixels = gdk_pixbuf_get_pixels (pixbuf);
529 
530         width = gdk_pixbuf_get_width (pixbuf);
531         height = gdk_pixbuf_get_height (pixbuf);
532 
533         sun_position (now, &sun_lat, &sun_lon);
534 
535         for (y = 0; y < height; y++) {
536                 gdouble lat = (height / 2.0 - y) / (height / 2.0) * 90.0;
537 
538                 for (x = 0; x < width; x++) {
539                         guchar shade;
540                         gdouble lon =
541                                 (x - width / 2.0) / (width / 2.0) * 180.0;
542 
543                         shade = clock_map_is_sunlit (lat, lon,
544                                                          sun_lat, sun_lon);
545 
546                         p = pixels + y * rowstride + x * n_channels;
547                         p[3] = shade;
548                 }
549         }
550 }
551 
552 static void
clock_map_render_shadow(ClockMap * this)553 clock_map_render_shadow (ClockMap *this)
554 {
555         ClockMapPrivate *priv = clock_map_get_instance_private (this);
556 
557         if (priv->shadow_pixbuf) {
558                 g_object_unref (priv->shadow_pixbuf);
559         }
560 
561         priv->shadow_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
562                                               priv->width, priv->height);
563 
564         /* Initialize to all shadow */
565         gdk_pixbuf_fill (priv->shadow_pixbuf, 0x6d9ccdff);
566 
567         clock_map_render_shadow_pixbuf (priv->shadow_pixbuf);
568 
569         if (priv->shadow_map_pixbuf) {
570                 g_object_unref (priv->shadow_map_pixbuf);
571         }
572 
573         priv->shadow_map_pixbuf = gdk_pixbuf_copy (priv->location_map_pixbuf);
574 
575         gdk_pixbuf_composite (priv->shadow_pixbuf, priv->shadow_map_pixbuf,
576                               0, 0, priv->width, priv->height,
577                               0, 0, 1, 1, GDK_INTERP_NEAREST, 0x66);
578 }
579 
580 static void
clock_map_display(ClockMap * this)581 clock_map_display (ClockMap *this)
582 {
583         ClockMapPrivate *priv = clock_map_get_instance_private (this);
584 
585         if (priv->width > 0 || priv->height > 0)
586                 clock_map_render_shadow (this);
587 	gtk_widget_queue_draw (GTK_WIDGET (this));
588 
589         time (&priv->last_refresh);
590 }
591 
592 typedef struct {
593        ClockMap *map;
594        ClockLocation *location;
595        int count;
596 } BlinkData;
597 
598 static gboolean
highlight(gpointer user_data)599 highlight (gpointer user_data)
600 {
601        BlinkData *data = user_data;
602 
603        if (data->count == 6)
604                return FALSE;
605 
606        if (data->count % 2 == 0) {
607                  if (!clock_map_place_location (data->map,
608                                                 data->location, TRUE))
609                          return FALSE;
610        } else
611                  clock_map_place_locations (data->map);
612        clock_map_display (data->map);
613 
614        data->count++;
615 
616        return TRUE;
617 }
618 
619 static void
highlight_destroy(gpointer user_data)620 highlight_destroy (gpointer user_data)
621 {
622        BlinkData *data = user_data;
623        ClockMapPrivate *priv;
624 
625        priv = clock_map_get_instance_private (data->map);
626        priv->highlight_timeout_id = 0;
627 
628        g_object_unref (data->location);
629        g_free (data);
630 }
631 
632 void
clock_map_blink_location(ClockMap * this,ClockLocation * loc)633 clock_map_blink_location (ClockMap *this, ClockLocation *loc)
634 {
635        BlinkData *data;
636        ClockMapPrivate *priv;
637 
638        priv = clock_map_get_instance_private (this);
639 
640        g_return_if_fail (IS_CLOCK_MAP (this));
641        g_return_if_fail (IS_CLOCK_LOCATION (loc));
642 
643        data = g_new0 (BlinkData, 1);
644        data->map = this;
645        data->location = g_object_ref (loc);
646 
647        if (priv->highlight_timeout_id) {
648 	       g_source_remove (priv->highlight_timeout_id);
649 	       clock_map_place_locations (this);
650        }
651 
652        highlight (data);
653 
654        priv->highlight_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
655 							300, highlight, data,
656 							highlight_destroy);
657 }
658 
659 static gboolean
clock_map_needs_refresh(ClockMap * this)660 clock_map_needs_refresh (ClockMap *this)
661 {
662         ClockMapPrivate *priv = clock_map_get_instance_private (this);
663         time_t now_t;
664 
665         time (&now_t);
666 
667 	/* refresh once per minute */
668 	return (ABS (now_t - priv->last_refresh) >= 60);
669 }
670 
671 void
clock_map_update_time(ClockMap * this)672 clock_map_update_time (ClockMap *this)
673 {
674 
675 	g_return_if_fail (IS_CLOCK_MAP (this));
676 
677         if (!clock_map_needs_refresh (this)) {
678                 return;
679         }
680 
681         clock_map_display (this);
682 }
683