weather-summary.c 37.2 KB
Newer Older
1
/*  Copyright (c) 2003-2013 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 51
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);

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/*
 * 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.
 */
67
#define APPEND_TEXT_ITEM(text, item)                            \
68 69
    rawvalue = get_data(conditions, data->units, item,          \
                        FALSE, data->night_time);               \
70
    unit = get_unit(data->units, item);                         \
71
    value = g_strdup_printf(_("\t%s%s%s%s%s\n"),                \
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
                            text, text ? ": " : "",             \
                            rawvalue,                           \
                            strcmp(unit, "°") ? " " : "",       \
                            unit);                              \
    g_free(rawvalue);                                           \
    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);                            \
    g_object_set_data_full(G_OBJECT(lnk_tag), "url", g_strdup(url), g_free); \
    g_signal_connect(G_OBJECT(lnk_tag), "event",                        \
                     G_CALLBACK(lnk_clicked), NULL);
89

Harald Judt's avatar
Harald Judt committed
90 91 92 93 94 95 96 97 98 99 100 101
#define ATTACH_DAYTIME_HEADER(title, pos)               \
    if (data->forecast_layout == FC_LAYOUT_CALENDAR)    \
        gtk_table_attach_defaults                       \
            (GTK_TABLE(table),                          \
             add_forecast_header(title, 90.0, &darkbg), \
             0, 1, pos, pos+1);                         \
    else                                                \
        gtk_table_attach_defaults                       \
            (GTK_TABLE(table),                          \
             add_forecast_header(title, 0.0, &darkbg),  \
             pos, pos+1, 0, 1);                         \

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
    unit = get_unit(data->units, item);                         \
    g_string_append_printf(text, description, value,            \
                           strcmp(unit, "°") ? " " : "",        \
                           unit);                               \
    g_free(value);

111

112 113 114 115 116 117
static gboolean
lnk_clicked(GtkTextTag *tag,
            GObject *obj,
            GdkEvent *event,
            GtkTextIter *iter,
            GtkWidget *textview)
118
{
119 120
    const gchar *url;
    gchar *str;
121

122 123 124 125 126 127 128 129 130 131
    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;
132 133
}

134

135
static gboolean
136
icon_clicked (GtkWidget *widget,
137
              GdkEventButton *event,
138
              gpointer user_data)
139
{
140
    return lnk_clicked(user_data, NULL, (GdkEvent *) (event), NULL, NULL);
141 142
}

143 144 145 146

static gboolean
view_motion_notify(GtkWidget *widget,
                   GdkEventMotion *event,
Harald Judt's avatar
Harald Judt committed
147
                   summary_details *sum)
148
{
Harald Judt's avatar
Harald Judt committed
149 150 151 152 153
    GtkTextIter iter;
    GtkTextTag *tag;
    GSList *tags;
    GSList *cur;
    gint bx, by;
154

Harald Judt's avatar
Harald Judt committed
155
    if (event->x != -1 && event->y != -1) {
Harald Judt's avatar
Harald Judt committed
156
        gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(sum->text_view),
157 158
                                              GTK_TEXT_WINDOW_WIDGET,
                                              event->x, event->y, &bx, &by);
Harald Judt's avatar
Harald Judt committed
159
        gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(sum->text_view),
160 161 162
                                           &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
163
            tag = cur->data;
164
            if (g_object_get_data(G_OBJECT(tag), "url")) {
Harald Judt's avatar
Harald Judt committed
165 166 167 168
                gdk_window_set_cursor
                    (gtk_text_view_get_window(GTK_TEXT_VIEW(sum->text_view),
                                              GTK_TEXT_WINDOW_TEXT),
                     sum->hand_cursor);
169 170 171
                return FALSE;
            }
        }
172
    }
Harald Judt's avatar
Harald Judt committed
173 174 175 176 177
    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);
178
    return FALSE;
179 180
}

181 182 183 184

