weather-summary.c 44 KB
Newer Older
Harald Judt's avatar
Harald Judt committed
1
/*  Copyright (c) 2003-2014 Xfce Development Team
2
 *
3 4 5 6
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
7
 *
8 9 10 11
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
12
 *
13 14 15 16
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
17 18 19
 */

#ifdef HAVE_CONFIG_H
20
#include <config.h>
21 22
#endif

23
#include <libxfce4ui/libxfce4ui.h>
24

Nick Schermer's avatar
Nick Schermer committed
25 26 27 28 29 30
#include "weather-parsers.h"
#include "weather-data.h"
#include "weather.h"
#include "weather-summary.h"
#include "weather-translate.h"
#include "weather-icon.h"
31

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
static gboolean
lnk_clicked(GtkTextTag *tag,
            GObject *obj,
            GdkEvent *event,
            GtkTextIter *iter,
            GtkWidget *textview);


#define BORDER 8

#define APPEND_BTEXT(text)                                          \
    gtk_text_buffer_insert_with_tags(GTK_TEXT_BUFFER(buffer),       \
                                     &iter, text, -1, btag, NULL);

#define APPEND_TEXT_ITEM_REAL(text)                 \
    gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffer), \
                           &iter, text, -1);        \
    g_free(value);

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
/*
 * TRANSLATORS: This format string belongs to the macro used for
 * printing the "Label: Value Unit" lines on the details tab, e.g.
 * "Temperature: 10 °C" or "Latitude: 95.7°".
 * The %s stand for:
 *   - label
 *   - ": " if label is not empty, else empty
 *   - value
 *   - space if unit is not degree "°" (but this is not °C or °F!)
 *   - unit
 * Usually, you should leave this unchanged, BUT...
 * RTL TRANSLATORS: In case you did not translate the measurement
 * unit, use LRM (left-to-right mark) etc. to align it properly with
 * its numeric value.
 */
Harald Judt's avatar
Harald Judt committed
66 67 68 69 70 71 72 73 74 75
#define APPEND_TEXT_ITEM(text, item)                        \
    rawvalue = get_data(conditions, data->units, item,      \
                        FALSE, data->night_time);           \
    unit = get_unit(data->units, item);                     \
    value = g_strdup_printf(_("\t%s%s%s%s%s\n"),            \
                            text, text ? ": " : "",         \
                            rawvalue,                       \
                            strcmp(unit, "°") ? " " : "",   \
                            unit);                          \
    g_free(rawvalue);                                       \
76 77 78 79 80 81 82 83 84
    APPEND_TEXT_ITEM_REAL(value);

#define APPEND_LINK_ITEM(prefix, text, url, lnk_tag)                    \
    gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffer),                     \
                           &iter, prefix, -1);                          \
    gtk_text_buffer_insert_with_tags(GTK_TEXT_BUFFER(buffer),           \
                                     &iter, text, -1, lnk_tag, NULL);   \
    gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffer),                     \
                           &iter, "\n", -1);                            \
Harald Judt's avatar
Harald Judt committed
85 86
    g_object_set_data_full(G_OBJECT(lnk_tag), "url",                    \
                           g_strdup(url), g_free);                      \
87 88
    g_signal_connect(G_OBJECT(lnk_tag), "event",                        \
                     G_CALLBACK(lnk_clicked), NULL);
89

Harald Judt's avatar
Harald Judt committed
90 91
#define ATTACH_DAYTIME_HEADER(title, pos)               \
    if (data->forecast_layout == FC_LAYOUT_CALENDAR)    \
Sean Davis's avatar
Sean Davis committed
92
        gtk_grid_attach                       \
Sean Davis's avatar
Sean Davis committed
93
            (GTK_GRID (grid),                          \
94
             add_forecast_header(title, 90.0, "darkbg"), \
Sean Davis's avatar
Sean Davis committed
95
             0, pos, 1, 1);                         \
Harald Judt's avatar
Harald Judt committed
96
    else                                                \
Sean Davis's avatar
Sean Davis committed
97
        gtk_grid_attach                       \
Sean Davis's avatar
Sean Davis committed
98
            (GTK_GRID (grid),                          \
99
             add_forecast_header(title, 0.0, "darkbg"),  \
Sean Davis's avatar
Sean Davis committed
100
             pos, 0, 1, 1);                         \
Harald Judt's avatar
Harald Judt committed
101

102
#define APPEND_TOOLTIP_ITEM(description, item)                  \
103 104
    value = get_data(fcdata, data->units, item,                 \
                     data->round, data->night_time);            \
105 106 107 108 109 110 111
    if (strcmp(value, "")) {                                    \
        unit = get_unit(data->units, item);                     \
        g_string_append_printf(text, description, value,        \
                               strcmp(unit, "°") ? " " : "",    \
                               unit);                           \
    } else                                                      \
        g_string_append_printf(text, description, "-", "", ""); \
112 113
    g_free(value);

114

Sean Davis's avatar
Sean Davis committed
115 116 117 118 119 120 121 122 123 124 125
static void
weather_widget_set_border_width (GtkWidget *widget,
                                 gint       border_width)
{
    gtk_widget_set_margin_start (widget, border_width);
    gtk_widget_set_margin_top (widget, border_width);
    gtk_widget_set_margin_end (widget, border_width);
    gtk_widget_set_margin_bottom (widget, border_width);
}


126 127 128 129 130 131
static gboolean
lnk_clicked(GtkTextTag *tag,
            GObject *obj,
            GdkEvent *event,
            GtkTextIter *iter,
            GtkWidget *textview)
