weather-summary.c 45.4 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 93
        gtk_grid_attach                       \
            (grid,                          \
Harald Judt's avatar
Harald Judt committed
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 98
        gtk_grid_attach                       \
            (grid,                          \
Harald Judt's avatar
Harald Judt committed
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 203 204 205
    sum->on_icon = TRUE;
    gdk_window_set_cursor
        (gtk_text_view_get_window(GTK_TEXT_VIEW(sum->text_view),
                                  GTK_TEXT_WINDOW_TEXT),
         sum->hand_cursor);
206
    return FALSE;
207 208
}

209 210 211 212

static gboolean
view_leave_notify(GtkWidget *widget,
                  GdkEventMotion *event,
Harald Judt's avatar
Harald Judt committed
213
                  summary_details *sum)
214
{
Harald Judt's avatar
Harald Judt committed
215 216 217 218 219
    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);
220
    return FALSE;
221 222
}

223 224 225

static void
view_scrolled_cb(GtkAdjustment *adj,
Harald Judt's avatar
Harald Judt committed
226
                 summary_details *sum)
227
{
Harald Judt's avatar
Harald Judt committed
228
    gint x, y, x1, y1;
229 230
    GtkAllocation allocation;
    GtkRequisition requisition;
Harald Judt's avatar
Harald Judt committed
231

Harald Judt's avatar
Harald Judt committed
232
    if (sum->icon_ebox) {
233 234 235 236 237 238 239 240 241 242 243
        /* TRANSLATORS: DO NOT TRANSLATE THIS STRING. This string is
           not visible to the user but controls the alignment of the
           met.no image on the details tab in the summary window,
           which is needed for RTL languages because the text is on
           the right side.
           If you are a RTL translator (hebrew, arabic), set this
           string to "RTL" to align the image to the left.
           If not, leave this string untouched or untranslated.
           Whatever you do, it should not have the value "RTL".
           If you know of a better way to determine LTR/RTL that makes
           this tweak unnecessary, please tell the developer.
244
        */
245 246 247
        gtk_widget_get_allocation (GTK_WIDGET (sum->text_view), &allocation);
        gtk_widget_get_requisition (GTK_WIDGET (sum->text_view), &requisition);

248
        if (!strcmp(_("LTR"), "RTL"))
249
            x1 = -30;
250
        else
251 252
            x1 = allocation.width - 191 - 15;
        y1 = requisition.height - 60 - 15;
Harald Judt's avatar
Harald Judt committed
253
        gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(sum->text_view),
254 255
                                              GTK_TEXT_WINDOW_TEXT,
                                              x1, y1, &x, &y);
Harald Judt's avatar
Harald Judt committed
256 257
        gtk_text_view_move_child(GTK_TEXT_VIEW(sum->text_view),
                                 sum->icon_ebox, x, y);
258
    }
259 260
}

261 262 263 264 265

static void
view_size_allocate_cb(GtkWidget *widget,
                      GtkAllocation *allocation,
                      gpointer data)
266
{
Harald Judt's avatar
Harald Judt committed
267
    view_scrolled_cb(NULL, data);
268
}
269

270 271 272

static gchar *
get_logo_path(void)
273
{
274 275 276 277 278 279 280
    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;
281 282
}

283

284
static void
285 286 287
logo_fetched(SoupSession *session,
             SoupMessage *msg,
             gpointer user_data)
288
{
289
    if (msg && msg->response_body && msg->response_body->length > 0) {
290 291 292
        gchar *path = get_logo_path();
        GError *error = NULL;
        GdkPixbuf *pixbuf = NULL;
293 294
        if (!g_file_set_contents(path, msg->response_body->data,
                                 msg->response_body->length, &error)) {
295 296 297
            g_warning(_("Error downloading met.no logo image to %s, "
                        "reason: %s\n"), path,
                      error ? error->message : _("unknown"));
298 299 300 301 302 303 304 305 306 307 308
            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);
        }
    }
309 310
}

311 312