static gboolean
icon_motion_notify(GtkWidget *widget,
                   GdkEventMotion *event,
Harald Judt's avatar
Harald Judt committed
185
                   summary_details *sum)
186
{
Harald Judt's avatar
Harald Judt committed
187 188 189 190 191
    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);
192
    return FALSE;
193 194
}

195 196 197 198

static gboolean
view_leave_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 = FALSE;
    gdk_window_set_cursor
        (gtk_text_view_get_window(GTK_TEXT_VIEW(sum->text_view),
                                  GTK_TEXT_WINDOW_TEXT),
         sum->text_cursor);
206
    return FALSE;
207 208
}

209 210 211

static void
view_scrolled_cb(GtkAdjustment *adj,
Harald Judt's avatar
Harald Judt committed
212
                 summary_details *sum)
213
{
Harald Judt's avatar
Harald Judt committed
214 215
    gint x, y, x1, y1;

Harald Judt's avatar
Harald Judt committed
216 217 218 219
    if (sum->icon_ebox) {
        x1 = sum->text_view->allocation.width - 191 - 15;
        y1 = sum->text_view->requisition.height - 60 - 15;
        gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(sum->text_view),
220 221
                                              GTK_TEXT_WINDOW_TEXT,
                                              x1, y1, &x, &y);
Harald Judt's avatar
Harald Judt committed
222 223
        gtk_text_view_move_child(GTK_TEXT_VIEW(sum->text_view),
                                 sum->icon_ebox, x, y);
224
    }
225 226
}

227 228 229 230 231

static void
view_size_allocate_cb(GtkWidget *widget,
                      GtkAllocation *allocation,
                      gpointer data)
232
{
Harald Judt's avatar
Harald Judt committed
233
    view_scrolled_cb(NULL, data);
234
}
235

236 237 238

static gchar *
get_logo_path(void)
239
{
240 241 242 243 244 245 246
    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;
247 248
}

249

250
static void
251 252 253
logo_fetched(SoupSession *session,
             SoupMessage *msg,
             gpointer user_data)
254
{
255
    if (msg && msg->response_body && msg->response_body->length > 0) {
256 257 258
        gchar *path = get_logo_path();
        GError *error = NULL;
        GdkPixbuf *pixbuf = NULL;
259 260
        if (!g_file_set_contents(path, msg->response_body->data,
                                 msg->response_body->length, &error)) {
261 262 263
            g_warning(_("Error downloading met.no logo image to %s, "
                        "reason: %s\n"), path,
                      error ? error->message : _("unknown"));
264 265 266 267 268 269 270 271 272 273 274
            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);
        }
    }
275 276
}

277 278

static GtkWidget *
279
weather_summary_get_logo(plugin_data *data)
280
{
281
    GtkWidget *image = gtk_image_new();
Harald Judt's avatar
Harald Judt committed
282
    GdkPixbuf *pixbuf;
283 284 285 286 287
    gchar *path = get_logo_path();

    pixbuf = gdk_pixbuf_new_from_file(path, NULL);
    g_free(path);
    if (pixbuf == NULL)
288 289
        weather_http_queue_request(data->session,
                                   "http://met.no/filestore/met.no-logo.gif",
290
                                   logo_fetched, image);
291 292 293 294 295
    else {
        gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
        g_object_unref(pixbuf);
    }
    return image;
296
}
297

298