132
{
133 134
    const gchar *url;
    gchar *str;
135

136 137 138 139 140 141 142 143 144 145
    if (event->type == GDK_BUTTON_RELEASE) {
        url = g_object_get_data(G_OBJECT(tag), "url");
        str = g_strdup_printf("exo-open --launch WebBrowser %s", url);
        g_spawn_command_line_async(str, NULL);
        g_free(str);
    } else if (event->type == GDK_LEAVE_NOTIFY)
        gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(obj),
                                                       GTK_TEXT_WINDOW_TEXT),
                              NULL);
    return FALSE;
146 147
}

148

149
static gboolean
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 159 160

static gboolean
view_motion_notify(GtkWidget *widget,
                   GdkEventMotion *event,
Harald Judt's avatar
Harald Judt committed
161
                   summary_details *sum)
162
{
Harald Judt's avatar
Harald Judt committed
163 164 165 166 167
    GtkTextIter iter;
    GtkTextTag *tag;
    GSList *tags;
    GSList *cur;
    gint bx, by;
168

Harald Judt's avatar
Harald Judt committed
169
    if (event->x != -1 && event->y != -1) {
Harald Judt's avatar
Harald Judt committed
170
        gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(sum->text_view),
171 172
                                              GTK_TEXT_WINDOW_WIDGET,
                                              event->x, event->y, &bx, &by);
Harald Judt's avatar
Harald Judt committed
173
        gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(sum->text_view),
174 175 176
                                           &iter, bx, by);
        tags = gtk_text_iter_get_tags(&iter);
        for (cur = tags; cur != NULL; cur = cur->next) {
Harald Judt's avatar
Harald Judt committed
177
            tag = cur->data;
178
            if (g_object_get_data(G_OBJECT(tag), "url")) {
Harald Judt's avatar
Harald Judt committed
179 180 181 182
                gdk_window_set_cursor
                    (gtk_text_view_get_window(GTK_TEXT_VIEW(sum->text_view),
                                              GTK_TEXT_WINDOW_TEXT),
                     sum->hand_cursor);
183 184 185
                return FALSE;
            }
        }
186
    }
Harald Judt's avatar
Harald Judt committed
187 188 189 190 191
    if (!sum->on_icon)
        gdk_window_set_cursor
            (gtk_text_view_get_window(GTK_TEXT_VIEW(sum->text_view),
                                      GTK_TEXT_WINDOW_TEXT),
             sum->text_cursor);
192
    return FALSE;
193 194
}

195 196 197 198

static gboolean
icon_motion_notify(GtkWidget *widget,
                   GdkEventMotion *event,
Harald Judt's avatar
Harald Judt committed
199
                   summary_details *sum)
200
{
Harald Judt's avatar
Harald Judt committed
201 202
    sum->on_icon = TRUE;
    gdk_window_set_cursor
203
        (gtk_widget_get_window(widget),
Harald Judt's avatar
Harald Judt committed
204
         sum->hand_cursor);
205
    return FALSE;
206 207
}

208 209 210 211

static gboolean
view_leave_notify(GtkWidget *widget,
                  GdkEventMotion *event,
Harald Judt's avatar
Harald Judt committed
212
                  summary_details *sum)
213
{
Harald Judt's avatar
Harald Judt committed
214 215 216 217 218
    sum->on_icon = FALSE;
    gdk_window_set_cursor
        (gtk_text_view_get_window(GTK_TEXT_VIEW(sum->text_view),
                                  GTK_TEXT_WINDOW_TEXT),
         sum->text_cursor);
219
    return FALSE;
220 221
}

222 223 224

static gchar *
get_logo_path(void)
225
{
226 227 228 229 230 231 232
    gchar *cache_dir, *logo_path;

    cache_dir = get_cache_directory();
    logo_path = g_strconcat(cache_dir, G_DIR_SEPARATOR_S,
                            "weather_logo.gif", NULL);
    g_free(cache_dir);
    return logo_path;
233 234
}

235

236
static void
237 238 239
logo_fetched(SoupSession *session,
             SoupMessage *msg,
             gpointer user_data)
240
{
241
    if (msg && msg->response_body && msg->response_body->length > 0) {
242 243 244
        gchar *path = get_logo_path();
        GError *error = NULL;
        GdkPixbuf *pixbuf = NULL;
245 246
        if (!g_file_set_contents(path, msg->response_body->data,
                                 msg->response_body->length, &error)) {
247 248 249
            g_warning(_("Error downloading met.no logo image to %s, "
                        "reason: %s\n"), path,
                      error ? error->message : _("unknown"));
250 251 252 253 254 255 256 257 258 259 260
            g_error_free(error);
            g_free(path);
            return;
        }
        pixbuf = gdk_pixbuf_new_from_file(path, NULL);
        g_free(path);
        if (pixbuf) {
            gtk_image_set_from_pixbuf(GTK_IMAGE(user_data), pixbuf);
            g_object_unref(pixbuf);
        }
    }
261 262
}

263 264

static GtkWidget *
265
weather_summary_get_logo(plugin_data *data)
266
{
267
    GtkWidget *image = gtk_image_new();
Harald Judt's avatar
Harald Judt committed
268
    GdkPixbuf *pixbuf;
269 270 271 272 273
    gchar *path = get_logo_path();

    pixbuf = gdk_pixbuf_new_from_file(path, NULL);
    g_free(path);
    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 278 279 280 281
    else {
        gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
        g_object_unref(pixbuf);
    }
    return image;
282
}
283

284

