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