299
static GtkWidget *
300
create_summary_tab(plugin_data *data)
301
{
302 303 304
    GtkTextBuffer *buffer;
    GtkTextIter iter;
    GtkTextTag *btag, *ltag0, *ltag1;
Harald Judt's avatar
Harald Judt committed
305
    GtkWidget *view, *frame, *scrolled, *icon;
306 307 308 309
    GtkAdjustment *adj;
    GdkColor lnk_color;
    xml_time *conditions;
    const gchar *unit;
Harald Judt's avatar
Harald Judt committed
310
    gchar *value, *rawvalue, *wind;
311 312
    gchar *last_download, *next_download;
    gchar *interval_start, *interval_end, *point;
313
    gchar *sunrise, *sunset, *moonrise, *moonset;
Harald Judt's avatar
Harald Judt committed
314
    summary_details *sum;
315

Harald Judt's avatar
Harald Judt committed
316 317 318 319 320 321 322
    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();
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    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);

    gdk_color_parse("#0000ff", &lnk_color);
    ltag0 = gtk_text_buffer_create_tag(buffer, "lnk0",
                                       "foreground-gdk", &lnk_color, NULL);
    ltag1 = gtk_text_buffer_create_tag(buffer, "lnk1",
                                       "foreground-gdk", &lnk_color, 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);

353 354
    /* TRANSLATORS: Please use as many \t as appropriate to align the
       date/time values as in the original. */
355 356 357 358 359 360 361 362 363 364 365 366
    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);
367 368
    APPEND_TEXT_ITEM_REAL(value);

369 370 371 372 373 374 375 376 377 378 379 380 381 382
    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);

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

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

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

420
            sunset = format_date(data->astrodata->sunset, NULL, TRUE);
421
            value = g_strdup_printf(_("\tSunset:\t\t%s\n\n"), sunset);
422
            g_free(sunset);
423 424 425 426 427 428 429 430 431 432 433 434
            APPEND_TEXT_ITEM_REAL(value);
        }

        if (data->astrodata->moon_phase)
            value = g_strdup_printf(_("\tMoon phase:\t%s\n"),
                                    translate_moon_phase
                                    (data->astrodata->moon_phase));
        else
            value = g_strdup(_("\tMoon phase:\tUnknown\n"));
        APPEND_TEXT_ITEM_REAL(value);

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

448
            moonset = format_date(data->astrodata->moonset, NULL, TRUE);
449
            value = g_strdup_printf(_("\tMoonset:\t%s\n"), moonset);
450
            g_free(moonset);
451 452 453 454 455 456 457 458
            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
459 460
    /* temperatures */
    APPEND_BTEXT(_("\nTemperatures\n"));
461
    APPEND_TEXT_ITEM(_("Temperature"), TEMPERATURE);
Harald Judt's avatar
Harald Judt committed
462
    APPEND_TEXT_ITEM(_("Dew point"), DEWPOINT);
463
    APPEND_TEXT_ITEM(_("Apparent temperature"), APPARENT_TEMPERATURE);
464 465 466

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

478 479
    rawvalue = get_data(conditions, data->units, WIND_DIRECTION,
                        FALSE, data->night_time);
480 481
    wind = translate_wind_direction(rawvalue);
    g_free(rawvalue);
482 483
    rawvalue = get_data(conditions, data->units, WIND_DIRECTION_DEG,
                        FALSE, data->night_time);
484 485 486

    /* wind direction */
    value = g_strdup_printf(_("\tDirection: %s (%s%s)\n"),
487
                            wind, rawvalue,
488
                            get_unit(data->units, WIND_DIRECTION_DEG));
489 490 491 492 493 494 495 496 497 498
    g_free(rawvalue);
    g_free(wind);
    APPEND_TEXT_ITEM_REAL(value);

    /* precipitation */
    APPEND_BTEXT(_("\nPrecipitations\n"));
    APPEND_TEXT_ITEM(_("Precipitations amount"), PRECIPITATIONS);

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

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

    APPEND_BTEXT(_("\nData from The Norwegian Meteorological Institute\n"));
    value = g_strdup("http://met.no");
    g_object_set_data_full(G_OBJECT(ltag0), "url", value, g_free);
    APPEND_LINK_ITEM("\t", _("Thanks to met.no"), "http://met.no/", ltag1);

    g_signal_connect(G_OBJECT(view), "motion-notify-event",
Harald Judt's avatar
Harald Judt committed
516
                     G_CALLBACK(view_motion_notify), sum);