285
static GtkWidget *
286
create_summary_tab(plugin_data *data)
287
{
288 289
    GtkTextBuffer *buffer;
    GtkTextIter iter;
290
    GtkTextTag *btag, *ltag_img, *ltag_metno, *ltag_wiki, *ltag_geonames;
291
    GtkWidget *view, *frame, *scrolled, *icon, *overlay;
292
    GdkRGBA lnk_color;
293 294
    xml_time *conditions;
    const gchar *unit;
Harald Judt's avatar
Harald Judt committed
295
    gchar *value, *rawvalue, *wind;
296 297
    gchar *last_download, *next_download;
    gchar *interval_start, *interval_end, *point;
298
    gchar *sunrise, *sunset, *moonrise, *moonset;
Harald Judt's avatar
Harald Judt committed
299
    summary_details *sum;
300

Harald Judt's avatar
Harald Judt committed
301 302
    sum = g_slice_new0(summary_details);
    sum->on_icon = FALSE;
Sean Davis's avatar
Sean Davis committed
303 304
    sum->hand_cursor = gdk_cursor_new_for_display (gdk_display_get_default(), GDK_HAND2);
    sum->text_cursor = gdk_cursor_new_for_display (gdk_display_get_default(), GDK_XTERM);
Harald Judt's avatar
Harald Judt committed
305 306 307
    data->summary_details = sum;

    sum->text_view = view = gtk_text_view_new();
308 309
    gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
    gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE);
310 311 312 313
    gtk_text_view_set_left_margin (GTK_TEXT_VIEW(view), 12);
    gtk_text_view_set_top_margin (GTK_TEXT_VIEW(view), 12);
    gtk_text_view_set_right_margin (GTK_TEXT_VIEW(view), 12);
    gtk_text_view_set_bottom_margin (GTK_TEXT_VIEW(view), 12);
314 315 316
    frame = gtk_frame_new(NULL);
    scrolled = gtk_scrolled_window_new(NULL, NULL);

317 318 319 320
    overlay = gtk_overlay_new ();
    gtk_container_add (GTK_CONTAINER (overlay), view);

    gtk_container_add(GTK_CONTAINER(scrolled), overlay);
321 322 323
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

324
    gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
325 326 327 328 329 330 331 332 333 334 335 336 337 338
    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
    gtk_container_add(GTK_CONTAINER(frame), scrolled);

    buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
    gtk_text_buffer_get_iter_at_offset(GTK_TEXT_BUFFER(buffer), &iter, 0);
    btag = gtk_text_buffer_create_tag(buffer, NULL, "weight",
                                      PANGO_WEIGHT_BOLD, NULL);

    conditions = get_current_conditions(data->weatherdata);
    APPEND_BTEXT(_("Coordinates\n"));
    APPEND_TEXT_ITEM(_("Altitude"), ALTITUDE);
    APPEND_TEXT_ITEM(_("Latitude"), LATITUDE);
    APPEND_TEXT_ITEM(_("Longitude"), LONGITUDE);

339 340
    /* TRANSLATORS: Please use as many \t as appropriate to align the
       date/time values as in the original. */
341 342 343 344 345 346 347 348 349 350 351 352
    APPEND_BTEXT(_("\nDownloads\n"));
    last_download = format_date(data->weather_update->last, NULL, TRUE);
    next_download = format_date(data->weather_update->next, NULL, TRUE);
    value = g_strdup_printf(_("\tWeather data:\n"
                              "\tLast:\t%s\n"
                              "\tNext:\t%s\n"
                              "\tCurrent failed attempts: %d\n\n"),
                            last_download,
                            next_download,
                            data->weather_update->attempt);
    g_free(last_download);
    g_free(next_download);
353 354
    APPEND_TEXT_ITEM_REAL(value);

355 356 357 358 359 360 361 362 363 364
    /* Check for deprecated API and issue a warning if necessary */
    if (data->weather_update->http_status_code == 203)
        APPEND_BTEXT
            (_("\tMet.no LocationforecastLTS API states that this version\n"
               "\tof the webservice is deprecated, and the plugin needs to be\n"
               "\tadapted to use a newer version, or it will stop working within\n"
               "\ta few months.\n"
               "\tPlease file a bug on https://bugzilla.xfce.org if no one\n"
               "\telse has done so yet.\n\n"));

365 366 367 368 369 370 371 372 373 374 375 376 377
    last_download = format_date(data->astro_update->last, NULL, TRUE);
    next_download = format_date(data->astro_update->next, NULL, TRUE);
    value = g_strdup_printf(_("\tAstronomical data:\n"
                              "\tLast:\t%s\n"
                              "\tNext:\t%s\n"
                              "\tCurrent failed attempts: %d\n"),
                            last_download,
                            next_download,
                            data->astro_update->attempt);
    g_free(last_download);
    g_free(next_download);
    APPEND_TEXT_ITEM_REAL(value);

378 379 380 381 382 383 384 385 386
    /* Check for deprecated sunrise API and issue a warning if necessary */
    if (data->astro_update->http_status_code == 203)
        APPEND_BTEXT
            (_("\n\tMet.no sunrise API states that this version of the webservice\n"
               "\tis deprecated, and the plugin needs to be adapted to use\n"
               "\ta newer version, or it will stop working within a few months.\n"
               "\tPlease file a bug on https://bugzilla.xfce.org if no one\n"
               "\telse has done so yet.\n"));

387
    /* calculation times */
388
    APPEND_BTEXT(_("\nTimes Used for Calculations\n"));
389
    point = format_date(conditions->point, NULL, TRUE);
390
    value = g_strdup_printf