static GtkWidget *
313
weather_summary_get_logo(plugin_data *data)
314
{
315
    GtkWidget *image = gtk_image_new();
Harald Judt's avatar
Harald Judt committed
316
    GdkPixbuf *pixbuf;
317 318 319 320 321
    gchar *path = get_logo_path();

    pixbuf = gdk_pixbuf_new_from_file(path, NULL);
    g_free(path);
    if (pixbuf == NULL)
322
        weather_http_queue_request(data->session,
323
                                   "https://met.no/filestore/met.no-logo.gif",
324
                                   logo_fetched, image);
325 326 327 328 329
    else {
        gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
        g_object_unref(pixbuf);
    }
    return image;
330
}
331

332

333
static GtkWidget *
334
create_summary_tab(plugin_data *data)
335
{
336 337
    GtkTextBuffer *buffer;
    GtkTextIter iter;
338
    GtkTextTag *btag, *ltag_img, *ltag_metno, *ltag_wiki, *ltag_geonames;
Harald Judt's avatar
Harald Judt committed
339
    GtkWidget *view, *frame, *scrolled, *icon;
340 341 342 343
    GtkAdjustment *adj;
    GdkColor lnk_color;
    xml_time *conditions;
    const gchar *unit;
Harald Judt's avatar
Harald Judt committed
344
    gchar *value, *rawvalue, *wind;
345 346
    gchar *last_download, *next_download;
    gchar *interval_start, *interval_end, *point;
347
    gchar *sunrise, *sunset, *moonrise, *moonset;
Harald Judt's avatar
Harald Judt committed
348
    summary_details *sum;
349

Harald Judt's avatar
Harald Judt committed
350 351 352 353 354 355 356
    sum = g_slice_new0(summary_details);
    sum->on_icon = FALSE;
    sum->hand_cursor = gdk_cursor_new(GDK_HAND2);
    sum->text_cursor = gdk_cursor_new(GDK_XTERM);
    data->summary_details = sum;

    sum->text_view = view = gtk_text_view_new();
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
    gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
    gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE);
    frame = gtk_frame_new(NULL);
    scrolled = gtk_scrolled_window_new(NULL, NULL);

    gtk_container_add(GTK_CONTAINER(scrolled), view);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

    gtk_container_set_border_width(GTK_CONTAINER(frame), BORDER);
    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);

381 382
    /* TRANSLATORS: Please use as many \t as appropriate to align the
       date/time values as in the original. */
383 384 385 386 387 388 389 390 391 392 393 394
    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);
395 396
    APPEND_TEXT_ITEM_REAL(value);

397 398 399 400 401 402 403 404 405 406
    /* 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"));

407 408 409 410 411 412 413 414 415 416 417 418 419
    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);

420 421 422 423 424 425 426 427 428
    /* 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"));

429
    /* calculation times */
430
    APPEND_BTEXT(_("\nTimes Used for Calculations\n"));
431
    point = format_date(conditions->point, NULL, TRUE);
432
    value = g_strdup_printf
Harald Judt's avatar
Harald Judt committed
433
        (_("\tTemperatures, wind, atmosphere and cloud data calculated\n"
434
           "\tfor:\t\t%s\n"),
435
         point);
436
    g_free(point);
437 438
    APPEND_TEXT_ITEM_REAL(value);

439 440
    interval_start = format_date(conditions->start, NULL, TRUE);
    interval_end = format_date(conditions->end, NULL, TRUE);
441
    value = g_strdup_printf
Harald Judt's avatar
Harald Judt committed
442
        (_("\n\tPrecipitation and the weather symbol have been calculated\n"
443
           "\tusing the following time interval:\n"
444 445
           "\tStart:\t%s\n"
           "\tEnd:\t%s\n"),
446 447
         interval_start,
         interval_end);
448 449
    g_free(interval_end);
    g_free(interval_start);
450 451
    APPEND_TEXT_ITEM_REAL(value);

452
    /* sun and moon */
453
    APPEND_BTEXT(_("\nAstronomical Data\n"));