517
    g_signal_connect(G_OBJECT(view), "leave-notify-event",
Harald Judt's avatar
Harald Judt committed
518
                     G_CALLBACK(view_leave_notify), sum);
519

Harald Judt's avatar
Harald Judt committed
520
    icon = weather_summary_get_logo(data);
521

Harald Judt's avatar
Harald Judt committed
522 523
    if (icon) {
        sum->icon_ebox = gtk_event_box_new();
524 525
        gtk_event_box_set_visible_window(GTK_EVENT_BOX(sum->icon_ebox), FALSE);
        gtk_container_add(GTK_CONTAINER(sum->icon_ebox), icon);
526
        gtk_text_view_add_child_in_window(GTK_TEXT_VIEW(view),
Harald Judt's avatar
Harald Judt committed
527
                                          sum->icon_ebox,
528
                                          GTK_TEXT_WINDOW_TEXT, 0, 0);
Harald Judt's avatar
Harald Judt committed
529
        gtk_widget_show_all(sum->icon_ebox);
530 531 532
        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
533
                         G_CALLBACK(view_scrolled_cb), sum);
534
        g_signal_connect(G_OBJECT(view), "size_allocate",
Harald Judt's avatar
Harald Judt committed
535 536 537 538 539 540 541 542 543
                         G_CALLBACK(view_size_allocate_cb), sum);
        g_signal_connect(G_OBJECT(sum->icon_ebox), "button-release-event",
                         G_CALLBACK(icon_clicked), ltag0);
        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);
544
    }
Harald Judt's avatar
Harald Judt committed
545

546
    return frame;
547
}
548

549

Harald Judt's avatar
Harald Judt committed
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
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);
    }
}


571 572 573 574 575 576 577 578
static gchar *
forecast_cell_get_tooltip_text(plugin_data *data,
                               xml_time *fcdata)
{
    GString *text;
    gchar *result, *value;
    const gchar *unit;

579 580 581 582 583
    /* 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.
     */
584
    text = g_string_new(_("<b>Times used for calculations</b>\n"));
585
    value = format_date(fcdata->start, NULL, TRUE);
586 587 588 589
    g_string_append_printf(text, _("<tt><small>"
                                   "Interval start:       %s"
                                   "</small></tt>\n"),
                           value);
590
    g_free(value);
591
    value = format_date(fcdata->end, NULL, TRUE);
592 593 594 595
    g_string_append_printf(text, _("<tt><small>"
                                   "Interval end:         %s"
                                   "</small></tt>\n"),
                           value);
596
    g_free(value);
597
    value = format_date(fcdata->point, NULL, TRUE);
598 599 600 601
    g_string_append_printf(text, _("<tt><small>"
                                   "Data calculated for:  %s"
                                   "</small></tt>\n\n"),
                           value);
602 603
    g_free(value);

Harald Judt's avatar
Harald Judt committed
604
    g_string_append(text, _("<b>Temperatures</b>\n"));
605 606 607 608 609 610 611
    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"),
612
                        APPARENT_TEMPERATURE);
Harald Judt's avatar
Harald Judt committed
613

614
    g_string_append(text, _("<b>Atmosphere</b>\n"));
615 616 617 618 619 620 621 622
    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);
623 624

    g_string_append(text, _("<b>Precipitations</b>\n"));
625 626 627 628
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "Amount:        %s%s%s"
                          "</small></tt>\n\n"),
                        PRECIPITATIONS);
629 630

    g_string_append(text, _("<b>Clouds</b>\n"));
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "Fog:           %s%s%s"
                          "</small></tt>\n"), FOG);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "Low clouds:    %s%s%s"
                          "</small></tt>\n"), CLOUDS_LOW);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "Middle clouds: %s%s%s"
                          "</small></tt>\n"), CLOUDS_MID);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "High clouds:   %s%s%s"
                          "</small></tt>\n"), CLOUDS_HIGH);
    APPEND_TOOLTIP_ITEM(_("<tt><small>"
                          "Cloudiness:    %s%s%s"
                          "</small></tt>"), CLOUDINESS);