Harald Judt's avatar
Harald Judt committed
391
        (_("\tTemperatures, wind, atmosphere and cloud data calculated\n"
392
           "\tfor:\t\t%s\n"),
393
         point);
394
    g_free(point);
395 396
    APPEND_TEXT_ITEM_REAL(value);

397 398
    interval_start = format_date(conditions->start, NULL, TRUE);
    interval_end = format_date(conditions->end, NULL, TRUE);
399
    value = g_strdup_printf
Harald Judt's avatar
Harald Judt committed
400
        (_("\n\tPrecipitation and the weather symbol have been calculated\n"
401
           "\tusing the following time interval:\n"
402 403
           "\tStart:\t%s\n"
           "\tEnd:\t%s\n"),
404 405
         interval_start,
         interval_end);
406 407
    g_free(interval_end);
    g_free(interval_start);
408 409
    APPEND_TEXT_ITEM_REAL(value);

410
    /* sun and moon */
411
    APPEND_BTEXT(_("\nAstronomical Data\n"));
412 413
    if (data->current_astro) {
        if (data->current_astro->sun_never_rises) {
414 415
            value = g_strdup(_("\tSunrise:\t\tThe sun never rises today.\n"));
            APPEND_TEXT_ITEM_REAL(value);
416
        } else if (data->current_astro->sun_never_sets) {
417 418 419
            value = g_strdup(_("\tSunset:\t\tThe sun never sets today.\n"));
            APPEND_TEXT_ITEM_REAL(value);
        } else {
420
            sunrise = format_date(data->current_astro->sunrise, NULL, TRUE);
421
            value = g_strdup_printf(_("\tSunrise:\t\t%s\n"), sunrise);
422
            g_free(sunrise);
423 424
            APPEND_TEXT_ITEM_REAL(value);

425
            sunset = format_date(data->current_astro->sunset, NULL, TRUE);
426
            value = g_strdup_printf(_("\tSunset:\t\t%s\n\n"), sunset);
427
            g_free(sunset);
428 429 430
            APPEND_TEXT_ITEM_REAL(value);
        }

431
        if (data->current_astro->moon_phase)
432 433
            value = g_strdup_printf(_("\tMoon phase:\t%s\n"),
                                    translate_moon_phase
434
                                    (data->current_astro->moon_phase));
435 436 437 438
        else
            value = g_strdup(_("\tMoon phase:\tUnknown\n"));
        APPEND_TEXT_ITEM_REAL(value);

439
        if (data->current_astro->moon_never_rises) {
440 441
            value =
                g_strdup(_("\tMoonrise:\tThe moon never rises today.\n"));
442
            APPEND_TEXT_ITEM_REAL(value);
443
        } else if (data->current_astro->moon_never_sets) {
444 445
            value =
                g_strdup(_("\tMoonset:\tThe moon never sets today.\n"));
446 447
            APPEND_TEXT_ITEM_REAL(value);
        } else {
448
            moonrise = format_date(data->current_astro->moonrise, NULL, TRUE);
449
            value = g_strdup_printf(_("\tMoonrise:\t%s\n"), moonrise);
450
            g_free(moonrise);
451 452
            APPEND_TEXT_ITEM_REAL(value);

453
            moonset = format_date(data->current_astro->moonset, NULL, TRUE);
454
            value = g_strdup_printf(_("\tMoonset:\t%s\n"), moonset);
455
            g_free(moonset);
456 457 458 459 460 461 462 463
            APPEND_TEXT_ITEM_REAL(value);
        }
    } else {
        value = g_strdup(_("\tData not available, will use sane "
                           "default values for night and day.\n"));
        APPEND_TEXT_ITEM_REAL(value);
    }

Harald Judt's avatar
Harald Judt committed
464 465
    /* temperatures */
    APPEND_BTEXT(_("\nTemperatures\n"));
466
    APPEND_TEXT_ITEM(_("Temperature"), TEMPERATURE);
Harald Judt's avatar
Harald Judt committed
467
    APPEND_TEXT_ITEM(_("Dew point"), DEWPOINT);
468
    APPEND_TEXT_ITEM(_("Apparent temperature"), APPARENT_TEMPERATURE);
469 470 471

    /* wind */
    APPEND_BTEXT(_("\nWind\n"));
472 473 474 475
    wind = get_data(conditions, data->units, WIND_SPEED,
                    FALSE, data->night_time);
    rawvalue = get_data(conditions, data->units, WIND_BEAUFORT,
                        FALSE, data->night_time);
476 477
    value = g_strdup_printf(_("\tSpeed: %s %s (%s on the Beaufort scale)\n"),
                            wind, get_unit(data->units, WIND_SPEED),
478
                            rawvalue);
479 480 481 482
    g_free(rawvalue);
    g_free(wind);
    APPEND_TEXT_ITEM_REAL(value);

483
    /* wind direction */
484 485
    rawvalue = get_data(conditions, data->units, WIND_DIRECTION_DEG,
                        FALSE, data->night_time);
486 487
    wind = get_data(conditions, data->units, WIND_DIRECTION,
                        FALSE, data->night_time);
488
    value = g_strdup_printf(_("\tDirection: %s (%s%s)\n"),
489
                            wind, rawvalue,
490
                            get_unit(data->units, WIND_DIRECTION_DEG));
491 492 493 494 495
    g_free(rawvalue);
    g_free(wind);
    APPEND_TEXT_ITEM_REAL(value);

    /* precipitation */
496 497
    APPEND_BTEXT(_("\nPrecipitation\n"));
    APPEND_TEXT_ITEM(_("Precipitation amount"), PRECIPITATION);