454 455
    if (data->current_astro) {
        if (data->current_astro->sun_never_rises) {
456 457
            value = g_strdup(_("\tSunrise:\t\tThe sun never rises today.\n"));
            APPEND_TEXT_ITEM_REAL(value);
458
        } else if (data->current_astro->sun_never_sets) {
459 460 461
            value = g_strdup(_("\tSunset:\t\tThe sun never sets today.\n"));
            APPEND_TEXT_ITEM_REAL(value);
        } else {
462
            sunrise = format_date(data->current_astro->sunrise, NULL, TRUE);
463
            value = g_strdup_printf(_("\tSunrise:\t\t%s\n"), sunrise);
464
            g_free(sunrise);
465 466
            APPEND_TEXT_ITEM_REAL(value);

467
            sunset = format_date(data->current_astro->sunset, NULL, TRUE);
468
            value = g_strdup_printf(_("\tSunset:\t\t%s\n\n"), sunset);
469
            g_free(sunset);
470 471 472
            APPEND_TEXT_ITEM_REAL(value);
        }

473
        if (data->current_astro->moon_phase)
474 475
            value = g_strdup_printf(_("\tMoon phase:\t%s\n"),
                                    translate_moon_phase
476
                                    (data->current_astro->moon_phase));
477 478 479 480
        else
            value = g_strdup(_("\tMoon phase:\tUnknown\n"));
        APPEND_TEXT_ITEM_REAL(value);

481
        if (data->current_astro->moon_never_rises) {
482 483
            value =
                g_strdup(_("\tMoonrise:\tThe moon never rises today.\n"));
484
            APPEND_TEXT_ITEM_REAL(value);
485
        } else if (data->current_astro->moon_never_sets) {
486 487
            value =
                g_strdup(_("\tMoonset:\tThe moon never sets today.\n"));
488 489
            APPEND_TEXT_ITEM_REAL(value);
        } else {
490
            moonrise = format_date(data->current_astro->moonrise, NULL, TRUE);
491
            value = g_strdup_printf(_("\tMoonrise:\t%s\n"), moonrise);
492
            g_free(moonrise);
493 494
            APPEND_TEXT_ITEM_REAL(value);

495
            moonset = format_date(data->current_astro->moonset, NULL, TRUE);
496
            value = g_strdup_printf(_("\tMoonset:\t%s\n"), moonset);
497
            g_free(moonset);
498 499 500 501 502 503 504 505
            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
506 507
    /* temperatures */
    APPEND_BTEXT(_("\nTemperatures\n"));
508
    APPEND_TEXT_ITEM(_("Temperature"), TEMPERATURE);
Harald Judt's avatar
Harald Judt committed
509
    APPEND_TEXT_ITEM(_("Dew point"), DEWPOINT);
510
    APPEND_TEXT_ITEM(_("Apparent temperature"), APPARENT_TEMPERATURE);
511 512 513

    /* wind */
    APPEND_BTEXT(_("\nWind\n"));
514 515 516 517
    wind = get_data(conditions, data->units, WIND_SPEED,
                    FALSE, data->night_time);
    rawvalue = get_data(conditions, data->units, WIND_BEAUFORT,
                        FALSE, data->night_time);
518 519
    value = g_strdup_printf(_("\tSpeed: %s %s (%s on the Beaufort scale)\n"),
                            wind, get_unit(data->units, WIND_SPEED),
520
                            rawvalue);
521 522 523 524
    g_free(rawvalue);
    g_free(wind);
    APPEND_TEXT_ITEM_REAL(value);

525
    /* wind direction */
526 527
    rawvalue = get_data(conditions, data->units, WIND_DIRECTION_DEG,
                        FALSE, data->night_time);
528 529
    wind = get_data(conditions, data->units, WIND_DIRECTION,
                        FALSE, data->night_time);
530
    value = g_strdup_printf(_("\tDirection: %s (%s%s)\n"),
531
                            wind, rawvalue,
532
                            get_unit(data->units, WIND_DIRECTION_DEG));
533 534 535 536 537
    g_free(rawvalue);
    g_free(wind);
    APPEND_TEXT_ITEM_REAL(value);

    /* precipitation */
538 539
    APPEND_BTEXT(_("\nPrecipitation\n"));
    APPEND_TEXT_ITEM(_("Precipitation amount"), PRECIPITATION);
540 541 542

    /* atmosphere */
    APPEND_BTEXT(_("\nAtmosphere\n"));
543 544
    APPEND_TEXT_ITEM(_("Barometric pressure"), PRESSURE);
    APPEND_TEXT_ITEM(_("Relative humidity"), HUMIDITY);
545 546 547 548 549

    /* clouds */
    APPEND_BTEXT(_("\nClouds\n"));
    APPEND_TEXT_ITEM(_("Fog"), FOG);
    APPEND_TEXT_ITEM(_("Low clouds"), CLOUDS_LOW);
550
    APPEND_TEXT_ITEM(_("Middle clouds"), CLOUDS_MID);
551 552 553
    APPEND_TEXT_ITEM(_("High clouds"), CLOUDS_HIGH);
    APPEND_TEXT_ITEM(_("Cloudiness"), CLOUDINESS);

Harald Judt's avatar
Harald Judt committed
554 555
    /* credits */
    gdk_color_parse("#0000ff", &lnk_color);
556 557 558 559 560 561 562 563 564
    ltag_img = gtk_text_buffer_create_tag(buffer, "lnk0", "foreground-gdk",
                                          &lnk_color, NULL);
    ltag_metno = gtk_text_buffer_create_tag(buffer, "lnk1", "foreground-gdk",
                                            &lnk_color, NULL);
    ltag_wiki = gtk_text_buffer_create_tag(buffer, "lnk2", "foreground-gdk",
                                           &lnk_color, NULL);
    ltag_geonames = gtk_text_buffer_create_tag(buffer, "lnk3",
                                               "foreground-gdk",
                                               &lnk_color, NULL);
Harald Judt's avatar
Harald Judt committed
565 566
    APPEND_BTEXT(_("\nCredits\n"));
    APPEND_LINK_ITEM(_("\tEncyclopedic information partly taken from\n\t\t"),
567 568 569 570
                     _("Wikipedia"), "http://wikipedia.org", ltag_wiki);
    APPEND_LINK_ITEM(_("\n\tElevation and timezone data provided by\n\t\t"),
                     _("GeoNames"),
                     "http://geonames.org/", ltag_geonames);
Harald Judt's avatar
Harald Judt committed
571 572
    APPEND_LINK_ITEM(_("\n\tWeather and astronomical data from\n\t\t"),
                     _("The Norwegian Meteorological Institute"),
573 574
                     "http://met.no/", ltag_metno);
    g_object_set_data_full(G_OBJECT(ltag_img), "url",   /* url for image */
Harald Judt's avatar
Harald Judt committed
575
                           g_strdup("http://met.no"), g_free);
576 577

    g_signal_connect(G_OBJECT(view), "motion-notify-event",
Harald Judt's avatar
Harald Judt committed
578
                     G_CALLBACK(view_motion_notify), sum);
579
    g_signal_connect(G_OBJECT(view), "leave-notify-event",
Harald Judt's avatar
Harald Judt committed
580
                     G_CALLBACK(view_leave_notify), sum);
581

Harald Judt's avatar
Harald Judt committed
582
    icon = weather_summary_get_logo(data);
583

Harald Judt's avatar
Harald Judt committed
584 585
    if (icon) {
        sum->icon_ebox = gtk_event_box_new();
586 587
        gtk_event_box_set_visible_window(GTK_EVENT_BOX(sum->icon_ebox), FALSE);
        gtk_container_add(GTK_CONTAINER(sum->icon_ebox), icon);
588
        gtk_text_view_add_child_in_window(GTK_TEXT_VIEW(view),
Harald Judt's avatar
Harald Judt committed
589
                                          sum->icon_ebox,
590
                                          GTK_TEXT_WINDOW_TEXT, 0, 0);
Harald Judt's avatar
Harald Judt committed
591
        gtk_widget_show_all(sum->icon_ebox);
592 593 594
        adj =
            gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scrolled));
        g_signal_connect(G_OBJECT(adj), "value-changed",
Harald Judt's avatar
Harald Judt committed
595
                         G_CALLBACK(view_scrolled_cb), sum);
596
        g_signal_connect(G_OBJECT(view), "size_allocate",
Harald Judt's avatar
Harald Judt committed
597 598
                         G_CALLBACK(view_size_allocate_cb), sum);
        g_signal_connect(G_OBJECT(sum->icon_ebox), "button-release-event",
599
                         G_CALLBACK(icon_clicked), ltag_img);
Harald Judt's avatar
Harald Judt committed
600 601 602 603 604 605
        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);