646 647 648 649 650 651 652 653

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


654
static GtkWidget *
Harald Judt's avatar
Harald Judt committed
655 656
wrap_forecast_cell(const GtkWidget *widget,
                   const GdkColor *color)
657
{
658
    GtkWidget *ebox;
Harald Judt's avatar
Harald Judt committed
659

660 661 662 663 664 665 666 667 668 669 670
    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;
}

671

672
static GtkWidget *
673 674 675
add_forecast_header(const gchar *text,
                    const gdouble angle,
                    const GdkColor *color)
676
{
Harald Judt's avatar
Harald Judt committed
677
    GtkWidget *label, *align;
678 679
    gchar *str;

680 681 682 683 684
    if (angle)
        align = gtk_alignment_new(1, 1, 0, 1);
    else
        align = gtk_alignment_new(1, 1, 1, 0);
    gtk_container_set_border_width(GTK_CONTAINER(align), 4);
685 686

    label = gtk_label_new(NULL);
Harald Judt's avatar
Harald Judt committed
687
    gtk_label_set_angle(GTK_LABEL(label), angle);
688 689 690
    str = g_strdup_printf("<span foreground=\"white\"><b>%s</b></span>", text ? text : "");
    gtk_label_set_markup(GTK_LABEL(label), str);
    g_free(str);
Harald Judt's avatar
Harald Judt committed
691
    gtk_container_add(GTK_CONTAINER(align), GTK_WIDGET(label));
Harald Judt's avatar
Harald Judt committed
692
    return wrap_forecast_cell(align, color);
693 694
}

695

696
static GtkWidget *
697
add_forecast_cell(plugin_data *data,
Harald Judt's avatar
Harald Judt committed
698 699
                  gint day,
                  gint daytime)
700
{
Harald Judt's avatar
Harald Judt committed
701
    GtkWidget *box, *label, *image;
702
    GdkPixbuf *icon;
703
    const GdkColor black = {0, 0x0000, 0x0000, 0x0000};
Harald Judt's avatar
Harald Judt committed
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
    gchar *wind_speed, *wind_direction, *value, *rawvalue;
    xml_time *fcdata;

    box = gtk_vbox_new(FALSE, 0);

    fcdata = make_forecast_data(data->weatherdata, day, daytime);
    if (fcdata == NULL)
        return box;

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

    /* symbol */
719 720
    rawvalue = get_data(fcdata, data->units, SYMBOL,
                        FALSE, data->night_time);
Harald Judt's avatar
Harald Judt committed
721 722 723 724 725 726 727 728
    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 */
729 730
    rawvalue = get_data(fcdata, data->units, SYMBOL,
                        FALSE, data->night_time);
Harald Judt's avatar
Harald Judt committed
731 732 733 734 735 736 737 738 739 740 741
    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 */
742 743
    rawvalue = get_data(fcdata, data->units, TEMPERATURE,
                        data->round, data->night_time);
Harald Judt's avatar
Harald Judt committed
744 745 746 747 748 749 750 751 752 753
    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 */
754 755
    rawvalue = get_data(fcdata, data->units, WIND_DIRECTION,
                        FALSE, data->night_time);
Harald Judt's avatar
Harald Judt committed
756
    wind_direction = translate_wind_direction(rawvalue);
757 758
    wind_speed = get_data(fcdata, data->units, WIND_SPEED,
                          data->round, data->night_time);
Harald Judt's avatar
Harald Judt committed
759 760 761 762 763 764 765 766 767 768 769 770 771
    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);
    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), label, TRUE, TRUE, 0);
    g_free(value);

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

772 773 774 775
    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
776 777 778 779 780 781
    xml_time_free(fcdata);
    return box;
}