498 499 500

    /* atmosphere */
    APPEND_BTEXT(_("\nAtmosphere\n"));
501 502
    APPEND_TEXT_ITEM(_("Barometric pressure"), PRESSURE);
    APPEND_TEXT_ITEM(_("Relative humidity"), HUMIDITY);
503 504 505 506 507

    /* clouds */
    APPEND_BTEXT(_("\nClouds\n"));
    APPEND_TEXT_ITEM(_("Fog"), FOG);
    APPEND_TEXT_ITEM(_("Low clouds"), CLOUDS_LOW);
508
    APPEND_TEXT_ITEM(_("Middle clouds"), CLOUDS_MID);
509 510 511
    APPEND_TEXT_ITEM(_("High clouds"), CLOUDS_HIGH);
    APPEND_TEXT_ITEM(_("Cloudiness"), CLOUDINESS);

Harald Judt's avatar
Harald Judt committed
512
    /* credits */
513 514
    gdk_rgba_parse(&lnk_color, "#0000ff");
    ltag_img = gtk_text_buffer_create_tag(buffer, "lnk0", "foreground-rgba",
515
                                          &lnk_color, NULL);
516
    ltag_metno = gtk_text_buffer_create_tag(buffer, "lnk1", "foreground-rgba",
517
                                            &lnk_color, NULL);
518
    ltag_wiki = gtk_text_buffer_create_tag(buffer, "lnk2", "foreground-rgba",
519 520
                                           &lnk_color, NULL);
    ltag_geonames = gtk_text_buffer_create_tag(buffer, "lnk3",
521
                                               "foreground-rgba",
522
                                               &lnk_color, NULL);
Harald Judt's avatar
Harald Judt committed
523 524
    APPEND_BTEXT(_("\nCredits\n"));
    APPEND_LINK_ITEM(_("\tEncyclopedic information partly taken from\n\t\t"),
525
                     _("Wikipedia"), "https://wikipedia.org", ltag_wiki);
526 527
    APPEND_LINK_ITEM(_("\n\tElevation and timezone data provided by\n\t\t"),
                     _("GeoNames"),
528
                     "https://geonames.org/", ltag_geonames);
Harald Judt's avatar
Harald Judt committed
529 530
    APPEND_LINK_ITEM(_("\n\tWeather and astronomical data from\n\t\t"),
                     _("The Norwegian Meteorological Institute"),
531
                     "https://met.no/", ltag_metno);
532
    g_object_set_data_full(G_OBJECT(ltag_img), "url",   /* url for image */
533
                           g_strdup("https://met.no"), g_free);
534 535

    g_signal_connect(G_OBJECT(view), "motion-notify-event",
Harald Judt's avatar
Harald Judt committed
536
                     G_CALLBACK(view_motion_notify), sum);
537
    g_signal_connect(G_OBJECT(view), "leave-notify-event",
Harald Judt's avatar
Harald Judt committed
538
                     G_CALLBACK(view_leave_notify), sum);
539

Harald Judt's avatar
Harald Judt committed
540
    icon = weather_summary_get_logo(data);
541

Harald Judt's avatar
Harald Judt committed
542 543
    if (icon) {
        sum->icon_ebox = gtk_event_box_new();
544 545
        gtk_event_box_set_visible_window(GTK_EVENT_BOX(sum->icon_ebox), FALSE);
        gtk_container_add(GTK_CONTAINER(sum->icon_ebox), icon);
546 547 548 549 550 551 552

        gtk_widget_set_halign (GTK_WIDGET (sum->icon_ebox), GTK_ALIGN_END);
        gtk_widget_set_valign (GTK_WIDGET (sum->icon_ebox), GTK_ALIGN_END);
        gtk_widget_set_margin_bottom (GTK_WIDGET (sum->icon_ebox), 12);
        gtk_widget_set_margin_end (GTK_WIDGET (sum->icon_ebox), 12);
        gtk_overlay_add_overlay (GTK_OVERLAY (overlay), sum->icon_ebox);

Harald Judt's avatar
Harald Judt committed
553 554
        gtk_widget_show_all(sum->icon_ebox);
        g_signal_connect(G_OBJECT(sum->icon_ebox), "button-release-event",
555
                         G_CALLBACK(icon_clicked), ltag_img);
Harald Judt's avatar
Harald Judt committed
556 557 558 559 560 561
        g_signal_connect(G_OBJECT(sum->icon_ebox), "enter-notify-event",
                         G_CALLBACK(icon_motion_notify), sum);
        g_signal_connect(G_OBJECT(sum->icon_ebox), "motion-notify-event",
                         G_CALLBACK(icon_motion_notify), sum);
        g_signal_connect(G_OBJECT(sum->icon_ebox), "leave-notify-event",
                         G_CALLBACK(view_leave_notify), sum);
562
    }
Harald Judt's avatar
Harald Judt committed
563

564
    return frame;
565
}
566

567

Harald Judt's avatar
Harald Judt committed
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
static gchar *
get_dayname(gint day)
{
    struct tm fcday_tm;
    time_t now_t = time(NULL), fcday_t;
    gint weekday;

    fcday_tm = *localtime(&now_t);
    fcday_t = time_calc_day(fcday_tm, day);
    weekday = localtime(&fcday_t)->tm_wday;
    switch (day) {
    case 0:
        return g_strdup_printf(_("Today"));
    case 1:
        return g_strdup_printf(_("Tomorrow"));
    default:
        return translate_day(weekday);
    }
}