606
    }
Harald Judt's avatar
Harald Judt committed
607

608
    return frame;
609
}
610

611

Harald Judt's avatar
Harald Judt committed
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
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);
    }
}


633 634 635 636 637 638 639 640
static gchar *
forecast_cell_get_tooltip_text(plugin_data *data,
                               xml_time *fcdata)
{
    GString *text;
    gchar *result, *value;
    const gchar *unit;

641 642 643 644
    /* 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
645
    */
646
    text = g_string_new(_("<b>Times used for calculations</b>\n"));
647
    value = format_date(fcdata->start, NULL, TRUE);
648 649 650 651
    g_string_append_printf(text, _("<tt><small>"
                                   "Interval start:       %s"
                                   "</small></tt>\n"),
                           value);
652
    g_free(value);
653
    value = format_date(fcdata->end, NULL, TRUE);
654 655 656 657
    g_string_append_printf(text, _("<tt><small>"
                                   "Interval end:         %s"
                                   "</small></tt>\n"),
                           value);
658
    g_free(value);
659
    value = format_date(fcdata->point, NULL, TRUE);
660 661 662 663
    g_string_append_printf(text, _("<tt><small>"
                                   "Data calculated for:  %s"
                                   "</small></tt>\n\n"),
                           value);
664 665
    g_free(value);

Harald Judt's avatar
Harald Judt committed
666
    g_string_append(text, _("<b>Temperatures</b>\n"));
667 668 669 670 671 672 673
    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"),
674
                        APPARENT_TEMPERATURE);