static GtkWidget *
782
make_forecast(plugin_data *data)
Harald Judt's avatar
Harald Judt committed
783 784 785
{
    GtkWidget *table, *ebox, *box, *align;
    GtkWidget *forecast_box;
786 787
    const GdkColor lightbg = {0, 0xeaea, 0xeaea, 0xeaea};
    const GdkColor darkbg = {0, 0x6666, 0x6666, 0x6666};
Harald Judt's avatar
Harald Judt committed
788
    gchar *dayname;
Harald Judt's avatar
Harald Judt committed
789 790 791 792 793 794 795
    gint i;
    daytime daytime;

    if (data->forecast_layout == FC_LAYOUT_CALENDAR)
        table = gtk_table_new(5, data->forecast_days + 1, FALSE);
    else
        table = gtk_table_new(data->forecast_days + 1, 5, FALSE);
796 797 798 799 800 801 802

    gtk_table_set_row_spacings(GTK_TABLE(table), 0);
    gtk_table_set_col_spacings(GTK_TABLE(table), 0);

    /* empty upper left corner */
    box = gtk_vbox_new(FALSE, 0);
    gtk_table_attach_defaults(GTK_TABLE(table),
Harald Judt's avatar
Harald Judt committed
803
                              wrap_forecast_cell(box, &darkbg),
804 805 806
                              0, 1, 0, 1);

    /* daytime headers */
Harald Judt's avatar
Harald Judt committed
807 808 809 810
    ATTACH_DAYTIME_HEADER(_("Morning"), 1);
    ATTACH_DAYTIME_HEADER(_("Afternoon"), 2);
    ATTACH_DAYTIME_HEADER(_("Evening"), 3);
    ATTACH_DAYTIME_HEADER(_("Night"), 4);
811

812
    for (i = 0; i < data->forecast_days; i++) {
813
        /* forecast day headers */
Harald Judt's avatar
Harald Judt committed
814 815 816
        dayname = get_dayname(i);
        if (data->forecast_layout == FC_LAYOUT_CALENDAR)
            ebox = add_forecast_header(dayname, 0.0, &darkbg);
817
        else
Harald Judt's avatar
Harald Judt committed
818
            ebox = add_forecast_header(dayname, 90.0, &darkbg);
Harald Judt's avatar
Harald Judt committed
819
        g_free(dayname);
820

Harald Judt's avatar
Harald Judt committed
821 822 823 824 825 826
        if (data->forecast_layout == FC_LAYOUT_CALENDAR)
            gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(ebox),
                                      i+1, i+2, 0, 1);
        else
            gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(ebox),
                                      0, 1, i+1, i+2);
827

828
        /* get forecast data for each daytime */
829
        for (daytime = MORNING; daytime <= NIGHT; daytime++) {
Harald Judt's avatar
Harald Judt committed
830
            forecast_box = add_forecast_cell(data, i, daytime);
Harald Judt's avatar
Harald Judt committed
831 832 833
            align = gtk_alignment_new(0.5, 0.5, 1, 1);
            gtk_container_set_border_width(GTK_CONTAINER(align), 4);
            gtk_container_add(GTK_CONTAINER(align), GTK_WIDGET(forecast_box));
834
            if (i % 2)
Harald Judt's avatar
Harald Judt committed
835
                ebox = wrap_forecast_cell(align, NULL);
836
            else
Harald Judt's avatar
Harald Judt committed
837 838
                ebox = wrap_forecast_cell(align, &lightbg);

Harald Judt's avatar
Harald Judt committed
839 840 841 842 843 844 845 846
            if (data->forecast_layout == FC_LAYOUT_CALENDAR)
                gtk_table_attach_defaults(GTK_TABLE(table),
                                          GTK_WIDGET(ebox),
                                          i+1, i+2, 1+daytime, 2+daytime);
            else
                gtk_table_attach_defaults(GTK_TABLE(table),
                                          GTK_WIDGET(ebox),
                                          1+daytime, 2+daytime, i+1, i+2);
847 848 849
        }
    }
    return table;
850 851
}

852