589 590 591 592 593 594 595 596
static gchar *
forecast_cell_get_tooltip_text(plugin_data *data,
                               xml_time *fcdata)
{
    GString *text;
    gchar *result, *value;
    const gchar *unit;

597 598 599 600
    /* TRANSLATORS: Please use spaces as needed or desired to properly
       align the values; Monospace font is enforced with <tt> tags for
       alignment, and the text is enclosed in <small> tags because
       that looks much better and saves space.
Harald Judt's avatar
Harald Judt committed
601
    */
602
    text = g_string_new(_("<b>Times used for calculations</b>\n"));
603
    value = format_date(fcdata->start, NULL, TRUE);
604 605 606 607
    g_string_append_printf(text, _("<tt><small>"
                                   "Interval start:       %s"
                                   "</small></tt>\n"),
                           value);
608
    g_free(value);
609
    value = format_date(fcdata->end, NULL, TRUE);
610 611 612 613
    g_string_append_printf(text, _("<tt><small>"
                                   "Interval end:         %s"
                                   "</small></tt>\n"),
                           value);
614
    g_free(value);
615
    value = format_date(fcdata->point, NULL, TRUE);
616 617 618 619
    g_string_append_printf(text, _("<tt><small>"
                                   "Data calculated for:  %s"
                                   "</small></tt>\n\n"),
                           value);
620 621
    g_free(value);

Harald Judt's avatar
Harald Judt committed
622
    g_string_append(text, _("<b>Temperatures</b>\n"));
623 624 625 626 627 628 629
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "Dew point:            %s%s%s"
                          "</small></tt>\n"),
                        DEWPOINT);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "Apparent temperature: %s%s%s"
                          "</small></tt>\n\n"),
630
                        APPARENT_TEMPERATURE);
Harald Judt's avatar
Harald Judt committed
631

632
    g_string_append(text, _("<b>Atmosphere</b>\n"));
633 634 635 636 637 638 639 640
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "Barometric pressure:  %s%s%s"
                          "</small></tt>\n"),
                        PRESSURE);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "Relative humidity:    %s%s%s"
                          "</small></tt>\n\n"),
                        HUMIDITY);
641

642
    g_string_append(text, _("<b>Precipitation</b>\n"));
643 644 645
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "Amount:        %s%s%s"
                          "</small></tt>\n\n"),
646
                        PRECIPITATION);
647 648

    g_string_append(text, _("<b>Clouds</b>\n"));
649 650 651
    /* TRANSLATORS: Clouds percentages are aligned to the right in the
       tooltip, the %5s are needed for that and are used both for
       rounded and unrounded values. */
652
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
653
                          "Fog:           %5s%s%s"
654 655
                          "</small></tt>\n"), FOG);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
656
                          "Low clouds:    %5s%s%s"
657 658
                          "</small></tt>\n"), CLOUDS_LOW);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
659
                          "Middle clouds: %5s%s%s"
660 661
                          "</small></tt>\n"), CLOUDS_MID);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
662
                          "High clouds:   %5s%s%s"
663 664
                          "</small></tt>\n"), CLOUDS_HIGH);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
665
                          "Cloudiness:    %5s%s%s"
666
                          "</small></tt>"), CLOUDINESS);
667 668 669 670 671 672 673 674

    /* Free GString only and return its character data */
    result = text->str;
    g_string_free(text, FALSE);
    return result;
}


675 676 677 678
static gchar *
forecast_day_header_tooltip_text(xml_astro *astro)
{
    GString *text;
Harald Judt's avatar
Harald Judt committed
679
    gchar *result, *day, *sunrise, *sunset, *moonrise, *moonset;
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701

    /* TRANSLATORS: Please use spaces as needed or desired to properly
       align the values; Monospace font is enforced with <tt> tags for
       alignment, and the text is enclosed in <small> tags because
       that looks much better and saves space.
    */

    text = g_string_new("");
    if (astro) {
        day = format_date(astro->day, "%Y-%m-%d", TRUE);
        g_string_append_printf(text, _("<b>%s</b>\n"), day);
        g_free(day);

        if (astro->sun_never_rises)
            g_string_append(text, _("<tt><small>"
                                    "Sunrise: The sun never rises this day."
                                    "</small></tt>\n"));
        else if (astro->sun_never_sets)
            g_string_append(text, _("<tt><small>"
                                    "Sunset: The sun never sets this day."
                                    "</small></tt>\n"));
        else {
702
            sunrise = format_date(astro->sunrise, NULL, TRUE);
703 704 705 706 707
            g_string_append_printf(text, _("<tt><small>"
                                           "Sunrise: %s"
                                           "</small></tt>\n"), sunrise);
            g_free(sunrise);

708
            sunset = format_date(astro->sunset, NULL, TRUE);
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
            g_string_append_printf(text, _("<tt><small>"
                                           "Sunset:  %s"
                                           "</small></tt>\n\n"), sunset);
            g_free(sunset);
        }

        if (astro->moon_phase)
            g_string_append_printf(text, _("<tt><small>"
                                           "Moon phase: %s"
                                           "</small></tt>\n"),
                                   translate_moon_phase(astro->moon_phase));
        else
            g_string_append(text, _("<tt><small>"
                                    "Moon phase: Unknown"
                                    "</small></tt>\n"));

        if (astro->moon_never_rises)
            g_string_append(text, _("<tt><small>"
                                    "Moonrise: The moon never rises this day."
                                    "</small></tt>\n"));
        else if (astro->moon_never_sets)
            g_string_append(text,
                            _("<tt><small>"
                              "Moonset: The moon never sets this day."
                              "</small></tt>\n"));
        else {
735
            moonrise = format_date(astro->moonrise, NULL, TRUE);
736 737 738 739 740
            g_string_append_printf(text, _("<tt><small>"
                                           "Moonrise: %s"
                                           "</small></tt>\n"), moonrise);
            g_free(moonrise);

741
            moonset = format_date(astro->moonset, NULL, TRUE);
742 743 744 745 746 747 748 749 750 751 752 753 754 755
            g_string_append_printf(text, _("<tt><small>"
                                           "Moonset:  %s"
                                           "</small></tt>"), moonset);
            g_free(moonset);
        }
    }

    /* Free GString only and return its character data */
    result = text->str;
    g_string_free(text, FALSE);
    return result;
}