Harald Judt's avatar
Harald Judt committed
675

676
    g_string_append(text, _("<b>Atmosphere</b>\n"));
677 678 679 680 681 682 683 684
    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);
685

686
    g_string_append(text, _("<b>Precipitation</b>\n"));
687 688 689
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "Amount:        %s%s%s"
                          "</small></tt>\n\n"),
690
                        PRECIPITATION);
691 692

    g_string_append(text, _("<b>Clouds</b>\n"));
693 694 695
    /* 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. */
696
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
697
                          "Fog:           %5s%s%s"
698 699
                          "</small></tt>\n"), FOG);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
700
                          "Low clouds:    %5s%s%s"
701 702
                          "</small></tt>\n"), CLOUDS_LOW);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
703
                          "Middle clouds: %5s%s%s"
704 705
                          "</small></tt>\n"), CLOUDS_MID);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
706
                          "High clouds:   %5s%s%s"
707 708
                          "</small></tt>\n"), CLOUDS_HIGH);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
709
                          "Cloudiness:    %5s%s%s"
710
                          "</small></tt>"), CLOUDINESS);
711 712 713 714 715 716 717 718

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


719 720 721 722
static gchar *
forecast_day_header_tooltip_text(xml_astro *astro)
{
    GString *text;
Harald Judt's avatar
Harald Judt committed
723
    gchar *result, *day, *sunrise, *sunset, *moonrise, *moonset;
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799

    /* 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 {
            sunrise = format_date(astro->sunrise, NULL, TRUE);
            g_string_append_printf(text, _("<tt><small>"
                                           "Sunrise: %s"
                                           "</small></tt>\n"), sunrise);
            g_free(sunrise);

            sunset = format_date(astro->sunset, NULL, TRUE);
            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 {
            moonrise = format_date(astro->moonrise, NULL, TRUE);
            g_string_append_printf(text, _("<tt><small>"
                                           "Moonrise: %s"
                                           "</small></tt>\n"), moonrise);
            g_free(moonrise);

            moonset = format_date(astro->moonset, NULL, TRUE);
            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;
}


800
static GtkWidget *
Harald Judt's avatar
Harald Judt committed
801 802
wrap_forecast_cell(const GtkWidget *widget,
                   const GdkColor *color)
803
{
804
    GtkWidget *ebox;
Harald Judt's avatar
Harald Judt committed
805

806 807 808 809 810 811 812 813 814 815 816
    ebox = gtk_event_box_new();
    if (color == NULL)
        gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
    else {
        gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), TRUE);
        gtk_widget_modify_bg(GTK_WIDGET(ebox), GTK_STATE_NORMAL, color);
    }
    gtk_container_add(GTK_CONTAINER(ebox), GTK_WIDGET(widget));
    return ebox;
}

817

818
static GtkWidget *
819 820 821
add_forecast_header(const gchar *text,
                    const gdouble angle,
                    const GdkColor *color)
822
{
Sean Davis's avatar
Sean Davis committed
823
    GtkWidget *label;
824 825 826
    gchar *str;

    label = gtk_label_new(NULL);
Harald Judt's avatar
Harald Judt committed
827
    gtk_label_set_angle(GTK_LABEL(label), angle);
828 829 830
    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
831 832 833 834 835 836 837 838 839 840 841

    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);

    return wrap_forecast_cell(label, color);
842 843
}

844

845
static GtkWidget *
846
add_forecast_cell(plugin_data *data,
847
                  GArray *daydata,
Harald Judt's avatar
Harald Judt committed
848 849
                  gint day,
                  gint daytime)
850
{
Harald Judt's avatar
Harald Judt committed
851
    GtkWidget *box, *label, *image;
852
    GdkPixbuf *icon;
853
    const GdkColor black = {0, 0x0000, 0x0000, 0x0000};
Harald Judt's avatar
Harald Judt committed
854 855 856
    gchar *wind_speed, *wind_direction, *value, *rawvalue;
    xml_time *fcdata;

857
    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
Harald Judt's avatar
Harald Judt committed
858

859
    fcdata = make_forecast_data(data->weatherdata, daydata, day, daytime);
Harald Judt's avatar
Harald Judt committed
860 861 862 863 864 865 866 867 868
    if (fcdata == NULL)
        return box;

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

    /* symbol */
869 870
    rawvalue = get_data(fcdata, data->units, SYMBOL,
                        FALSE, data->night_time);
Harald Judt's avatar
Harald Judt committed
871 872 873 874 875 876 877 878
    icon = get_icon(data->icon_theme, rawvalue, 48, (daytime == NIGHT));
    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 */
879 880
    rawvalue = get_data(fcdata, data->units, SYMBOL,
                        FALSE, data->night_time);
Harald Judt's avatar
Harald Judt committed
881 882 883 884 885 886 887 888 889 890 891
    value = g_strdup_printf("%s",
                            translate_desc(rawvalue, (daytime == NIGHT)));
    g_free(rawvalue);
    label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label), value);
    if (!(day % 2))
        gtk_widget_modify_fg(GTK_WIDGET(label), GTK_STATE_NORMAL, &black);
    gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(label), TRUE, TRUE, 0);
    g_free(value);

    /* temperature */