853
static GtkWidget *
854
create_forecast_tab(plugin_data *data)
855
{
856
    GtkWidget *ebox, *align, *hbox, *scrolled, *table;
857
    GdkWindow *window;
858
    GdkScreen *screen;
859
    GdkRectangle rect;
Harald Judt's avatar
Harald Judt committed
860 861
    gint monitor_num = 0, h_need, h_max, height;
    gint w_need, w_max, width;
862

863 864 865 866 867 868 869 870 871 872
    /* To avoid causing a GDK assertion, determine the monitor
     * geometry using the weather icon window, which has already been
     * realized in contrast to the summary window. Then calculate the
     * maximum height we may use, subtracting some sane value just to
     * be on the safe side. */
    window = GDK_WINDOW(gtk_widget_get_window(GTK_WIDGET(data->iconimage)));
    screen = GDK_SCREEN(gdk_window_get_screen(window));
    if (G_LIKELY(window && screen))
        monitor_num = gdk_screen_get_monitor_at_window(screen, window);
    gdk_screen_get_monitor_geometry(screen, monitor_num, &rect);
873

Harald Judt's avatar
Harald Judt committed
874 875 876
    /* calculate maximum width and height */
    h_max = rect.height - 250;
    w_max = rect.width - 50;
877

Harald Judt's avatar
Harald Judt committed
878 879 880 881 882 883 884 885
    /* calculate needed space using a good arbitrary value */
    if (data->forecast_layout == FC_LAYOUT_CALENDAR) {
        w_need = ((data->forecast_days < 8) ? data->forecast_days : 7) * 142;
        h_need = 500;
    } else {
        w_need = (rect.width <= 720) ? 650 : 700;
        h_need = data->forecast_days * 110;
    }
886 887 888 889

    /* generate the forecast table */
    table = GTK_WIDGET(make_forecast(data));

Harald Judt's avatar
Harald Judt committed
890
    /* generate the containing widgets */
891
    align = gtk_alignment_new(0.5, 0, 0.5, 0);
Harald Judt's avatar
Harald Judt committed
892 893 894 895
    if ((data->forecast_layout == FC_LAYOUT_CALENDAR &&
         w_need < w_max && data->forecast_days < 8) ||
        (data->forecast_layout == FC_LAYOUT_LIST && h_need < h_max)) {
        /* no scroll window needed, just align the contents */
896 897 898 899
        gtk_container_add(GTK_CONTAINER(align), GTK_WIDGET(table));
        gtk_container_set_border_width(GTK_CONTAINER(align), BORDER);
        return align;
    } else {
Harald Judt's avatar
Harald Judt committed
900
        /* contents too big, scroll window needed */
901 902 903 904 905 906 907
        hbox = gtk_hbox_new(FALSE, 0);
        gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, FALSE, 0);
        gtk_container_add(GTK_CONTAINER(align), GTK_WIDGET(hbox));

        scrolled = gtk_scrolled_window_new (NULL, NULL);
        gtk_container_set_border_width(GTK_CONTAINER(scrolled), BORDER);

908 909
        gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled),
                                              align);
910
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
911 912
                                       GTK_POLICY_AUTOMATIC,
                                       GTK_POLICY_AUTOMATIC);
Harald Judt's avatar
Harald Judt committed
913 914 915 916 917 918

        /* set scroll window size */
        width = (w_need > w_max) ? w_max : w_need;
        height = (h_need > h_max) ? h_max : h_need;
        gtk_widget_set_size_request(GTK_WIDGET(scrolled), width, height);

919
        ebox = gtk_event_box_new();
920
        gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), TRUE);
921 922
        gtk_container_add(GTK_CONTAINER(ebox), GTK_WIDGET(scrolled));
        return ebox;
923
    }
924
}
925

926

927
static void
928 929
summary_dialog_response(const GtkWidget *dlg,
                        const gint response,
930
                        GtkWidget *window)
931
{
932 933
    if (response == GTK_RESPONSE_ACCEPT)
        gtk_widget_destroy(window);
934 935
}