756
static GtkWidget *
Harald Judt's avatar
Harald Judt committed
757
wrap_forecast_cell(const GtkWidget *widget,
758
                   const gchar *style_class)
759
{
760
    GtkWidget *ebox;
761
    GtkStyleContext *ctx;
Harald Judt's avatar
Harald Judt committed
762

763
    ebox = gtk_event_box_new();
764
    if (style_class == NULL)
765 766 767
        gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
    else {
        gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), TRUE);
768 769 770
        ctx = gtk_widget_get_style_context (GTK_WIDGET (ebox));
        gtk_style_context_add_class(ctx, "forecast-cell");
        gtk_style_context_add_class(ctx, style_class);
771 772 773 774 775
    }
    gtk_container_add(GTK_CONTAINER(ebox), GTK_WIDGET(widget));
    return ebox;
}

776

777
static GtkWidget *
778 779
add_forecast_header(const gchar *text,
                    const gdouble angle,
780
                    const gchar *style_class)
781
{
Sean Davis's avatar
Sean Davis committed
782
    GtkWidget *label;
783 784 785
    gchar *str;

    label = gtk_label_new(NULL);
Harald Judt's avatar
Harald Judt committed
786
    gtk_label_set_angle(GTK_LABEL(label), angle);
787 788 789
    str = g_strdup_printf("<span foreground=\"white\"><b>%s</b></span>", text ? text : "");
    gtk_label_set_markup(GTK_LABEL(label), str);
    g_free(str);
Sean Davis's avatar
Sean Davis committed
790 791 792 793 794 795 796 797 798 799

    if (angle) {
        gtk_widget_set_hexpand (GTK_WIDGET (label), FALSE);
        gtk_widget_set_vexpand (GTK_WIDGET (label), TRUE);
    } else {
        gtk_widget_set_hexpand (GTK_WIDGET (label), TRUE);
        gtk_widget_set_vexpand (GTK_WIDGET (label), FALSE);
    }
    weather_widget_set_border_width (GTK_WIDGET (label), 4);

800
    return wrap_forecast_cell(label, style_class);
801 802
}

803

804
static GtkWidget *
805
add_forecast_cell(plugin_data *data,
806
                  GArray *daydata,
Harald Judt's avatar
Harald Judt committed
807
                  gint day,
808
                  gint time_of_day)
809
{
Harald Judt's avatar
Harald Judt committed
810
    GtkWidget *box, *label, *image;
811
    GdkPixbuf *icon;
Harald Judt's avatar
Harald Judt committed
812 813 814
    gchar *wind_speed, *wind_direction, *value, *rawvalue;
    xml_time *fcdata;

815
    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
Harald Judt's avatar
Harald Judt committed
816

817
    fcdata = make_forecast_data(data->weatherdata, daydata, day, time_of_day);
Harald Judt's avatar
Harald Judt committed
818 819 820 821 822 823 824 825 826
    if (fcdata == NULL)
        return box;

    if (fcdata->location == NULL) {
        xml_time_free(fcdata);
        return box;
    }

    /* symbol */
827 828
    rawvalue = get_data(fcdata, data->units, SYMBOL,
                        FALSE, data->night_time);
829
    icon = get_icon(data->icon_theme, rawvalue, 48, (time_of_day == NIGHT));
Harald Judt's avatar
Harald Judt committed
830 831 832 833 834 835 836
    g_free(rawvalue);
    image = gtk_image_new_from_pixbuf(icon);
    gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(image), TRUE, TRUE, 0);
    if (G_LIKELY(icon))
        g_object_unref(G_OBJECT(icon));

    /* symbol description */
837 838
    rawvalue = get_data(fcdata, data->units, SYMBOL,
                        FALSE, data->night_time);
Harald Judt's avatar
Harald Judt committed
839
    value = g_strdup_printf("%s",
840
                            translate_desc(rawvalue, (time_of_day == NIGHT)));
Harald Judt's avatar
Harald Judt committed
841 842 843 844 845 846 847
    g_free(rawvalue);
    label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label), value);
    gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(label), TRUE, TRUE, 0);
    g_free(value);

    /* temperature */
848 849
    rawvalue = get_data(fcdata, data->units, TEMPERATURE,
                        data->round, data->night_time);
Harald Judt's avatar
Harald Judt committed
850 851 852 853 854 855 856 857
    value = g_strdup_printf("%s %s", rawvalue,
                            get_unit(data->units, TEMPERATURE));
    g_free(rawvalue);
    label = gtk_label_new(value);
    gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(label), TRUE, TRUE, 0);
    g_free(value);

    /* wind direction and speed */
858 859
    wind_direction = get_data(fcdata, data->units, WIND_DIRECTION,
                              FALSE, data->night_time);
860 861
    wind_speed = get_data(fcdata, data->units, WIND_SPEED,
                          data->round, data->night_time);