892 893
    rawvalue = get_data(fcdata, data->units, TEMPERATURE,
                        data->round, data->night_time);
Harald Judt's avatar
Harald Judt committed
894 895 896 897 898 899 900 901 902 903
    value = g_strdup_printf("%s %s", rawvalue,
                            get_unit(data->units, TEMPERATURE));
    g_free(rawvalue);
    label = gtk_label_new(value);
    if (!(day % 2))
        gtk_widget_modify_fg(GTK_WIDGET(label), GTK_STATE_NORMAL, &black);
    gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(label), TRUE, TRUE, 0);
    g_free(value);

    /* wind direction and speed */
904 905
    wind_direction = get_data(fcdata, data->units, WIND_DIRECTION,
                              FALSE, data->night_time);
906 907
    wind_speed = get_data(fcdata, data->units, WIND_SPEED,
                          data->round, data->night_time);
Harald Judt's avatar
Harald Judt committed
908 909 910 911 912 913 914 915 916 917 918 919
    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);
    if (!(day % 2))
        gtk_widget_modify_fg(GTK_WIDGET(label), GTK_STATE_NORMAL, &black);
    gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
    g_free(value);

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

920 921 922 923
    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
924 925 926 927 928 929
    xml_time_free(fcdata);
    return box;
}


static GtkWidget *
930
make_forecast(plugin_data *data)
Harald Judt's avatar
Harald Judt committed
931
{
Sean Davis's avatar
Sean Davis committed
932
    GtkWidget *grid, *ebox, *box;
Harald Judt's avatar
Harald Judt committed
933
    GtkWidget *forecast_box;
934 935
    const GdkColor lightbg = {0, 0xeaea, 0xeaea, 0xeaea};
    const GdkColor darkbg = {0, 0x6666, 0x6666, 0x6666};
936
    GArray *daydata;
937 938
    xml_astro *astro;
    gchar *dayname, *text;
Harald Judt's avatar
Harald Judt committed
939 940 941
    gint i;
    daytime daytime;

Sean Davis's avatar
Sean Davis committed
942
    grid = gtk_grid_new ();
943

Sean Davis's avatar
Sean Davis committed
944 945
    gtk_grid_set_row_spacing(grid, 0);
    gtk_grid_set_column_spacing(grid, 0);
946 947

    /* empty upper left corner */
948
    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
Sean Davis's avatar
Sean Davis committed
949 950 951
    gtk_grid_attach (grid,
                     wrap_forecast_cell(box, &darkbg),
                     0, 0, 1, 1);
952 953

    /* daytime headers */
Harald Judt's avatar
Harald Judt committed
954 955 956 957
    ATTACH_DAYTIME_HEADER(_("Morning"), 1);
    ATTACH_DAYTIME_HEADER(_("Afternoon"), 2);
    ATTACH_DAYTIME_HEADER(_("Evening"), 3);
    ATTACH_DAYTIME_HEADER(_("Night"), 4);
958

959
    for (i = 0; i < data->forecast_days; i++) {
960
        /* forecast day headers */
Harald Judt's avatar
Harald Judt committed
961 962 963
        dayname = get_dayname(i);
        if (data->forecast_layout == FC_LAYOUT_CALENDAR)
            ebox = add_forecast_header(dayname, 0.0, &darkbg);
964
        else
Harald Judt's avatar
Harald Judt committed
965
            ebox = add_forecast_header(dayname, 90.0, &darkbg);
Harald Judt's avatar
Harald Judt committed
966
        g_free(dayname);
967

968 969 970 971 972
        /* 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
973
        if (data->forecast_layout == FC_LAYOUT_CALENDAR)
Sean Davis's avatar
Sean Davis committed
974 975
            gtk_grid_attach (grid, GTK_WIDGET(ebox),
                             i+1, 0, 1, 1);
Harald Judt's avatar
Harald Judt committed
976
        else
Sean Davis's avatar
Sean Davis committed
977 978
            gtk_grid_attach (grid, GTK_WIDGET(ebox),
                             0, i+1, 1, 1);
979

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

983
        /* get forecast data for each daytime */
984
        for (daytime = MORNING; daytime <= NIGHT; daytime++) {
985
            forecast_box = add_forecast_cell(data, daydata, i, daytime);
Sean Davis's avatar
Sean Davis committed
986 987 988 989
            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);

990
            if (i % 2)
Sean Davis's avatar
Sean Davis committed
991
                ebox = wrap_forecast_cell(forecast_box, NULL);
992
            else
Sean Davis's avatar
Sean Davis committed
993
                ebox = wrap_forecast_cell(forecast_box, &lightbg);
Harald Judt's avatar
Harald Judt committed
994

Harald Judt's avatar
Harald Judt committed
995
            if (data->forecast_layout == FC_LAYOUT_CALENDAR)
Sean Davis's avatar
Sean Davis committed
996 997 998
                gtk_grid_attach (grid,
                                 GTK_WIDGET(ebox),
                                 i+1, 1+daytime, 1, 1);
Harald Judt's avatar
Harald Judt committed
999
            else
Sean Davis's avatar
Sean Davis committed
1000 1001 1002
                gtk_grid_attach (grid,
                                 GTK_WIDGET(ebox),
                                 1+daytime, i+1, 1, 1);
1003
        }
1004
        g_array_free(daydata, FALSE);
1005
    }
Sean Davis's avatar
Sean Davis committed
1006
    return grid;
1007 1008
}

1009

1010