936

937 938 939 940 941 942 943 944 945 946 947 948
static void
cb_notebook_page_switched(GtkNotebook *notebook,
                          GtkNotebookPage *page,
                          guint page_num,
                          gpointer user_data)
{
    plugin_data *data = (plugin_data *) user_data;

    data->summary_remember_tab = page_num;
}


949
GtkWidget *
950
create_summary_window(plugin_data *data)
951
{
952
    GtkWidget *window, *notebook, *vbox, *hbox, *label;
Harald Judt's avatar
Harald Judt committed
953
    gchar *title, *symbol;
954 955 956
    GdkPixbuf *icon;
    xml_time *conditions;

957 958
    conditions = get_current_conditions(data->weatherdata);
    window = xfce_titled_dialog_new_with_buttons(_("Weather Report"),
959 960 961 962 963
                                                 NULL,
                                                 GTK_DIALOG_NO_SEPARATOR,
                                                 GTK_STOCK_CLOSE,
                                                 GTK_RESPONSE_ACCEPT, NULL);
    if (data->location_name != NULL) {
964
        title = g_strdup_printf("%s", data->location_name);
965 966 967 968 969
        xfce_titled_dialog_set_subtitle(XFCE_TITLED_DIALOG(window), title);
        g_free(title);
    }

    vbox = gtk_vbox_new(FALSE, 0);
Harald Judt's avatar
Harald Judt committed
970
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), vbox, TRUE, TRUE, 0);
971

972 973
    symbol = get_data(conditions, data->units, SYMBOL,
                      FALSE, data->night_time);
974
    icon = get_icon(data->icon_theme, symbol, 48, data->night_time);
Harald Judt's avatar
Harald Judt committed
975
    g_free(symbol);
976 977 978 979 980 981

    gtk_window_set_icon(GTK_WINDOW(window), icon);

    if (G_LIKELY(icon))
        g_object_unref(G_OBJECT(icon));

982 983
    if (data->location_name == NULL || data->weatherdata == NULL ||
        data->weatherdata->current_conditions == NULL) {
984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
        hbox = gtk_hbox_new(FALSE, 0);
        if (data->location_name == NULL)
            label = gtk_label_new(_("Please set a location in the plugin settings."));
        else
            label = gtk_label_new(_("Currently no data available."));
        gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label),
                           TRUE, TRUE, 0);

        gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox),
                           TRUE, TRUE, 0);
        gtk_window_set_default_size(GTK_WINDOW(window), 500, 400);
    } else {
        notebook = gtk_notebook_new();
        gtk_container_set_border_width(GTK_CONTAINER(notebook), BORDER);
        gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
999
                                 create_forecast_tab(data),
1000 1001 1002 1003 1004
                                 gtk_label_new_with_mnemonic(_("_Forecast")));
        gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
                                 create_summary_tab(data),
                                 gtk_label_new_with_mnemonic(_("_Details")));
        gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
1005 1006 1007 1008
        gtk_widget_show_all(GTK_WIDGET(notebook));
        gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), data->summary_remember_tab);
        g_signal_connect(GTK_NOTEBOOK(notebook), "switch-page",
                         G_CALLBACK(cb_notebook_page_switched), data);
1009 1010 1011 1012 1013 1014
    }

    g_signal_connect(G_OBJECT(window), "response",
                     G_CALLBACK(summary_dialog_response), window);

    return window;
1015
}
Harald Judt's avatar
Harald Judt committed
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033


void
summary_details_free(summary_details *sum)
{
    g_assert(sum != NULL);
    if (G_UNLIKELY(sum == NULL))
        return;

    sum->icon_ebox = NULL;
    sum->text_view = NULL;
    if (sum->hand_cursor)
        gdk_cursor_unref(sum->hand_cursor);
    sum->hand_cursor = NULL;
    if (sum->text_cursor)
        gdk_cursor_unref(sum->text_cursor);
    sum->text_cursor = NULL;
}