Harald Judt's avatar
Harald Judt committed
862 863 864 865 866 867 868 869 870 871
    value = g_strdup_printf("%s %s %s", wind_direction, wind_speed,
                            get_unit(data->units, WIND_SPEED));
    g_free(wind_speed);
    g_free(wind_direction);
    label = gtk_label_new(value);
    gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
    g_free(value);

    gtk_widget_set_size_request(GTK_WIDGET(box), 150, -1);

872 873 874 875
    value = forecast_cell_get_tooltip_text(data, fcdata);
    gtk_widget_set_tooltip_markup(GTK_WIDGET(box), value);
    g_free(value);

Harald Judt's avatar
Harald Judt committed
876 877 878 879 880 881
    xml_time_free(fcdata);
    return box;
}


static GtkWidget *
882
make_forecast(plugin_data *data)
Harald Judt's avatar
Harald Judt committed
883
{
Sean Davis's avatar
Sean Davis committed
884
    GtkWidget *grid, *ebox, *box;
Harald Judt's avatar
Harald Judt committed
885
    GtkWidget *forecast_box;
886

887
    GArray *daydata;
888 889
    xml_astro *astro;
    gchar *dayname, *text;
890 891
    guint i;
    daytime time_of_day;
Harald Judt's avatar
Harald Judt committed
892

893 894 895 896
    GdkScreen *screen = gdk_screen_get_default ();
    GtkCssProvider *provider = gtk_css_provider_new ();
    gchar *css_string;

897 898
    css_string = g_strdup (".forecast-cell.lightbg { background-color: rgba(0, 0, 0, 0.2); }"
                           ".forecast-cell.darkbg { background-color: rgba(0, 0, 0, 0.4); }");
899 900 901 902

    gtk_css_provider_load_from_data (provider, css_string, -1, NULL);
    gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);

Sean Davis's avatar
Sean Davis committed
903
    grid = gtk_grid_new ();
904

Sean Davis's avatar
Sean Davis committed
905 906
    gtk_grid_set_row_spacing(GTK_GRID (grid), 0);
    gtk_grid_set_column_spacing(GTK_GRID (grid), 0);
907 908

    /* empty upper left corner */
909
    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
Sean Davis's avatar
Sean Davis committed
910
    gtk_grid_attach (GTK_GRID (grid),
911
                     wrap_forecast_cell(box, "darkbg"),
Sean Davis's avatar
Sean Davis committed
912
                     0, 0, 1, 1);
913 914

    /* daytime headers */
Harald Judt's avatar
Harald Judt committed
915 916 917 918
    ATTACH_DAYTIME_HEADER(_("Morning"), 1);
    ATTACH_DAYTIME_HEADER(_("Afternoon"), 2);
    ATTACH_DAYTIME_HEADER(_("Evening"), 3);
    ATTACH_DAYTIME_HEADER(_("Night"), 4);
919

920
    for (i = 0; i < data->forecast_days; i++) {
921
        /* forecast day headers */
Harald Judt's avatar
Harald Judt committed
922 923
        dayname = get_dayname(i);
        if (data->forecast_layout == FC_LAYOUT_CALENDAR)
924
            ebox = add_forecast_header(dayname, 0.0, "darkbg");
925
        else
926
            ebox = add_forecast_header(dayname, 90.0, "darkbg");
Harald Judt's avatar
Harald Judt committed
927
        g_free(dayname);
928

929 930 931 932 933
        /* add tooltip to forecast day header */
        astro = get_astro_data_for_day(data->astrodata, i);
        text = forecast_day_header_tooltip_text(astro);
        gtk_widget_set_tooltip_markup(GTK_WIDGET(ebox), text);

Harald Judt's avatar
Harald Judt committed
934
        if (data->forecast_layout == FC_LAYOUT_CALENDAR)
Sean Davis's avatar
Sean Davis committed
935
            gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET(ebox),
Sean Davis's avatar
Sean Davis committed
936
                             i+1, 0, 1, 1);
Harald Judt's avatar
Harald Judt committed
937
        else
Sean Davis's avatar
Sean Davis committed
938
            gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET(ebox),
Sean Davis's avatar
Sean Davis committed
939
                             0, i+1, 1, 1);
940

941 942 943
        /* to speed up things, first get forecast data for all daytimes */
        daydata = get_point_data_for_day(data->weatherdata, i);

944
        /* get forecast data for each daytime */
945 946
        for (time_of_day = MORNING; time_of_day <= NIGHT; time_of_day++) {
            forecast_box = add_forecast_cell(data, daydata, i, time_of_day);
Sean Davis's avatar
Sean Davis committed
947 948 949 950
            weather_widget_set_border_width (GTK_WIDGET (forecast_box), 4);
            gtk_widget_set_hexpand (GTK_WIDGET (forecast_box), TRUE);
            gtk_widget_set_vexpand (GTK_WIDGET (forecast_box), TRUE);

951
            if (i % 2)
Sean Davis's avatar
Sean Davis committed
952
                ebox = wrap_forecast_cell(forecast_box, NULL);
953
            else
954
                ebox = wrap_forecast_cell(forecast_box, "lightbg");
Harald Judt's avatar
Harald Judt committed
955

Harald Judt's avatar
Harald Judt committed
956
            if (data->forecast_layout == FC_LAYOUT_CALENDAR)
Sean Davis's avatar
Sean Davis committed
957
                gtk_grid_attach (GTK_GRID (grid),
Sean Davis's avatar
Sean Davis committed
958
                                 GTK_WIDGET(ebox),
959
                                 i+1, 1+time_of_day, 1, 1);