1 /*  Copyright (c) 2003-2014 Xfce Development Team
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  * Boston, MA 02110-1301, USA.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 
23 #include <libxfce4ui/libxfce4ui.h>
24 
25 #include "weather-parsers.h"
26 #include "weather-data.h"
27 #include "weather.h"
28 #include "weather-summary.h"
29 #include "weather-translate.h"
30 #include "weather-icon.h"
31 
32 static gboolean
33 lnk_clicked(GtkTextTag *tag,
34             GObject *obj,
35             GdkEvent *event,
36             GtkTextIter *iter,
37             GtkWidget *textview);
38 
39 
40 #define BORDER 8
41 
42 #define APPEND_BTEXT(text)                                          \
43     gtk_text_buffer_insert_with_tags(GTK_TEXT_BUFFER(buffer),       \
44                                      &iter, text, -1, btag, NULL);
45 
46 #define APPEND_TEXT_ITEM_REAL(text)                 \
47     gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffer), \
48                            &iter, text, -1);        \
49     g_free(value);
50 
51 /*
52  * TRANSLATORS: This format string belongs to the macro used for
53  * printing the "Label: Value Unit" lines on the details tab, e.g.
54  * "Temperature: 10 °C" or "Latitude: 95.7°".
55  * The %s stand for:
56  *   - label
57  *   - ": " if label is not empty, else empty
58  *   - value
59  *   - space if unit is not degree "°" (but this is not °C or °F!)
60  *   - unit
61  * Usually, you should leave this unchanged, BUT...
62  * RTL TRANSLATORS: In case you did not translate the measurement
63  * unit, use LRM (left-to-right mark) etc. to align it properly with
64  * its numeric value.
65  */
66 #define APPEND_TEXT_ITEM(text, item)                        \
67     rawvalue = get_data(conditions, data->units, item,      \
68                         FALSE, data->night_time);           \
69     unit = get_unit(data->units, item);                     \
70     value = g_strdup_printf(_("\t%s%s%s%s%s\n"),            \
71                             text, text ? ": " : "",         \
72                             rawvalue,                       \
73                             strcmp(unit, "°") ? " " : "",   \
74                             unit);                          \
75     g_free(rawvalue);                                       \
76     APPEND_TEXT_ITEM_REAL(value);
77 
78 #define APPEND_LINK_ITEM(prefix, text, url, lnk_tag)                    \
79     gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffer),                     \
80                            &iter, prefix, -1);                          \
81     gtk_text_buffer_insert_with_tags(GTK_TEXT_BUFFER(buffer),           \
82                                      &iter, text, -1, lnk_tag, NULL);   \
83     gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffer),                     \
84                            &iter, "\n", -1);                            \
85     g_object_set_data_full(G_OBJECT(lnk_tag), "url",                    \
86                            g_strdup(url), g_free);                      \
87     g_signal_connect(G_OBJECT(lnk_tag), "event",                        \
88                      G_CALLBACK(lnk_clicked), NULL);
89 
90 #define ATTACH_DAYTIME_HEADER(title, pos)               \
91     if (data->forecast_layout == FC_LAYOUT_CALENDAR)    \
92         gtk_grid_attach                       \
93             (GTK_GRID (grid),                          \
94              add_forecast_header(title, 90.0, "darkbg"), \
95              0, pos, 1, 1);                         \
96     else                                                \
97         gtk_grid_attach                       \
98             (GTK_GRID (grid),                          \
99              add_forecast_header(title, 0.0, "darkbg"),  \
100              pos, 0, 1, 1);                         \
101 
102 #define APPEND_TOOLTIP_ITEM(description, item)                  \
103     value = get_data(fcdata, data->units, item,                 \
104                      data->round, data->night_time);            \
105     if (strcmp(value, "")) {                                    \
106         unit = get_unit(data->units, item);                     \
107         g_string_append_printf(text, description, value,        \
108                                strcmp(unit, "°") ? " " : "",    \
109                                unit);                           \
110     } else                                                      \
111         g_string_append_printf(text, description, "-", "", ""); \
112     g_free(value);
113 
114 
115 static void
weather_widget_set_border_width(GtkWidget * widget,gint border_width)116 weather_widget_set_border_width (GtkWidget *widget,
117                                  gint       border_width)
118 {
119     gtk_widget_set_margin_start (widget, border_width);
120     gtk_widget_set_margin_top (widget, border_width);
121     gtk_widget_set_margin_end (widget, border_width);
122     gtk_widget_set_margin_bottom (widget, border_width);
123 }
124 
125 
126 static gboolean
lnk_clicked(GtkTextTag * tag,GObject * obj,GdkEvent * event,GtkTextIter * iter,GtkWidget * textview)127 lnk_clicked(GtkTextTag *tag,
128             GObject *obj,
129             GdkEvent *event,
130             GtkTextIter *iter,
131             GtkWidget *textview)
132 {
133     const gchar *url;
134     gchar *str;
135 
136     if (event->type == GDK_BUTTON_RELEASE) {
137         url = g_object_get_data(G_OBJECT(tag), "url");
138         str = g_strdup_printf("exo-open --launch WebBrowser %s", url);
139         g_spawn_command_line_async(str, NULL);
140         g_free(str);
141     } else if (event->type == GDK_LEAVE_NOTIFY)
142         gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(obj),
143                                                        GTK_TEXT_WINDOW_TEXT),
144                               NULL);
145     return FALSE;
146 }
147 
148 
149 static gboolean
icon_clicked(GtkWidget * widget,GdkEventButton * event,gpointer user_data)150 icon_clicked (GtkWidget *widget,
151               GdkEventButton *event,
152               gpointer user_data)
153 {
154     return lnk_clicked(user_data, NULL, (GdkEvent *) (event), NULL, NULL);
155 }
156 
157 
158 static gboolean
view_motion_notify(GtkWidget * widget,GdkEventMotion * event,summary_details * sum)159 view_motion_notify(GtkWidget *widget,
160                    GdkEventMotion *event,
161                    summary_details *sum)
162 {
163     GtkTextIter iter;
164     GtkTextTag *tag;
165     GSList *tags;
166     GSList *cur;
167     gint bx, by;
168 
169     if (event->x != -1 && event->y != -1) {
170         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(sum->text_view),
171                                               GTK_TEXT_WINDOW_WIDGET,
172                                               event->x, event->y, &bx, &by);
173         gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(sum->text_view),
174                                            &iter, bx, by);
175         tags = gtk_text_iter_get_tags(&iter);
176         for (cur = tags; cur != NULL; cur = cur->next) {
177             tag = cur->data;
178             if (g_object_get_data(G_OBJECT(tag), "url")) {
179                 gdk_window_set_cursor
180                     (gtk_text_view_get_window(GTK_TEXT_VIEW(sum->text_view),
181                                               GTK_TEXT_WINDOW_TEXT),
182                      sum->hand_cursor);
183                 return FALSE;
184             }
185         }
186     }
187     if (!sum->on_icon)
188         gdk_window_set_cursor
189             (gtk_text_view_get_window(GTK_TEXT_VIEW(sum->text_view),
190                                       GTK_TEXT_WINDOW_TEXT),
191              sum->text_cursor);
192     return FALSE;
193 }
194 
195 
196 static gboolean
icon_motion_notify(GtkWidget * widget,GdkEventMotion * event,summary_details * sum)197 icon_motion_notify(GtkWidget *widget,
198                    GdkEventMotion *event,
199                    summary_details *sum)
200 {
201     sum->on_icon = TRUE;
202     gdk_window_set_cursor
203         (gtk_widget_get_window(widget),
204          sum->hand_cursor);
205     return FALSE;
206 }
207 
208 
209 static gboolean
view_leave_notify(GtkWidget * widget,GdkEventMotion * event,summary_details * sum)210 view_leave_notify(GtkWidget *widget,
211                   GdkEventMotion *event,
212                   summary_details *sum)
213 {
214     sum->on_icon = FALSE;
215     gdk_window_set_cursor
216         (gtk_text_view_get_window(GTK_TEXT_VIEW(sum->text_view),
217                                   GTK_TEXT_WINDOW_TEXT),
218          sum->text_cursor);
219     return FALSE;
220 }
221 
222 
223 static gchar *
get_logo_path(void)224 get_logo_path(void)
225 {
226     gchar *cache_dir, *logo_path;
227 
228     cache_dir = get_cache_directory();
229     logo_path = g_strconcat(cache_dir, G_DIR_SEPARATOR_S,
230                             "weather_logo.gif", NULL);
231     g_free(cache_dir);
232     return logo_path;
233 }
234 
235 
236 static void
logo_fetched(SoupSession * session,SoupMessage * msg,gpointer user_data)237 logo_fetched(SoupSession *session,
238              SoupMessage *msg,
239              gpointer user_data)
240 {
241     if (msg && msg->response_body && msg->response_body->length > 0) {
242         gchar *path = get_logo_path();
243         GError *error = NULL;
244         GdkPixbuf *pixbuf = NULL;
245         if (!g_file_set_contents(path, msg->response_body->data,
246                                  msg->response_body->length, &error)) {
247             g_warning(_("Error downloading met.no logo image to %s, "
248                         "reason: %s\n"), path,
249                       error ? error->message : _("unknown"));
250             g_error_free(error);
251             g_free(path);
252             return;
253         }
254         pixbuf = gdk_pixbuf_new_from_file(path, NULL);
255         g_free(path);
256         if (pixbuf) {
257             gtk_image_set_from_pixbuf(GTK_IMAGE(user_data), pixbuf);
258             g_object_unref(pixbuf);
259         }
260     }
261 }
262 
263 
264 static GtkWidget *
weather_summary_get_logo(plugin_data * data)265 weather_summary_get_logo(plugin_data *data)
266 {
267     GtkWidget *image = gtk_image_new();
268     GdkPixbuf *pixbuf;
269     gchar *path = get_logo_path();
270 
271     pixbuf = gdk_pixbuf_new_from_file(path, NULL);
272     g_free(path);
273     if (pixbuf == NULL)
274         weather_http_queue_request(data->session,
275                                    "https://www.met.no/_/asset/no.met.metno:1497355518/images/met-logo.svg",
276                                    logo_fetched, image);
277     else {
278         gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
279         g_object_unref(pixbuf);
280     }
281     return image;
282 }
283 
284 
285 static gboolean
text_view_key_pressed_cb(GtkWidget * widget,GdkEventKey * event,gpointer user_data)286 text_view_key_pressed_cb (GtkWidget   *widget,
287                           GdkEventKey *event,
288                           gpointer     user_data)
289 {
290     GtkScrolledWindow *scrolled = GTK_SCROLLED_WINDOW (user_data);
291     GtkAdjustment *adjustment = gtk_scrolled_window_get_vadjustment (scrolled);
292     gdouble current = gtk_adjustment_get_value (adjustment);
293     gdouble min = gtk_adjustment_get_lower (adjustment);
294     gdouble max = gtk_adjustment_get_upper (adjustment);
295     gdouble step_size = 0;
296 
297     switch (event->keyval) {
298         case GDK_KEY_Up:
299         case GDK_KEY_uparrow:
300             step_size = -1 * gtk_adjustment_get_step_increment (adjustment);
301             break;
302         case GDK_KEY_Down:
303         case GDK_KEY_downarrow:
304             step_size = gtk_adjustment_get_step_increment (adjustment);
305             break;
306         case GDK_KEY_Page_Up:
307             step_size = -1 * gtk_adjustment_get_page_size (adjustment);
308             break;
309         case GDK_KEY_Page_Down:
310         case GDK_KEY_space:
311             step_size = gtk_adjustment_get_page_size (adjustment);
312             break;
313         case GDK_KEY_Home:
314             step_size = -1 * current;
315             break;
316         case GDK_KEY_End:
317             step_size = max;
318             break;
319     }
320 
321     if (step_size != 0) {
322         current = current + step_size;
323         if (current < min) current = min;
324         if (current > max) current = max;
325 
326         gtk_adjustment_set_value (adjustment, current);
327 
328         return TRUE;
329     }
330 
331     return FALSE;
332 }
333 
334 
335 static GtkWidget *
create_summary_tab(plugin_data * data)336 create_summary_tab(plugin_data *data)
337 {
338     GtkTextBuffer *buffer;
339     GtkTextIter iter;
340     GtkTextTag *btag, *ltag_img, *ltag_metno, *ltag_wiki, *ltag_geonames;
341     GtkWidget *view, *frame, *scrolled, *icon, *overlay;
342     GdkRGBA lnk_color;
343     xml_time *conditions;
344     const gchar *unit;
345     gchar *value, *rawvalue, *wind;
346     gchar *last_download, *next_download;
347     gchar *interval_start, *interval_end, *point;
348     gchar *sunrise, *sunset, *moonrise, *moonset;
349     summary_details *sum;
350 
351     sum = g_slice_new0(summary_details);
352     sum->on_icon = FALSE;
353     sum->hand_cursor = gdk_cursor_new_for_display (gdk_display_get_default(), GDK_HAND2);
354     sum->text_cursor = gdk_cursor_new_for_display (gdk_display_get_default(), GDK_XTERM);
355     data->summary_details = sum;
356 
357     sum->text_view = view = gtk_text_view_new();
358     gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
359     gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE);
360     gtk_text_view_set_left_margin (GTK_TEXT_VIEW(view), 12);
361     gtk_text_view_set_top_margin (GTK_TEXT_VIEW(view), 12);
362     gtk_text_view_set_right_margin (GTK_TEXT_VIEW(view), 12);
363     gtk_text_view_set_bottom_margin (GTK_TEXT_VIEW(view), 12);
364 
365     frame = gtk_frame_new(NULL);
366     scrolled = gtk_scrolled_window_new(NULL, NULL);
367 
368     g_signal_connect(GTK_WIDGET(view), "key-press-event", G_CALLBACK(text_view_key_pressed_cb), scrolled);
369 
370     overlay = gtk_overlay_new ();
371     gtk_container_add (GTK_CONTAINER (overlay), view);
372 
373     gtk_container_add(GTK_CONTAINER(scrolled), overlay);
374     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
375                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
376 
377     gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
378     gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
379     gtk_container_add(GTK_CONTAINER(frame), scrolled);
380 
381     buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
382     gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(buffer), &iter, 0);
383     btag = gtk_text_buffer_create_tag(buffer, NULL, "weight",
384                                       PANGO_WEIGHT_BOLD, NULL);
385 
386     conditions = get_current_conditions(data->weatherdata);
387     APPEND_BTEXT(_("Coordinates\n"));
388     APPEND_TEXT_ITEM(_("Altitude"), ALTITUDE);
389     APPEND_TEXT_ITEM(_("Latitude"), LATITUDE);
390     APPEND_TEXT_ITEM(_("Longitude"), LONGITUDE);
391 
392     /* TRANSLATORS: Please use as many \t as appropriate to align the
393        date/time values as in the original. */
394     APPEND_BTEXT(_("\nDownloads\n"));
395     last_download = format_date(data->weather_update->last, NULL, TRUE);
396     next_download = format_date(data->weather_update->next, NULL, TRUE);
397     value = g_strdup_printf(_("\tWeather data:\n"
398                               "\tLast:\t%s\n"
399                               "\tNext:\t%s\n"
400                               "\tCurrent failed attempts: %d\n\n"),
401                             last_download,
402                             next_download,
403                             data->weather_update->attempt);
404     g_free(last_download);
405     g_free(next_download);
406     APPEND_TEXT_ITEM_REAL(value);
407 
408     /* Check for deprecated API and issue a warning if necessary */
409     if (data->weather_update->http_status_code == 203)
410         APPEND_BTEXT
411             (_("\tMet.no Locationforecast API states that this version\n"
412                "\tof the webservice is deprecated, and the plugin needs to be\n"
413                "\tadapted to use a newer version, or it will stop working within\n"
414                "\ta few months.\n"
415                "\tPlease file a bug on https://gitlab.xfce.org/panel-plugins/xfce4-weather-plugin/\n"
416                "\tif no one else has done so yet.\n\n"));
417 
418     last_download = format_date(data->astro_update->last, NULL, TRUE);
419     next_download = format_date(data->astro_update->next, NULL, TRUE);
420     value = g_strdup_printf(_("\tAstronomical data:\n"
421                               "\tLast:\t%s\n"
422                               "\tNext:\t%s\n"
423                               "\tCurrent failed attempts: %d\n"),
424                             last_download,
425                             next_download,
426                             data->astro_update->attempt);
427     g_free(last_download);
428     g_free(next_download);
429     APPEND_TEXT_ITEM_REAL(value);
430 
431     /* Check for deprecated sunrise API and issue a warning if necessary */
432     if (data->astro_update->http_status_code == 203)
433         APPEND_BTEXT
434             (_("\n\tMet.no sunrise API states that this version of the webservice\n"
435                "\tis deprecated, and the plugin needs to be adapted to use\n"
436                "\ta newer version, or it will stop working within a few months.\n"
437                "\tPlease file a bug on https://gitlab.xfce.org/panel-plugins/xfce4-weather-plugin/\n"
438                "\tif no one else has done so yet.\n\n"));
439 
440     /* calculation times */
441     APPEND_BTEXT(_("\nTimes Used for Calculations\n"));
442     point = format_date(conditions->point, NULL, TRUE);
443     value = g_strdup_printf
444         (_("\tTemperatures, wind, atmosphere and cloud data calculated\n"
445            "\tfor:\t\t%s\n"),
446          point);
447     g_free(point);
448     APPEND_TEXT_ITEM_REAL(value);
449 
450     interval_start = format_date(conditions->start, NULL, TRUE);
451     interval_end = format_date(conditions->end, NULL, TRUE);
452     value = g_strdup_printf
453         (_("\n\tPrecipitation and the weather symbol have been calculated\n"
454            "\tusing the following time interval:\n"
455            "\tStart:\t%s\n"
456            "\tEnd:\t%s\n"),
457          interval_start,
458          interval_end);
459     g_free(interval_end);
460     g_free(interval_start);
461     APPEND_TEXT_ITEM_REAL(value);
462 
463     /* sun and moon */
464     APPEND_BTEXT(_("\nAstronomical Data\n"));
465     if (data->current_astro) {
466         if (data->current_astro->sun_never_rises) {
467             value = g_strdup(_("\tSunrise:\t\tThe sun never rises today.\n"));
468             APPEND_TEXT_ITEM_REAL(value);
469         } else if (data->current_astro->sun_never_sets) {
470             value = g_strdup(_("\tSunset:\t\tThe sun never sets today.\n"));
471             APPEND_TEXT_ITEM_REAL(value);
472         } else {
473             sunrise = format_date(data->current_astro->sunrise, NULL, TRUE);
474             value = g_strdup_printf(_("\tSunrise:\t\t%s\n"), sunrise);
475             g_free(sunrise);
476             APPEND_TEXT_ITEM_REAL(value);
477 
478             sunset = format_date(data->current_astro->sunset, NULL, TRUE);
479             value = g_strdup_printf(_("\tSunset:\t\t%s\n\n"), sunset);
480             g_free(sunset);
481             APPEND_TEXT_ITEM_REAL(value);
482         }
483 
484         if (data->current_astro->moon_phase)
485             value = g_strdup_printf(_("\tMoon phase:\t%s\n"),
486                                     translate_moon_phase
487                                     (data->current_astro->moon_phase));
488         else
489             value = g_strdup(_("\tMoon phase:\tUnknown\n"));
490         APPEND_TEXT_ITEM_REAL(value);
491 
492         if (data->current_astro->moon_never_rises) {
493             value =
494                 g_strdup(_("\tMoonrise:\tThe moon never rises today.\n"));
495             APPEND_TEXT_ITEM_REAL(value);
496         } else if (data->current_astro->moon_never_sets) {
497             value =
498                 g_strdup(_("\tMoonset:\tThe moon never sets today.\n"));
499             APPEND_TEXT_ITEM_REAL(value);
500         } else {
501             moonrise = format_date(data->current_astro->moonrise, NULL, TRUE);
502             value = g_strdup_printf(_("\tMoonrise:\t%s\n"), moonrise);
503             g_free(moonrise);
504             APPEND_TEXT_ITEM_REAL(value);
505 
506             moonset = format_date(data->current_astro->moonset, NULL, TRUE);
507             value = g_strdup_printf(_("\tMoonset:\t%s\n"), moonset);
508             g_free(moonset);
509             APPEND_TEXT_ITEM_REAL(value);
510         }
511     } else {
512         value = g_strdup(_("\tData not available, will use sane "
513                            "default values for night and day.\n"));
514         APPEND_TEXT_ITEM_REAL(value);
515     }
516 
517     /* temperatures */
518     APPEND_BTEXT(_("\nTemperatures\n"));
519     APPEND_TEXT_ITEM(_("Temperature"), TEMPERATURE);
520     APPEND_TEXT_ITEM(_("Dew point"), DEWPOINT);
521     APPEND_TEXT_ITEM(_("Apparent temperature"), APPARENT_TEMPERATURE);
522 
523     /* wind */
524     APPEND_BTEXT(_("\nWind\n"));
525     wind = get_data(conditions, data->units, WIND_SPEED,
526                     FALSE, data->night_time);
527     rawvalue = get_data(conditions, data->units, WIND_BEAUFORT,
528                         FALSE, data->night_time);
529     value = g_strdup_printf(_("\tSpeed: %s %s (%s on the Beaufort scale)\n"),
530                             wind, get_unit(data->units, WIND_SPEED),
531                             rawvalue);
532     g_free(rawvalue);
533     g_free(wind);
534     APPEND_TEXT_ITEM_REAL(value);
535 
536     /* wind direction */
537     rawvalue = get_data(conditions, data->units, WIND_DIRECTION_DEG,
538                         FALSE, data->night_time);
539     wind = get_data(conditions, data->units, WIND_DIRECTION,
540                         FALSE, data->night_time);
541     value = g_strdup_printf(_("\tDirection: %s (%s%s)\n"),
542                             wind, rawvalue,
543                             get_unit(data->units, WIND_DIRECTION_DEG));
544     g_free(rawvalue);
545     g_free(wind);
546     APPEND_TEXT_ITEM_REAL(value);
547 
548     /* precipitation */
549     APPEND_BTEXT(_("\nPrecipitation\n"));
550     APPEND_TEXT_ITEM(_("Precipitation amount"), PRECIPITATION);
551 
552     /* atmosphere */
553     APPEND_BTEXT(_("\nAtmosphere\n"));
554     APPEND_TEXT_ITEM(_("Barometric pressure"), PRESSURE);
555     APPEND_TEXT_ITEM(_("Relative humidity"), HUMIDITY);
556 
557     /* clouds */
558     APPEND_BTEXT(_("\nClouds\n"));
559     APPEND_TEXT_ITEM(_("Fog"), FOG);
560     APPEND_TEXT_ITEM(_("Low clouds"), CLOUDS_LOW);
561     APPEND_TEXT_ITEM(_("Middle clouds"), CLOUDS_MID);
562     APPEND_TEXT_ITEM(_("High clouds"), CLOUDS_HIGH);
563     APPEND_TEXT_ITEM(_("Cloudiness"), CLOUDINESS);
564 
565     /* credits */
566     gdk_rgba_parse(&lnk_color, "#0000ff");
567     ltag_img = gtk_text_buffer_create_tag(buffer, "lnk0", "foreground-rgba",
568                                           &lnk_color, NULL);
569     ltag_metno = gtk_text_buffer_create_tag(buffer, "lnk1", "foreground-rgba",
570                                             &lnk_color, NULL);
571     ltag_wiki = gtk_text_buffer_create_tag(buffer, "lnk2", "foreground-rgba",
572                                            &lnk_color, NULL);
573     ltag_geonames = gtk_text_buffer_create_tag(buffer, "lnk3",
574                                                "foreground-rgba",
575                                                &lnk_color, NULL);
576     APPEND_BTEXT(_("\nCredits\n"));
577     APPEND_LINK_ITEM(_("\tEncyclopedic information partly taken from\n\t\t"),
578                      _("Wikipedia"), "https://wikipedia.org", ltag_wiki);
579     APPEND_LINK_ITEM(_("\n\tElevation and timezone data provided by\n\t\t"),
580                      _("GeoNames"),
581                      "https://geonames.org/", ltag_geonames);
582     APPEND_LINK_ITEM(_("\n\tWeather and astronomical data from\n\t\t"),
583                      _("The Norwegian Meteorological Institute"),
584                      "https://met.no/", ltag_metno);
585     g_object_set_data_full(G_OBJECT(ltag_img), "url",   /* url for image */
586                            g_strdup("https://met.no"), g_free);
587 
588     g_signal_connect(G_OBJECT(view), "motion-notify-event",
589                      G_CALLBACK(view_motion_notify), sum);
590     g_signal_connect(G_OBJECT(view), "leave-notify-event",
591                      G_CALLBACK(view_leave_notify), sum);
592 
593     icon = weather_summary_get_logo(data);
594 
595     if (icon) {
596         sum->icon_ebox = gtk_event_box_new();
597         gtk_event_box_set_visible_window(GTK_EVENT_BOX(sum->icon_ebox), FALSE);
598         gtk_container_add(GTK_CONTAINER(sum->icon_ebox), icon);
599 
600         gtk_widget_set_halign (GTK_WIDGET (sum->icon_ebox), GTK_ALIGN_END);
601         gtk_widget_set_valign (GTK_WIDGET (sum->icon_ebox), GTK_ALIGN_END);
602         gtk_widget_set_margin_bottom (GTK_WIDGET (sum->icon_ebox), 12);
603         gtk_widget_set_margin_end (GTK_WIDGET (sum->icon_ebox), 12);
604         gtk_overlay_add_overlay (GTK_OVERLAY (overlay), sum->icon_ebox);
605 
606         gtk_widget_show_all(sum->icon_ebox);
607         g_signal_connect(G_OBJECT(sum->icon_ebox), "button-release-event",
608                          G_CALLBACK(icon_clicked), ltag_img);
609         g_signal_connect(G_OBJECT(sum->icon_ebox), "enter-notify-event",
610                          G_CALLBACK(icon_motion_notify), sum);
611         g_signal_connect(G_OBJECT(sum->icon_ebox), "motion-notify-event",
612                          G_CALLBACK(icon_motion_notify), sum);
613         g_signal_connect(G_OBJECT(sum->icon_ebox), "leave-notify-event",
614                          G_CALLBACK(view_leave_notify), sum);
615     }
616 
617     return frame;
618 }
619 
620 
621 static gchar *
get_dayname(gint day)622 get_dayname(gint day)
623 {
624     struct tm fcday_tm;
625     time_t now_t = time(NULL), fcday_t;
626     gint weekday;
627 
628     fcday_tm = *localtime(&now_t);
629     fcday_t = time_calc_day(fcday_tm, day);
630     weekday = localtime(&fcday_t)->tm_wday;
631     switch (day) {
632     case 0:
633         return g_strdup_printf(_("Today"));
634     case 1:
635         return g_strdup_printf(_("Tomorrow"));
636     default:
637         return translate_day(weekday);
638     }
639 }
640 
641 
642 static gchar *
forecast_cell_get_tooltip_text(plugin_data * data,xml_time * fcdata)643 forecast_cell_get_tooltip_text(plugin_data *data,
644                                xml_time *fcdata)
645 {
646     GString *text;
647     gchar *result, *value;
648     const gchar *unit;
649 
650     /* TRANSLATORS: Please use spaces as needed or desired to properly
651        align the values; Monospace font is enforced with <tt> tags for
652        alignment, and the text is enclosed in <small> tags because
653        that looks much better and saves space.
654     */
655     text = g_string_new(_("<b>Times used for calculations</b>\n"));
656     value = format_date(fcdata->start, NULL, TRUE);
657     g_string_append_printf(text, _("<tt><small>"
658                                    "Interval start:       %s"
659                                    "</small></tt>\n"),
660                            value);
661     g_free(value);
662     value = format_date(fcdata->end, NULL, TRUE);
663     g_string_append_printf(text, _("<tt><small>"
664                                    "Interval end:         %s"
665                                    "</small></tt>\n"),
666                            value);
667     g_free(value);
668     value = format_date(fcdata->point, NULL, TRUE);
669     g_string_append_printf(text, _("<tt><small>"
670                                    "Data calculated for:  %s"
671                                    "</small></tt>\n\n"),
672                            value);
673     g_free(value);
674 
675     g_string_append(text, _("<b>Temperatures</b>\n"));
676     APPEND_TOOLTIP_ITEM(_("<tt><small>"
677                           "Dew point:            %s%s%s"
678                           "</small></tt>\n"),
679                         DEWPOINT);
680     APPEND_TOOLTIP_ITEM(_("<tt><small>"
681                           "Apparent temperature: %s%s%s"
682                           "</small></tt>\n\n"),
683                         APPARENT_TEMPERATURE);
684 
685     g_string_append(text, _("<b>Atmosphere</b>\n"));
686     APPEND_TOOLTIP_ITEM(_("<tt><small>"
687                           "Barometric pressure:  %s%s%s"
688                           "</small></tt>\n"),
689                         PRESSURE);
690     APPEND_TOOLTIP_ITEM(_("<tt><small>"
691                           "Relative humidity:    %s%s%s"
692                           "</small></tt>\n\n"),
693                         HUMIDITY);
694 
695     g_string_append(text, _("<b>Precipitation</b>\n"));
696     APPEND_TOOLTIP_ITEM(_("<tt><small>"
697                           "Amount:        %s%s%s"
698                           "</small></tt>\n\n"),
699                         PRECIPITATION);
700 
701     g_string_append(text, _("<b>Clouds</b>\n"));
702     /* TRANSLATORS: Clouds percentages are aligned to the right in the
703        tooltip, the %5s are needed for that and are used both for
704        rounded and unrounded values. */
705     APPEND_TOOLTIP_ITEM(_("<tt><small>"
706                           "Fog:           %5s%s%s"
707                           "</small></tt>\n"), FOG);
708     APPEND_TOOLTIP_ITEM(_("<tt><small>"
709                           "Low clouds:    %5s%s%s"
710                           "</small></tt>\n"), CLOUDS_LOW);
711     APPEND_TOOLTIP_ITEM(_("<tt><small>"
712                           "Middle clouds: %5s%s%s"
713                           "</small></tt>\n"), CLOUDS_MID);
714     APPEND_TOOLTIP_ITEM(_("<tt><small>"
715                           "High clouds:   %5s%s%s"
716                           "</small></tt>\n"), CLOUDS_HIGH);
717     APPEND_TOOLTIP_ITEM(_("<tt><small>"
718                           "Cloudiness:    %5s%s%s"
719                           "</small></tt>"), CLOUDINESS);
720 
721     /* Free GString only and return its character data */
722     result = text->str;
723     g_string_free(text, FALSE);
724     return result;
725 }
726 
727 
728 static gchar *
forecast_day_header_tooltip_text(xml_astro * astro)729 forecast_day_header_tooltip_text(xml_astro *astro)
730 {
731     GString *text;
732     gchar *result, *day, *sunrise, *sunset, *moonrise, *moonset;
733 
734     /* TRANSLATORS: Please use spaces as needed or desired to properly
735        align the values; Monospace font is enforced with <tt> tags for
736        alignment, and the text is enclosed in <small> tags because
737        that looks much better and saves space.
738     */
739 
740     text = g_string_new("");
741     if (astro) {
742         day = format_date(astro->day, "%Y-%m-%d", TRUE);
743         g_string_append_printf(text, _("<b>%s</b>\n"), day);
744         g_free(day);
745 
746         if (astro->sun_never_rises)
747             g_string_append(text, _("<tt><small>"
748                                     "Sunrise: The sun never rises this day."
749                                     "</small></tt>\n"));
750         else if (astro->sun_never_sets)
751             g_string_append(text, _("<tt><small>"
752                                     "Sunset: The sun never sets this day."
753                                     "</small></tt>\n"));
754         else {
755             sunrise = format_date(astro->sunrise, NULL, TRUE);
756             g_string_append_printf(text, _("<tt><small>"
757                                            "Sunrise: %s"
758                                            "</small></tt>\n"), sunrise);
759             g_free(sunrise);
760 
761             sunset = format_date(astro->sunset, NULL, TRUE);
762             g_string_append_printf(text, _("<tt><small>"
763                                            "Sunset:  %s"
764                                            "</small></tt>\n\n"), sunset);
765             g_free(sunset);
766         }
767 
768         if (astro->moon_phase)
769             g_string_append_printf(text, _("<tt><small>"
770                                            "Moon phase: %s"
771                                            "</small></tt>\n"),
772                                    translate_moon_phase(astro->moon_phase));
773         else
774             g_string_append(text, _("<tt><small>"
775                                     "Moon phase: Unknown"
776                                     "</small></tt>\n"));
777 
778         if (astro->moon_never_rises)
779             g_string_append(text, _("<tt><small>"
780                                     "Moonrise: The moon never rises this day."
781                                     "</small></tt>\n"));
782         else if (astro->moon_never_sets)
783             g_string_append(text,
784                             _("<tt><small>"
785                               "Moonset: The moon never sets this day."
786                               "</small></tt>\n"));
787         else {
788             moonrise = format_date(astro->moonrise, NULL, TRUE);
789             g_string_append_printf(text, _("<tt><small>"
790                                            "Moonrise: %s"
791                                            "</small></tt>\n"), moonrise);
792             g_free(moonrise);
793 
794             moonset = format_date(astro->moonset, NULL, TRUE);
795             g_string_append_printf(text, _("<tt><small>"
796                                            "Moonset:  %s"
797                                            "</small></tt>"), moonset);
798             g_free(moonset);
799         }
800     }
801 
802     /* Free GString only and return its character data */
803     result = text->str;
804     g_string_free(text, FALSE);
805     return result;
806 }
807 
808 
809 static GtkWidget *
wrap_forecast_cell(const GtkWidget * widget,const gchar * style_class)810 wrap_forecast_cell(const GtkWidget *widget,
811                    const gchar *style_class)
812 {
813     GtkWidget *ebox;
814     GtkStyleContext *ctx;
815 
816     ebox = gtk_event_box_new();
817     if (style_class == NULL)
818         gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
819     else {
820         gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), TRUE);
821         ctx = gtk_widget_get_style_context (GTK_WIDGET (ebox));
822         gtk_style_context_add_class(ctx, "forecast-cell");
823         gtk_style_context_add_class(ctx, style_class);
824     }
825     gtk_container_add(GTK_CONTAINER(ebox), GTK_WIDGET(widget));
826     return ebox;
827 }
828 
829 
830 static GtkWidget *
add_forecast_header(const gchar * text,const gdouble angle,const gchar * style_class)831 add_forecast_header(const gchar *text,
832                     const gdouble angle,
833                     const gchar *style_class)
834 {
835     GtkWidget *label;
836     gchar *str;
837 
838     label = gtk_label_new(NULL);
839     gtk_label_set_angle(GTK_LABEL(label), angle);
840     str = g_strdup_printf("<span foreground=\"white\"><b>%s</b></span>", text ? text : "");
841     gtk_label_set_markup(GTK_LABEL(label), str);
842     g_free(str);
843 
844     if (angle) {
845         gtk_widget_set_hexpand (GTK_WIDGET (label), FALSE);
846         gtk_widget_set_vexpand (GTK_WIDGET (label), TRUE);
847     } else {
848         gtk_widget_set_hexpand (GTK_WIDGET (label), TRUE);
849         gtk_widget_set_vexpand (GTK_WIDGET (label), FALSE);
850     }
851     weather_widget_set_border_width (GTK_WIDGET (label), 4);
852 
853     return wrap_forecast_cell(label, style_class);
854 }
855 
856 
857 static GtkWidget *
add_forecast_cell(plugin_data * data,GArray * daydata,gint day,gint time_of_day)858 add_forecast_cell(plugin_data *data,
859                   GArray *daydata,
860                   gint day,
861                   gint time_of_day)
862 {
863     GtkWidget *box, *label, *image;
864     GdkPixbuf *icon;
865     gchar *wind_speed, *wind_direction, *value, *rawvalue;
866     xml_time *fcdata;
867 
868     box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
869 
870     fcdata = make_forecast_data(data->weatherdata, daydata, day, time_of_day);
871     if (fcdata == NULL)
872         return box;
873 
874     if (fcdata->location == NULL) {
875         xml_time_free(fcdata);
876         return box;
877     }
878 
879     /* symbol */
880     rawvalue = get_data(fcdata, data->units, SYMBOL,
881                         FALSE, data->night_time);
882     icon = get_icon(data->icon_theme, rawvalue, 48, (time_of_day == NIGHT));
883     g_free(rawvalue);
884     image = gtk_image_new_from_pixbuf(icon);
885     gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(image), TRUE, TRUE, 0);
886     if (G_LIKELY(icon))
887         g_object_unref(G_OBJECT(icon));
888 
889     /* symbol description */
890     rawvalue = get_data(fcdata, data->units, SYMBOL,
891                         FALSE, data->night_time);
892     value = g_strdup_printf("%s",
893                             translate_desc(rawvalue, (time_of_day == NIGHT)));
894     g_free(rawvalue);
895     label = gtk_label_new(NULL);
896     gtk_label_set_markup(GTK_LABEL(label), value);
897     gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(label), TRUE, TRUE, 0);
898     g_free(value);
899 
900     /* temperature */
901     rawvalue = get_data(fcdata, data->units, TEMPERATURE,
902                         data->round, data->night_time);
903     value = g_strdup_printf("%s %s", rawvalue,
904                             get_unit(data->units, TEMPERATURE));
905     g_free(rawvalue);
906     label = gtk_label_new(value);
907     gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(label), TRUE, TRUE, 0);
908     g_free(value);
909 
910     /* wind direction and speed */
911     wind_direction = get_data(fcdata, data->units, WIND_DIRECTION,
912                               FALSE, data->night_time);
913     wind_speed = get_data(fcdata, data->units, WIND_SPEED,
914                           data->round, data->night_time);
915     value = g_strdup_printf("%s %s %s", wind_direction, wind_speed,
916                             get_unit(data->units, WIND_SPEED));
917     g_free(wind_speed);
918     g_free(wind_direction);
919     label = gtk_label_new(value);
920     gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
921     g_free(value);
922 
923     gtk_widget_set_size_request(GTK_WIDGET(box), 150, -1);
924 
925     value = forecast_cell_get_tooltip_text(data, fcdata);
926     gtk_widget_set_tooltip_markup(GTK_WIDGET(box), value);
927     g_free(value);
928 
929     xml_time_free(fcdata);
930     return box;
931 }
932 
933 
934 static GtkWidget *
make_forecast(plugin_data * data)935 make_forecast(plugin_data *data)
936 {
937     GtkWidget *grid, *ebox, *box;
938     GtkWidget *forecast_box;
939 
940     GArray *daydata;
941     xml_astro *astro;
942     gchar *dayname, *text;
943     guint i;
944     daytime time_of_day;
945 
946     GdkScreen *screen = gdk_screen_get_default ();
947     GtkCssProvider *provider = gtk_css_provider_new ();
948     gchar *css_string;
949 
950     GtkStyleContext *ctx;
951 
952     css_string = g_strdup (".forecast-cell.lightbg { background-color: rgba(0, 0, 0, 0.05); }"
953                            ".forecast-cell.darkbg { background-color: rgba(0, 0, 0, 0.6); }");
954 
955     gtk_css_provider_load_from_data (provider, css_string, -1, NULL);
956     gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
957 
958     grid = gtk_grid_new ();
959     ctx = gtk_widget_get_style_context (GTK_WIDGET (grid));
960     gtk_style_context_add_class (ctx, "background");
961 
962     gtk_grid_set_row_spacing(GTK_GRID (grid), 0);
963     gtk_grid_set_column_spacing(GTK_GRID (grid), 0);
964 
965     /* empty upper left corner */
966     box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
967     gtk_grid_attach (GTK_GRID (grid),
968                      wrap_forecast_cell(box, "darkbg"),
969                      0, 0, 1, 1);
970 
971     /* daytime headers */
972     ATTACH_DAYTIME_HEADER(_("Morning"), 1);
973     ATTACH_DAYTIME_HEADER(_("Afternoon"), 2);
974     ATTACH_DAYTIME_HEADER(_("Evening"), 3);
975     ATTACH_DAYTIME_HEADER(_("Night"), 4);
976 
977     for (i = 0; i < data->forecast_days; i++) {
978         /* forecast day headers */
979         dayname = get_dayname(i);
980         if (data->forecast_layout == FC_LAYOUT_CALENDAR)
981             ebox = add_forecast_header(dayname, 0.0, "darkbg");
982         else
983             ebox = add_forecast_header(dayname, 90.0, "darkbg");
984         g_free(dayname);
985 
986         /* add tooltip to forecast day header */
987         astro = get_astro_data_for_day(data->astrodata, i);
988         text = forecast_day_header_tooltip_text(astro);
989         gtk_widget_set_tooltip_markup(GTK_WIDGET(ebox), text);
990 
991         if (data->forecast_layout == FC_LAYOUT_CALENDAR)
992             gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET(ebox),
993                              i+1, 0, 1, 1);
994         else
995             gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET(ebox),
996                              0, i+1, 1, 1);
997 
998         /* to speed up things, first get forecast data for all daytimes */
999         daydata = get_point_data_for_day(data->weatherdata, i);
1000 
1001         /* get forecast data for each daytime */
1002         for (time_of_day = MORNING; time_of_day <= NIGHT; time_of_day++) {
1003             forecast_box = add_forecast_cell(data, daydata, i, time_of_day);
1004             weather_widget_set_border_width (GTK_WIDGET (forecast_box), 4);
1005             gtk_widget_set_hexpand (GTK_WIDGET (forecast_box), TRUE);
1006             gtk_widget_set_vexpand (GTK_WIDGET (forecast_box), TRUE);
1007 
1008             if (i % 2)
1009                 ebox = wrap_forecast_cell(forecast_box, NULL);
1010             else
1011                 ebox = wrap_forecast_cell(forecast_box, "lightbg");
1012 
1013             if (data->forecast_layout == FC_LAYOUT_CALENDAR)
1014                 gtk_grid_attach (GTK_GRID (grid),
1015                                  GTK_WIDGET(ebox),
1016                                  i+1, 1+time_of_day, 1, 1);
1017             else
1018                 gtk_grid_attach (GTK_GRID (grid),
1019                                  GTK_WIDGET(ebox),
1020                                  1+time_of_day, i+1, 1, 1);
1021         }
1022         g_array_free(daydata, FALSE);
1023     }
1024     return grid;
1025 }
1026 
1027 
1028 static GtkWidget *
create_forecast_tab(plugin_data * data)1029 create_forecast_tab(plugin_data *data)
1030 {
1031     GtkWidget *ebox, *hbox, *scrolled, *viewport, *table;
1032     GdkWindow *window;
1033     GdkMonitor *monitor;
1034     GdkRectangle rect;
1035     gint h_need, h_max, height;
1036     gint w_need, w_max, width;
1037 
1038     /* To avoid causing a GDK assertion, determine the monitor
1039      * geometry using the weather icon window, which has already been
1040      * realized in contrast to the summary window. Then calculate the
1041      * maximum height we may use, subtracting some sane value just to
1042      * be on the safe side. */
1043     window = GDK_WINDOW(gtk_widget_get_window(GTK_WIDGET(data->iconimage)));
1044     monitor = gdk_display_get_monitor_at_window(gdk_display_get_default(), window);
1045     if (G_LIKELY(window && monitor)) {
1046         gdk_monitor_get_geometry (monitor, &rect);
1047     }
1048 
1049     /* calculate maximum width and height */
1050     h_max = rect.height - 250;
1051     w_max = rect.width - 50;
1052 
1053     /* calculate needed space using a good arbitrary value */
1054     if (data->forecast_layout == FC_LAYOUT_CALENDAR) {
1055         w_need = ((data->forecast_days < 8) ? data->forecast_days : 7) * 142;
1056         h_need = 500;
1057     } else {
1058         w_need = (rect.width <= 720) ? 650 : 700;
1059         h_need = data->forecast_days * 110;
1060     }
1061 
1062     /* generate the forecast table */
1063     table = GTK_WIDGET(make_forecast(data));
1064 
1065     /* generate the containing widgets */
1066     if ((data->forecast_layout == FC_LAYOUT_CALENDAR &&
1067          w_need < w_max && data->forecast_days < 8) ||
1068         (data->forecast_layout == FC_LAYOUT_LIST && h_need < h_max)) {
1069         /* no scroll window needed, just align the contents */
1070         gtk_container_set_border_width(GTK_CONTAINER(table), 0);
1071         return table;
1072     } else {
1073         /* contents too big, scroll window needed */
1074         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
1075         gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, FALSE, 0);
1076 
1077         scrolled = gtk_scrolled_window_new (NULL, NULL);
1078         gtk_container_set_border_width(GTK_CONTAINER(scrolled), 0);
1079 
1080         viewport = gtk_viewport_new (NULL, NULL);
1081         gtk_container_add (GTK_CONTAINER (scrolled), viewport);
1082 
1083         gtk_container_add (GTK_CONTAINER (viewport), hbox);
1084         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
1085                                        GTK_POLICY_AUTOMATIC,
1086                                        GTK_POLICY_AUTOMATIC);
1087 
1088         /* set scroll window size */
1089         width = (w_need > w_max) ? w_max : w_need;
1090         height = (h_need > h_max) ? h_max : h_need;
1091         gtk_widget_set_size_request(GTK_WIDGET(scrolled), width, height);
1092 
1093         ebox = gtk_event_box_new();
1094         gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), TRUE);
1095         gtk_container_add(GTK_CONTAINER(ebox), GTK_WIDGET(scrolled));
1096         return ebox;
1097     }
1098 }
1099 
1100 
1101 static void
summary_dialog_response(const GtkWidget * dlg,const gint response,GtkWidget * window)1102 summary_dialog_response(const GtkWidget *dlg,
1103                         const gint response,
1104                         GtkWidget *window)
1105 {
1106     if (response == GTK_RESPONSE_ACCEPT)
1107         gtk_widget_destroy(window);
1108 }
1109 
1110 
1111 static void
cb_notebook_page_switched(GtkNotebook * notebook,GtkWidget * page,guint page_num,gpointer user_data)1112 cb_notebook_page_switched(GtkNotebook *notebook,
1113                           GtkWidget *page,
1114                           guint page_num,
1115                           gpointer user_data)
1116 {
1117     plugin_data *data = (plugin_data *) user_data;
1118 
1119     data->summary_remember_tab = page_num;
1120 }
1121 
1122 
1123 static gboolean
update_summary_subtitle_cb(gpointer user_data)1124 update_summary_subtitle_cb(gpointer user_data)
1125 {
1126     plugin_data *data = user_data;
1127     return update_summary_subtitle(data);
1128 }
1129 
1130 gboolean
update_summary_subtitle(plugin_data * data)1131 update_summary_subtitle(plugin_data *data)
1132 {
1133     time_t now_t;
1134     gchar *title, *date;
1135     guint update_interval;
1136     gint64 now_ms;
1137 
1138     if (data->summary_update_timer) {
1139         g_source_remove(data->summary_update_timer);
1140         data->summary_update_timer = 0;
1141     }
1142 
1143     if (G_UNLIKELY(data->location_name == NULL) ||
1144         G_UNLIKELY(data->summary_window == NULL))
1145         return FALSE;
1146 
1147     time(&now_t);
1148     date = format_date(now_t, "%A %_d %b %Y, %H:%M (%Z)", TRUE);
1149     title = g_markup_printf_escaped("<big><b>%s</b>\n%s</big>", data->location_name, date);
1150     g_free(date);
1151     gtk_label_set_markup(GTK_LABEL(data->summary_subtitle), title);
1152     g_free(title);
1153 
1154     /* compute and schedule the next update */
1155     now_ms = g_get_real_time () / 1000;
1156     update_interval = 60000 - (now_ms % 60000) + 10;
1157     data->summary_update_timer =
1158         g_timeout_add(update_interval, update_summary_subtitle_cb, data);
1159     return FALSE;
1160 }
1161 
1162 
1163 GtkWidget *
create_summary_window(plugin_data * data)1164 create_summary_window(plugin_data *data)
1165 {
1166     GtkWidget *window, *notebook, *vbox, *hbox, *label, *image;
1167     GdkPixbuf *icon;
1168     xml_time *conditions;
1169     gchar *title, *symbol;
1170 
1171     conditions = get_current_conditions(data->weatherdata);
1172     window = xfce_titled_dialog_new_with_buttons(_("Weather Report"),
1173                                                  NULL,
1174                                                  GTK_DIALOG_DESTROY_WITH_PARENT,
1175                                                  _("Close"),
1176                                                  GTK_RESPONSE_ACCEPT, NULL);
1177 
1178     data->summary_subtitle = gtk_label_new (NULL);
1179     if (G_LIKELY(data->location_name != NULL)) {
1180         title = g_markup_printf_escaped("<big><b>%s</b></big>\n", data->location_name);
1181         gtk_label_set_markup(GTK_LABEL(data->summary_subtitle), title);
1182         g_free(title);
1183     }
1184     vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1185     gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area (GTK_DIALOG(window))), vbox, TRUE, TRUE, 0);
1186 
1187     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
1188     gtk_box_pack_start(GTK_BOX (vbox), hbox, TRUE, FALSE, 6);
1189     image = gtk_image_new ();
1190     gtk_box_pack_start(GTK_BOX (hbox), image, FALSE, FALSE, 6);
1191     gtk_box_pack_start(GTK_BOX (hbox), data->summary_subtitle, FALSE, FALSE, 6);
1192 
1193     symbol = get_data(conditions, data->units, SYMBOL,
1194                       FALSE, data->night_time);
1195     icon = get_icon(data->icon_theme, symbol, 48, data->night_time);
1196     gtk_image_set_from_pixbuf (GTK_IMAGE (image), icon);
1197     g_free(symbol);
1198 
1199     gtk_window_set_icon_name(GTK_WINDOW(window), "xfce4-weather");
1200 
1201     if (G_LIKELY(icon))
1202         g_object_unref(G_OBJECT(icon));
1203 
1204     if (data->location_name == NULL || data->weatherdata == NULL ||
1205         data->weatherdata->current_conditions == NULL) {
1206         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1207         if (data->location_name == NULL)
1208             label = gtk_label_new(_("Please set a location in the plugin settings."));
1209         else
1210             label = gtk_label_new(_("Currently no data available."));
1211         gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label),
1212                            TRUE, TRUE, 0);
1213 
1214         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox),
1215                            TRUE, TRUE, 0);
1216         gtk_window_set_default_size(GTK_WINDOW(window), 500, 400);
1217     } else {
1218         notebook = gtk_notebook_new();
1219         gtk_container_set_border_width(GTK_CONTAINER(notebook), 6);
1220         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1221                                  create_forecast_tab(data),
1222                                  gtk_label_new_with_mnemonic(_("_Forecast")));
1223         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1224                                  create_summary_tab(data),
1225                                  gtk_label_new_with_mnemonic(_("_Details")));
1226         gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
1227         gtk_widget_show_all(GTK_WIDGET(notebook));
1228         gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), data->summary_remember_tab);
1229         g_signal_connect(GTK_NOTEBOOK(notebook), "switch-page",
1230                          G_CALLBACK(cb_notebook_page_switched), data);
1231     }
1232 
1233     g_signal_connect(G_OBJECT(window), "response",
1234                      G_CALLBACK(summary_dialog_response), window);
1235 
1236     return window;
1237 }
1238 
1239 
1240 void
summary_details_free(summary_details * sum)1241 summary_details_free(summary_details *sum)
1242 {
1243     g_assert(sum != NULL);
1244     if (G_UNLIKELY(sum == NULL))
1245         return;
1246 
1247     sum->icon_ebox = NULL;
1248     sum->text_view = NULL;
1249     if (sum->hand_cursor)
1250         g_object_unref (sum->hand_cursor);
1251     sum->hand_cursor = NULL;
1252     if (sum->text_cursor)
1253         g_object_unref (sum->text_cursor);
1254     sum->text_cursor = NULL;
1255 }
1256