source: trunk/third/gnome-applets/gweather/weather.c @ 21373

Revision 21373, 82.9 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21372, which included commits to RCS files with non-trunk default branches.
Line 
1/* $Id: weather.c,v 1.1.1.4 2005-03-10 19:44:38 ghudson Exp $ */
2
3/*
4 *  Papadimitriou Spiros <spapadim+@cs.cmu.edu>
5 *
6 *  This code released under the GNU GPL.
7 *  Read the file COPYING for more information.
8 *
9 *  Weather server functions (METAR and IWIN)
10 *
11 */
12
13#ifdef HAVE_CONFIG_H
14#  include <config.h>
15#endif
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <assert.h>
20#include <string.h>
21#include <ctype.h>
22#include <math.h>
23
24#ifdef __FreeBSD__
25#include <sys/types.h>
26#endif
27#include <regex.h>
28#include <time.h>
29#include <unistd.h>
30
31#include <gnome.h>
32#include <gdk-pixbuf/gdk-pixbuf.h>
33#include <gdk-pixbuf/gdk-pixbuf-loader.h>
34#include <libgnomevfs/gnome-vfs.h>
35
36#include "weather.h"
37
38void
39close_cb (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data);
40static gchar* formatWeatherMsg (gchar* msg);
41
42/* FIXME: these global variables will cause conflicts when multiple
43** instances of the applets update at the same time
44*/
45static WeatherForecastType weather_forecast = FORECAST_STATE;
46static gboolean weather_radar = FALSE;
47
48#define DATA_SIZE 5000
49
50/* Unit conversions and names */
51
52#define TEMP_F_TO_C(f)  (((f) - 32.0) * 0.555556)
53#define TEMP_F_TO_K(f)  (TEMP_F_TO_C(f) + 273.15)
54#define TEMP_C_TO_F(c)  (((c) * 1.8) + 32.0)
55
56#define WINDSPEED_KNOTS_TO_KPH(knots)  ((knots) * 1.851965)
57#define WINDSPEED_KNOTS_TO_MPH(knots)  ((knots) * 1.150779)
58#define WINDSPEED_KNOTS_TO_MS(knots)   ((knots) * 0.514444)
59
60#define PRESSURE_INCH_TO_KPA(inch)   ((inch) * 3.386)
61#define PRESSURE_INCH_TO_HPA(inch)   ((inch) * 33.86)
62#define PRESSURE_INCH_TO_MM(inch)    ((inch) * 25.40005)
63#define PRESSURE_INCH_TO_MB(inch)    (PRESSURE_INCH_TO_HPA(inch))
64#define PRESSURE_MBAR_TO_INCH(mbar) ((mbar) * 0.02963742)
65
66#define VISIBILITY_SM_TO_KM(sm)  ((sm) * 1.609344)
67#define VISIBILITY_SM_TO_M(sm)   (VISIBILITY_SM_TO_KM(sm) * 1000)
68
69
70WeatherLocation *weather_location_new (const gchar *untrans_name, const gchar *trans_name, const gchar *code, const gchar *zone, const gchar *radar)
71{
72    WeatherLocation *location;
73
74    location = g_new(WeatherLocation, 1);
75 
76    /* untransalted name and metar code must be set */
77    location->untrans_name = g_strdup(untrans_name);
78    location->code = g_strdup(code);
79
80    /* if there is no translated name, then use the untranslated version */
81    if (trans_name) {
82        location->trans_name = g_strdup(trans_name);   
83    } else {
84        location->trans_name = g_strdup(untrans_name);
85    }
86   
87    if (zone) {   
88        location->zone = g_strdup(zone);
89    } else {
90        location->zone = g_strdup("------");
91    }
92   
93    if (radar) {
94        location->radar = g_strdup(radar);
95    } else {
96        location->radar = g_strdup("---");
97    }
98
99    if (location->zone[0] == '-') {
100        location->zone_valid = FALSE;
101    } else {
102        location->zone_valid = TRUE;
103    }
104
105    return location;
106}
107
108WeatherLocation *weather_location_config_read (PanelApplet *applet)
109{
110    WeatherLocation *location;
111    gchar *untrans_name, *trans_name, *code, *zone, *radar;
112   
113    untrans_name = panel_applet_gconf_get_string(applet, "location0", NULL);
114    if (!untrans_name) {
115        if ( g_strstr_len ("DEFAULT_LOCATION", 16, _("DEFAULT_LOCATION")) == NULL ) {
116            /* TRANSLATOR: Change this to the default location name (1st parameter) in the */
117            /* gweather/Locations file */
118            /* For example for New York (JFK) the entry is loc14=New\\ York-JFK\\ Arpt KJFK NYZ076 nyc */
119            /* so this should be translated as "New York-JFK Arpt" */
120            untrans_name = g_strdup ( _("DEFAULT_LOCATION") );
121            trans_name = g_strdup ( _( _("DEFAULT_LOCATION") ) );
122                } else {
123            untrans_name = g_strdup ("Pittsburgh");
124        }
125    } else if ( g_strstr_len ("DEFAULT_LOCATION", 16, untrans_name) ) {
126        g_free ( untrans_name );
127                untrans_name = g_strdup ("Pittsburgh");
128        trans_name = g_strdup ( _("Pittsburgh") );
129    } else {
130        /* Use the stored value */
131        trans_name = panel_applet_gconf_get_string (applet, "location4", NULL);
132    }
133
134    code = panel_applet_gconf_get_string(applet, "location1", NULL);
135    if (!code) {
136        if (g_strstr_len ("DEFAULT_CODE", 12, _("DEFAULT_CODE")) == NULL) {
137            /* TRANSLATOR: Change this to the default location code (2nd parameter) in the */
138            /* gweather/Locations file */
139            /* For example for New York (JFK) the entry is loc14=New\\ York-JFK\\ Arpt KJFK NYZ076 nyc */
140            /* so this should be translated as "KJFK" */
141            code = g_strdup ( _("DEFAULT_CODE") );
142        } else {
143            code = g_strdup ("KPIT");
144        }
145    } else if ( g_strstr_len ("DEFAULT_CODE", 12, code) ) {
146        g_free (code);
147        code = g_strdup ("KPIT");
148    }
149       
150    zone = panel_applet_gconf_get_string(applet, "location2", NULL);
151    if (!zone) {
152        if (g_strstr_len("DEFAULT_ZONE", 12, _("DEFAULT_ZONE")) == NULL) {
153            /* TRANSLATOR: Change this to the default location zone (3rd parameter) in the */
154            /* gweather/Locations file */
155            /* For example for New York (JFK) the entry is loc14=New\\ York-JFK\\ Arpt KJFK NYZ076 nyc */
156            /* so this should be translated as "NYZ076" */
157            zone = g_strdup ( _("DEFAULT_ZONE" ) );
158        } else {
159            zone = g_strdup ("PAZ021");
160        }
161    } else if ( g_strstr_len ("DEFAULT_ZONE", 12, code) ) {
162        g_free (zone);
163                zone = g_strdup ("PAZ021");
164    }
165    radar = panel_applet_gconf_get_string(applet, "location3", NULL);
166    if (!radar) {
167        if (g_strstr_len("DEFAULT_RADAR", 13, N_("DEFAULT_RADAR")) == NULL) {
168            /* Translators: Change this to the default location radar (4th parameter) in the */
169            /* gweather/Locations file */
170            /* For example for New York (JFK) the entry is loc14=New\\ York-JFK\\ Arpt KJFK NYZ076 nyc */
171            /* so this should be translated as "nyc" */
172                        radar = g_strdup ( _("DEFAULT_RADAR") );
173        } else {
174            radar = g_strdup ("pit");
175        }
176    } else if ( g_strstr_len ("DEFAULT_RADAR", 13, radar) ) {
177        g_free (radar);
178        radar = g_strdup ("pit");
179    }
180
181    location = weather_location_new(untrans_name, trans_name, code, zone, radar);
182    g_free (untrans_name);
183    g_free (trans_name);
184    g_free (code);
185    g_free (zone);
186    g_free (radar);
187
188    return location;
189}
190
191WeatherLocation *weather_location_clone (const WeatherLocation *location)
192{
193    WeatherLocation *clone;
194
195    clone = weather_location_new (location->untrans_name, location->trans_name,
196                                                   location->code, location->zone, location->radar);
197    return clone;
198}
199
200void weather_location_free (WeatherLocation *location)
201{
202    if (location) {
203        g_free (location->untrans_name);
204        g_free (location->trans_name);
205        g_free (location->code);
206        g_free (location->zone);
207        g_free (location->radar);
208   
209        g_free (location);
210    }
211}
212
213gboolean weather_location_equal (const WeatherLocation *location1, const WeatherLocation *location2)
214{
215    if (!location1->code || !location2->code)
216        return 1;
217    return ( (strcmp(location1->code, location2->code) == 0) &&
218             (strcmp(location1->untrans_name, location2->untrans_name) == 0) );   
219}
220
221static const gchar *wind_direction_str[] = {
222    N_("Variable"),
223    N_("North"), N_("North - NorthEast"), N_("Northeast"), N_("East - NorthEast"),
224    N_("East"), N_("East - Southeast"), N_("Southeast"), N_("South - Southeast"),
225    N_("South"), N_("South - Southwest"), N_("Southwest"), N_("West - Southwest"),
226    N_("West"), N_("West - Northwest"), N_("Northwest"), N_("North - Northwest")
227};
228
229const gchar *weather_wind_direction_string (WeatherWindDirection wind)
230{
231        if (wind < 0)
232                return _("Unknown");
233        if (wind >= (sizeof (wind_direction_str) / sizeof (char *)))
234                return _("Invalid");
235
236        return _(wind_direction_str[(int)wind]);
237}
238
239static const gchar *sky_str[] = {
240    N_("Clear Sky"),
241    N_("Broken clouds"),
242    N_("Scattered clouds"),
243    N_("Few clouds"),
244    N_("Overcast")
245};
246
247const gchar *weather_sky_string (WeatherSky sky)
248{
249        if (sky < 0 ||
250            sky >= (sizeof (sky_str) / sizeof (char *)))
251                return _("Invalid");
252
253        return _(sky_str[(int)sky]);
254}
255
256
257/*
258 * Even though tedious, I switched to a 2D array for weather condition
259 * strings, in order to facilitate internationalization, esp. for languages
260 * with genders.
261 */
262
263/*
264 * Almost all reportable combinations listed in
265 * http://www.crh.noaa.gov/arx/wx.tbl.html are entered below, except those
266 * having 2 qualifiers mixed together [such as "Blowing snow in vicinity"
267 * (VCBLSN), "Thunderstorm in vicinity" (VCTS), etc].
268 * Combinations that are not possible are filled in with "??".
269 * Some other exceptions not handled yet, such as "SN BLSN" which has
270 * special meaning.
271 */
272
273/*
274 * Note, magic numbers, when you change the size here, make sure to change
275 * the below function so that new values are recognized
276 */
277/*                   NONE                         VICINITY                             LIGHT                      MODERATE                      HEAVY                      SHALLOW                      PATCHES                         PARTIAL                      THUNDERSTORM                    BLOWING                      SHOWERS                         DRIFTING                      FREEZING                      */
278/*               *******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/
279static const gchar *conditions_str[24][13] = {
280/* TRANSLATOR: If you want to know what "blowing" "shallow" "partial"
281 * etc means, you can go to http://www.weather.com/glossary/ and
282 * http://www.crh.noaa.gov/arx/wx.tbl.html */
283/* NONE          */ {"??",                        "??",                                "??",                      "??",                         "??",                      "??",                        "??",                           "??",                        N_("Thunderstorm"),             "??",                        "??",                           "??",                         "??"                         },
284/* DRIZZLE       */ {N_("Drizzle"),               "??",                                N_("Light drizzle"),       N_("Moderate drizzle"),       N_("Heavy drizzle"),       "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         N_("Freezing drizzle")       },
285/* RAIN          */ {N_("Rain"),                  "??",                                N_("Light rain"),          N_("Moderate rain"),          N_("Heavy rain"),          "??",                        "??",                           "??",                        N_("Thunderstorm"),             "??",                        N_("Rain showers"),             "??",                         N_("Freezing rain")          },
286/* SNOW          */ {N_("Snow"),                  "??",                                N_("Light snow"),          N_("Moderate snow"),          N_("Heavy snow"),          "??",                        "??",                           "??",                        N_("Snowstorm"),                N_("Blowing snowfall"),      N_("Snow showers"),             N_("Drifting snow"),          "??"                         },
287/* SNOW_GRAINS   */ {N_("Snow grains"),           "??",                                N_("Light snow grains"),   N_("Moderate snow grains"),   N_("Heavy snow grains"),   "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
288/* ICE_CRYSTALS  */ {N_("Ice crystals"),          "??",                                "??",                      N_("Ice crystals"),           "??",                      "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
289/* ICE_PELLETS   */ {N_("Ice pellets"),           "??",                                N_("Few ice pellets"),     N_("Moderate ice pellets"),   N_("Heavy ice pellets"),   "??",                        "??",                           "??",                        N_("Ice pellet storm"),         "??",                        N_("Showers of ice pellets"),   "??",                         "??"                         },
290/* HAIL          */ {N_("Hail"),                  "??",                                "??",                      N_("Hail"),                   "??",                      "??",                        "??",                           "??",                        N_("Hailstorm"),                "??",                        N_("Hail showers"),             "??",                         "??",                        },
291/* SMALL_HAIL    */ {N_("Small hail"),            "??",                                "??",                      N_("Small hail"),             "??",                      "??",                        "??",                           "??",                        N_("Small hailstorm"),          "??",                        N_("Showers of small hail"),    "??",                         "??"                         },
292/* PRECIPITATION */ {N_("Unknown precipitation"), "??",                                "??",                      "??",                         "??",                      "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
293/* MIST          */ {N_("Mist"),                  "??",                                "??",                      N_("Mist"),                   "??",                      "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
294/* FOG           */ {N_("Fog"),                   N_("Fog in the vicinity") ,          "??",                      N_("Fog"),                    "??",                      N_("Shallow fog"),           N_("Patches of fog"),           N_("Partial fog"),           "??",                           "??",                        "??",                           "??",                         N_("Freezing fog")           },
295/* SMOKE         */ {N_("Smoke"),                 "??",                                "??",                      N_("Smoke"),                  "??",                      "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
296/* VOLCANIC_ASH  */ {N_("Volcanic ash"),          "??",                                "??",                      N_("Volcanic ash"),           "??",                      "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
297/* SAND          */ {N_("Sand"),                  "??",                                "??",                      N_("Sand"),                   "??",                      "??",                        "??",                           "??",                        "??",                           N_("Blowing sand"),          "",                             N_("Drifting sand"),          "??"                         },
298/* HAZE          */ {N_("Haze"),                  "??",                                "??",                      N_("Haze"),                   "??",                      "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
299/* SPRAY         */ {"??",                        "??",                                "??",                      "??",                         "??",                      "??",                        "??",                           "??",                        "??",                           N_("Blowing sprays"),        "??",                           "??",                         "??"                         },
300/* DUST          */ {N_("Dust"),                  "??",                                "??",                      N_("Dust"),                   "??",                      "??",                        "??",                           "??",                        "??",                           N_("Blowing dust"),          "??",                           N_("Drifting dust"),          "??"                         },
301/* SQUALL        */ {N_("Squall"),                "??",                                "??",                      N_("Squall"),                 "??",                      "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
302/* SANDSTORM     */ {N_("Sandstorm"),             N_("Sandstorm in the vicinity") ,    "??",                      N_("Sandstorm"),              N_("Heavy sandstorm"),     "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
303/* DUSTSTORM     */ {N_("Duststorm"),             N_("Duststorm in the vicinity") ,    "??",                      N_("Duststorm"),              N_("Heavy duststorm"),     "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
304/* FUNNEL_CLOUD  */ {N_("Funnel cloud"),          "??",                                "??",                      "??",                         "??",                      "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
305/* TORNADO       */ {N_("Tornado"),               "??",                                "??",                      "??",                         "??",                      "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         },
306/* DUST_WHIRLS   */ {N_("Dust whirls"),           N_("Dust whirls in the vicinity") ,  "??",                      N_("Dust whirls"),            "??",                      "??",                        "??",                           "??",                        "??",                           "??",                        "??",                           "??",                         "??"                         }
307};
308
309const gchar *weather_conditions_string (WeatherConditions cond)
310{
311    const gchar *str;
312
313    if (!cond.significant) {
314            return "-";
315    } else {
316            if (cond.phenomenon >= 0 &&
317                cond.phenomenon < 24 &&
318                cond.qualifier >= 0 &&
319                cond.qualifier < 13)
320                    str = _(conditions_str[(int)cond.phenomenon][(int)cond.qualifier]);
321            else
322                    str = _("Invalid");
323            return (strlen(str) > 0) ? str : "-";
324    }
325}
326
327/* Locals turned global to facilitate asynchronous HTTP requests */
328
329
330static inline gboolean requests_init (WeatherInfoFunc cb, WeatherInfo *info)
331{
332    if (info->requests_pending)
333        return FALSE;
334
335    /*g_assert(!metar_handle && !iwin_handle && !wx_handle && !met_handle);*/
336
337    info->requests_pending = TRUE;
338       
339    return TRUE;
340}
341
342static inline void request_done (GnomeVFSAsyncHandle *handle, WeatherInfo *info)
343{
344    if (!handle)
345        return;
346
347    gnome_vfs_async_close(handle, close_cb, info->applet);
348   
349    return;
350}
351
352static inline void requests_done_check (WeatherInfo *info)
353{
354    g_return_if_fail(info->requests_pending);
355
356    if (!info->metar_handle && !info->iwin_handle &&
357        !info->wx_handle && !info->met_handle &&
358        !info->bom_handle) {
359        info->requests_pending = FALSE;
360        update_finish(info);
361    }
362}
363
364void
365close_cb (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data)
366{
367        GWeatherApplet *gw_applet = (GWeatherApplet *)data;
368        WeatherInfo *info;
369
370        g_return_if_fail (gw_applet != NULL);
371        g_return_if_fail (gw_applet->gweather_info != NULL);
372
373        info = gw_applet->gweather_info;
374
375        if (result != GNOME_VFS_OK)
376                g_warning("Error closing GnomeVFSAsyncHandle.\n");
377       
378        if (handle == info->metar_handle)
379                info->metar_handle = NULL;
380        if (handle == info->iwin_handle)
381                info->iwin_handle = NULL;
382        if (handle == info->wx_handle)
383                info->wx_handle = NULL;
384        if (handle == info->met_handle)
385                info->met_handle = NULL;
386        if (handle == info->bom_handle)
387                info->bom_handle = NULL;
388       
389        requests_done_check(info);
390               
391        return;
392}
393
394#define TIME_RE_STR  "^([0-9]{6})Z$"
395#define WIND_RE_STR  "^(([0-9]{3})|VRB)([0-9]?[0-9]{2})(G[0-9]?[0-9]{2})?KT$"
396#define VIS_RE_STR   "^(([0-9]?[0-9])|(M?1/[0-9]?[0-9]))SM$"
397#define CLOUD_RE_STR "^(CLR|BKN|SCT|FEW|OVC|SKC|NSC)([0-9]{3})?(CB|TCU)?$"
398#define TEMP_RE_STR  "^(M?[0-9][0-9])/(M?(//|[0-9][0-9]))$"
399#define PRES_RE_STR  "^(A|Q)([0-9]{4})$"
400#define COND_RE_STR  "^(-|\\+)?(VC|MI|BC|PR|TS|BL|SH|DR|FZ)?(DZ|RA|SN|SG|IC|PE|GR|GS|UP|BR|FG|FU|VA|SA|HZ|PY|DU|SQ|SS|DS|PO|\\+?FC)$"
401
402#define TIME_RE   0
403#define WIND_RE   1
404#define VIS_RE    2
405#define CLOUD_RE  3
406#define TEMP_RE   4
407#define PRES_RE   5
408#define COND_RE   6
409
410#define RE_NUM   7
411
412static regex_t metar_re[RE_NUM];
413
414static void metar_init_re (void)
415{
416    static gboolean initialized = FALSE;
417    if (initialized)
418        return;
419    initialized = TRUE;
420
421    regcomp(&metar_re[TIME_RE], TIME_RE_STR, REG_EXTENDED);
422    regcomp(&metar_re[WIND_RE], WIND_RE_STR, REG_EXTENDED);
423    regcomp(&metar_re[VIS_RE], VIS_RE_STR, REG_EXTENDED);
424    regcomp(&metar_re[CLOUD_RE], CLOUD_RE_STR, REG_EXTENDED);
425    regcomp(&metar_re[TEMP_RE], TEMP_RE_STR, REG_EXTENDED);
426    regcomp(&metar_re[PRES_RE], PRES_RE_STR, REG_EXTENDED);
427    regcomp(&metar_re[COND_RE], COND_RE_STR, REG_EXTENDED);
428}
429
430static inline gint days_in_month (gint month, gint year)
431{
432    if (month == 1)
433        return ((year % 4) == 0) ? 29 : 28;
434    else if (((month <= 6) && (month % 2 == 0)) || ((month >=7) && (month % 2 != 0)))
435        return 31;
436    else
437        return 30;
438}
439
440/* FIX - there *must* be a simpler, less stupid way to do this!... */
441time_t make_time (gint date, gint hour, gint min)
442{
443    struct tm *tm;
444    struct tm tms;
445    time_t now;
446    gint loc_mday, loc_hour, gm_mday, gm_hour;
447    gint hour_diff;  /* local time = UTC - hour_diff */
448    gint is_dst;
449
450    now = time(NULL);
451
452    tm = gmtime(&now);
453    gm_mday = tm->tm_mday;
454    gm_hour = tm->tm_hour;
455    memcpy(&tms, tm, sizeof(struct tm));
456
457    tm = localtime(&now);
458    loc_mday = tm->tm_mday;
459    loc_hour = tm->tm_hour;
460    is_dst = tm->tm_isdst;
461
462    /* Estimate timezone */
463    if (gm_mday == loc_mday)
464        hour_diff = gm_hour - loc_hour;
465    else
466        if ((gm_mday == loc_mday + 1) || ((gm_mday == 1) && (loc_mday >= 27)))
467            hour_diff = gm_hour + (24 - loc_hour);
468        else
469            hour_diff = -((24 - gm_hour) + loc_hour);
470
471    /* Make time */
472    tms.tm_min  = min;
473    tms.tm_sec  = 0;
474    tms.tm_hour = hour - hour_diff;
475    tms.tm_mday = date;
476    tms.tm_isdst = is_dst;
477    if (tms.tm_hour < 0) {
478        tms.tm_hour += 24;
479        --tms.tm_mday;
480        if (tms.tm_mday < 1) {
481            --tms.tm_mon;
482            if (tms.tm_mon < 0) {
483                tms.tm_mon = 11;
484                --tms.tm_year;
485            }
486            tms.tm_mday = days_in_month(tms.tm_mon, tms.tm_year + 1900);
487        }
488    } else if (tms.tm_hour > 23) {
489        tms.tm_hour -= 24;
490        ++tms.tm_mday;
491        if (tms.tm_mday > days_in_month(tms.tm_mon, tms.tm_year + 1900)) {
492            ++tms.tm_mon;
493            if (tms.tm_mon > 11) {
494                tms.tm_mon = 0;
495                ++tms.tm_year;
496            }
497            tms.tm_mday = 1;
498        }
499    }
500
501    return mktime(&tms);
502}
503
504static gboolean metar_tok_time (gchar *tokp, WeatherInfo *info)
505{
506    gchar sday[3], shr[3], smin[3];
507    gint day, hr, min;
508
509    if (regexec(&metar_re[TIME_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
510        return FALSE;
511
512    strncpy(sday, tokp, 2);
513    sday[2] = 0;
514    day = atoi(sday);
515
516    strncpy(shr, tokp+2, 2);
517    shr[2] = 0;
518    hr = atoi(shr);
519
520    strncpy(smin, tokp+4, 2);
521    smin[2] = 0;
522    min = atoi(smin);
523
524    info->update = make_time(day, hr, min);
525
526    return TRUE;
527}
528
529#define CONST_DIGITS "0123456789"
530
531static gboolean metar_tok_wind (gchar *tokp, WeatherInfo *info)
532{
533    gchar sdir[4], sspd[4], sgust[4];
534    gint dir, spd, gust = -1;
535    gchar *gustp;
536
537    if (regexec(&metar_re[WIND_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
538        return FALSE;
539
540    strncpy(sdir, tokp, 3);
541    sdir[3] = 0;
542    dir = (!strcmp(sdir, "VRB")) ? -1 : atoi(sdir);
543
544    memset(sspd, 0, sizeof(sspd));
545    strncpy(sspd, tokp+3, strspn(tokp+3, CONST_DIGITS));
546    spd = atoi(sspd);
547
548    gustp = strchr(tokp, 'G');
549    if (gustp) {
550        memset(sgust, 0, sizeof(sgust));
551        strncpy(sgust, gustp+1, strspn(gustp+1, CONST_DIGITS));
552        gust = atoi(sgust);
553    }
554
555    if ((349 <= dir) || (dir <= 11))
556        info->wind = WIND_N;
557    else if ((12 <= dir) && (dir <= 33))
558        info->wind = WIND_NNE;
559    else if ((34 <= dir) && (dir <= 56))
560        info->wind = WIND_NE;
561    else if ((57 <= dir) && (dir <= 78))
562        info->wind = WIND_ENE;
563    else if ((79 <= dir) && (dir <= 101))
564        info->wind = WIND_E;
565    else if ((102 <= dir) && (dir <= 123))
566        info->wind = WIND_ESE;
567    else if ((124 <= dir) && (dir <= 146))
568        info->wind = WIND_SE;
569    else if ((147 <= dir) && (dir <= 168))
570        info->wind = WIND_SSE;
571    else if ((169 <= dir) && (dir <= 191))
572        info->wind = WIND_S;
573    else if ((192 <= dir) && (dir <= 213))
574        info->wind = WIND_SSW;
575    else if ((214 <= dir) && (dir <= 236))
576        info->wind = WIND_SW;
577    else if ((237 <= dir) && (dir <= 258))
578        info->wind = WIND_WSW;
579    else if ((259 <= dir) && (dir <= 281))
580        info->wind = WIND_W;
581    else if ((282 <= dir) && (dir <= 303))
582        info->wind = WIND_WNW;
583    else if ((304 <= dir) && (dir <= 326))
584        info->wind = WIND_NW;
585    else if ((327 <= dir) && (dir <= 348))
586        info->wind = WIND_NNW;
587   
588    info->windspeed = (WeatherWindSpeed)spd;
589
590    return TRUE;
591}
592
593static gboolean metar_tok_vis (gchar *tokp, WeatherInfo *info)
594{
595    gchar *pfrac, *pend;
596    gchar sval[4];
597    gint val;
598
599    if (regexec(&metar_re[VIS_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
600        return FALSE;
601
602    pfrac = strchr(tokp, '/');
603    pend = strstr(tokp, "SM");
604    memset(sval, 0, sizeof(sval));
605
606    if (pfrac) {
607        strncpy(sval, pfrac + 1, pend - pfrac - 1);
608        val = atoi(sval);
609        info->visibility = (*tokp == 'M') ? 0.001 : (1.0 / ((WeatherVisibility)val));
610    } else {
611        strncpy(sval, tokp, pend - tokp);
612        val = atoi(sval);
613        info->visibility = (WeatherVisibility)val;
614    }
615
616    return TRUE;
617}
618
619static gboolean metar_tok_cloud (gchar *tokp, WeatherInfo *info)
620{
621    gchar stype[4], salt[4];
622    gint alt = -1;
623
624    if (regexec(&metar_re[CLOUD_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
625        if (!strcmp(tokp,"CAVOK"))
626        {
627                info->sky=SKY_CLEAR;
628                return TRUE;
629        }
630        else
631                return FALSE;
632
633    strncpy(stype, tokp, 3);
634    stype[3] = 0;
635    if (strlen(tokp) == 6) {
636        strncpy(salt, tokp+3, 3);
637        salt[3] = 0;
638        alt = atoi(salt);  /* Altitude - currently unused */
639    }
640
641    if (!strcmp(stype, "CLR")) {
642        info->sky = SKY_CLEAR;
643    } else if (!strcmp(stype, "SKC")) {
644        info->sky = SKY_CLEAR;
645    } else if (!strcmp(stype, "NSC")) {
646        info->sky = SKY_CLEAR;
647    } else if (!strcmp(stype, "BKN")) {
648        info->sky = SKY_BROKEN;
649    } else if (!strcmp(stype, "SCT")) {
650        info->sky = SKY_SCATTERED;
651    } else if (!strcmp(stype, "FEW")) {
652        info->sky = SKY_FEW;
653    } else if (!strcmp(stype, "OVC")) {
654        info->sky = SKY_OVERCAST;
655    }
656
657    return TRUE;
658}
659
660static gboolean metar_tok_pres (gchar *tokp, WeatherInfo *info)
661{
662    if (regexec(&metar_re[PRES_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
663        return FALSE;
664
665    if (*tokp == 'A') {
666        gchar sintg[3], sfract[3];
667        gint intg, fract;
668
669        strncpy(sintg, tokp+1, 2);
670        sintg[2] = 0;
671        intg = atoi(sintg);
672
673        strncpy(sfract, tokp+3, 2);
674        sfract[2] = 0;
675        fract = atoi(sfract);
676
677        info->pressure = (WeatherPressure)intg + (((WeatherPressure)fract)/100.0);
678    } else {  /* *tokp == 'Q' */
679        gchar spres[5];
680        gint pres;
681
682        strncpy(spres, tokp+1, 4);
683        spres[4] = 0;
684        pres = atoi(spres);
685
686        info->pressure = PRESSURE_MBAR_TO_INCH((WeatherPressure)pres);
687    }
688
689    return TRUE;
690}
691
692/* Relative humidity computation - thanks to <Olof.Oberg@modopaper.modogroup.com> */
693
694
695static inline gdouble calc_humidity(gdouble temp, gdouble dewp)
696{
697    gdouble esat, esurf;
698
699    temp = TEMP_F_TO_C(temp);
700    dewp = TEMP_F_TO_C(dewp);
701
702    esat = 6.11 * pow(10.0, (7.5 * temp) / (237.7 + temp));
703    esurf = 6.11 * pow(10.0, (7.5 * dewp) / (237.7 + dewp));
704
705    return ((esurf/esat) * 100.0);
706}
707
708static inline gdouble calc_apparent (WeatherInfo *info)
709{
710    gdouble temp = info->temp;
711    gdouble wind = WINDSPEED_KNOTS_TO_MPH(info->wind);
712    gdouble apparent;
713
714
715    /*
716     * Wind chill calculations as of 01-Nov-2001
717     * http://www.nws.noaa.gov/om/windchill/index.shtml
718     * Some pages suggest that the formula will soon be adjusted
719     * to account for solar radiation (bright sun vs cloudy sky)
720     */
721    if (temp <= 50.0 && wind > 3.0) {
722        gdouble v = pow(wind, 0.16);
723        apparent = 35.74 + 0.6215 * temp - 35.75 * v + 0.4275 * temp * v;
724    }
725    /*
726     * Heat index calculations:
727     * http://www.srh.noaa.gov/fwd/heatindex/heat5.html
728     */
729    else if (temp >= 80.0) {
730        gdouble humidity = calc_humidity(info->temp, info->dew);
731        gdouble t2 = temp * temp;
732        gdouble h2 = humidity * humidity;
733
734#if 1
735        /*
736         * A really precise formula.  Note that overall precision is
737         * constrained by the accuracy of the instruments and that the
738         * we receive the temperature and dewpoints as integers.
739         */
740        gdouble t3 = t2 * temp;
741        gdouble h3 = h2 * temp;
742       
743        apparent = 16.923
744                  + 0.185212 * temp
745                  + 5.37941 * humidity
746                  - 0.100254 * temp * humidity
747                  + 9.41695e-3 * t2
748                  + 7.28898e-3 * h2
749                  + 3.45372e-4 * t2 * humidity
750                  - 8.14971e-4 * temp * h2
751                  + 1.02102e-5 * t2 * h2
752                  - 3.8646e-5 * t3
753                  + 2.91583e-5 * h3
754                  + 1.42721e-6 * t3 * humidity
755                  + 1.97483e-7 * temp * h3
756                  - 2.18429e-8 * t3 * h2
757                  + 8.43296e-10 * t2 * h3
758                  - 4.81975e-11 * t3 * h3;
759#else
760        /*
761         * An often cited alternative: values are within 5 degrees for
762         * most ranges between 10% and 70% humidity and to 110 degrees.
763         */
764        apparent = - 42.379
765                   +  2.04901523 * temp
766                   + 10.14333127 * humidity
767                   -  0.22475541 * temp * humidity
768                   -  6.83783e-3 * t2
769                   -  5.481717e-2 * h2
770                   +  1.22874e-3 * t2 * humidity
771                   +  8.5282e-4 * temp * h2
772                   -  1.99e-6 * t2 * h2;
773#endif
774    }
775    else {
776        apparent = temp;
777    }
778
779    return apparent;
780}
781
782
783static gboolean metar_tok_temp (gchar *tokp, WeatherInfo *info)
784{
785    gchar *ptemp, *pdew, *psep;
786
787    if (regexec(&metar_re[TEMP_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
788        return FALSE;
789
790    psep = strchr(tokp, '/');
791    *psep = 0;
792    ptemp = tokp;
793    pdew = psep + 1;
794
795    info->temp = (*ptemp == 'M') ? TEMP_C_TO_F(-atoi(ptemp+1))
796                                 : TEMP_C_TO_F(atoi(ptemp));
797    info->dew = (*pdew == 'M') ? TEMP_C_TO_F(-atoi(pdew+1))
798                               : TEMP_C_TO_F(atoi(pdew));
799    return TRUE;
800}
801
802static gboolean metar_tok_cond (gchar *tokp, WeatherInfo *info)
803{
804    gchar squal[3], sphen[4];
805    gchar *pphen;
806
807    if (regexec(&metar_re[COND_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
808        return FALSE;
809
810    if ((strlen(tokp) > 3) && ((*tokp == '+') || (*tokp == '-')))
811        ++tokp;   /* FIX */
812
813    if ((*tokp == '+') || (*tokp == '-'))
814        pphen = tokp + 1;
815    else if (strlen(tokp) < 4)
816        pphen = tokp;
817    else
818        pphen = tokp + 2;
819
820    memset(squal, 0, sizeof(squal));
821    strncpy(squal, tokp, pphen - tokp);
822    squal[pphen - tokp] = 0;
823
824    memset(sphen, 0, sizeof(sphen));
825    strncpy(sphen, pphen, sizeof(sphen));
826    sphen[sizeof(sphen)-1] = '\0';
827
828    /* Defaults */
829    info->cond.qualifier = QUALIFIER_NONE;
830    info->cond.phenomenon = PHENOMENON_NONE;
831    info->cond.significant = FALSE;
832
833    if (!strcmp(squal, "")) {
834        info->cond.qualifier = QUALIFIER_MODERATE;
835    } else if (!strcmp(squal, "-")) {
836        info->cond.qualifier = QUALIFIER_LIGHT;
837    } else if (!strcmp(squal, "+")) {
838        info->cond.qualifier = QUALIFIER_HEAVY;
839    } else if (!strcmp(squal, "VC")) {
840        info->cond.qualifier = QUALIFIER_VICINITY;
841    } else if (!strcmp(squal, "MI")) {
842        info->cond.qualifier = QUALIFIER_SHALLOW;
843    } else if (!strcmp(squal, "BC")) {
844        info->cond.qualifier = QUALIFIER_PATCHES;
845    } else if (!strcmp(squal, "PR")) {
846        info->cond.qualifier = QUALIFIER_PARTIAL;
847    } else if (!strcmp(squal, "TS")) {
848        info->cond.qualifier = QUALIFIER_THUNDERSTORM;
849    } else if (!strcmp(squal, "BL")) {
850        info->cond.qualifier = QUALIFIER_BLOWING;
851    } else if (!strcmp(squal, "SH")) {
852        info->cond.qualifier = QUALIFIER_SHOWERS;
853    } else if (!strcmp(squal, "DR")) {
854        info->cond.qualifier = QUALIFIER_DRIFTING;
855    } else if (!strcmp(squal, "FZ")) {
856        info->cond.qualifier = QUALIFIER_FREEZING;
857    } else {
858        g_return_val_if_fail(FALSE, FALSE);
859    }
860
861    if (!strcmp(sphen, "DZ")) {
862        info->cond.phenomenon = PHENOMENON_DRIZZLE;
863    } else if (!strcmp(sphen, "RA")) {
864        info->cond.phenomenon = PHENOMENON_RAIN;
865    } else if (!strcmp(sphen, "SN")) {
866        info->cond.phenomenon = PHENOMENON_SNOW;
867    } else if (!strcmp(sphen, "SG")) {
868        info->cond.phenomenon = PHENOMENON_SNOW_GRAINS;
869    } else if (!strcmp(sphen, "IC")) {
870        info->cond.phenomenon = PHENOMENON_ICE_CRYSTALS;
871    } else if (!strcmp(sphen, "PE")) {
872        info->cond.phenomenon = PHENOMENON_ICE_PELLETS;
873    } else if (!strcmp(sphen, "GR")) {
874        info->cond.phenomenon = PHENOMENON_HAIL;
875    } else if (!strcmp(sphen, "GS")) {
876        info->cond.phenomenon = PHENOMENON_SMALL_HAIL;
877    } else if (!strcmp(sphen, "UP")) {
878        info->cond.phenomenon = PHENOMENON_UNKNOWN_PRECIPITATION;
879    } else if (!strcmp(sphen, "BR")) {
880        info->cond.phenomenon = PHENOMENON_MIST;
881    } else if (!strcmp(sphen, "FG")) {
882        info->cond.phenomenon = PHENOMENON_FOG;
883    } else if (!strcmp(sphen, "FU")) {
884        info->cond.phenomenon = PHENOMENON_SMOKE;
885    } else if (!strcmp(sphen, "VA")) {
886        info->cond.phenomenon = PHENOMENON_VOLCANIC_ASH;
887    } else if (!strcmp(sphen, "SA")) {
888        info->cond.phenomenon = PHENOMENON_SAND;
889    } else if (!strcmp(sphen, "HZ")) {
890        info->cond.phenomenon = PHENOMENON_HAZE;
891    } else if (!strcmp(sphen, "PY")) {
892        info->cond.phenomenon = PHENOMENON_SPRAY;
893    } else if (!strcmp(sphen, "DU")) {
894        info->cond.phenomenon = PHENOMENON_DUST;
895    } else if (!strcmp(sphen, "SQ")) {
896        info->cond.phenomenon = PHENOMENON_SQUALL;
897    } else if (!strcmp(sphen, "SS")) {
898        info->cond.phenomenon = PHENOMENON_SANDSTORM;
899    } else if (!strcmp(sphen, "DS")) {
900        info->cond.phenomenon = PHENOMENON_DUSTSTORM;
901    } else if (!strcmp(sphen, "PO")) {
902        info->cond.phenomenon = PHENOMENON_DUST_WHIRLS;
903    } else if (!strcmp(sphen, "+FC")) {
904        info->cond.phenomenon = PHENOMENON_TORNADO;
905    } else if (!strcmp(sphen, "FC")) {
906        info->cond.phenomenon = PHENOMENON_FUNNEL_CLOUD;
907    } else {
908        g_return_val_if_fail(FALSE, FALSE);
909    }
910
911    if ((info->cond.qualifier != QUALIFIER_NONE) || (info->cond.phenomenon != PHENOMENON_NONE))
912        info->cond.significant = TRUE;
913
914    return TRUE;
915}
916
917static void metar_parse_token (gchar *tokp, gboolean in_remark, WeatherInfo *info)
918{
919    if (!in_remark) {
920        if (metar_tok_time(tokp, info))
921            return;
922        else if (metar_tok_wind(tokp, info))
923            return;
924        else if (metar_tok_vis(tokp, info))
925            return;
926        else if (metar_tok_cloud(tokp, info))
927            return;
928        else if (metar_tok_temp(tokp, info))
929            return;
930        else if (metar_tok_pres(tokp, info))
931            return;
932        else if (metar_tok_cond(tokp, info))
933            return;
934    }
935}
936
937static gboolean metar_parse (gchar *metar, WeatherInfo *info)
938{
939    gchar **toks;
940    gint ntoks;
941    gint i;
942    gboolean in_remark = FALSE;
943
944    g_return_val_if_fail(info != NULL, FALSE);
945    g_return_val_if_fail(metar != NULL, FALSE);
946
947    metar_init_re();
948
949    toks = g_strsplit(metar, " ", 0);
950
951    for (ntoks = 0;  toks[ntoks];  ntoks++)
952        if (!strcmp(toks[ntoks], "RMK"))
953            in_remark = TRUE;
954
955    for (i = ntoks-1; i >= 0;  i--)
956        if (strlen(toks[i]) > 0) {
957            if (!strcmp(toks[i], "RMK"))
958                in_remark = FALSE;
959            else
960                metar_parse_token(toks[i], in_remark, info);
961        }
962
963    g_strfreev(toks);
964
965    return TRUE;
966}
967
968static void metar_finish_read(GnomeVFSAsyncHandle *handle, GnomeVFSResult result,
969                              gpointer buffer, GnomeVFSFileSize requested,
970                              GnomeVFSFileSize body_len, gpointer data)
971{
972    GWeatherApplet *gw_applet = (GWeatherApplet *)data;
973    WeatherInfo *info;
974    WeatherLocation *loc;
975    gchar *metar, *eoln, *body, *temp;
976    gboolean success = FALSE;
977    gchar *searchkey;
978
979    g_return_if_fail(gw_applet != NULL);
980    g_return_if_fail(gw_applet->gweather_info != NULL);
981    g_return_if_fail(handle == gw_applet->gweather_info->metar_handle);
982       
983    info = gw_applet->gweather_info;
984    loc = info->location;
985    body = (gchar *)buffer;
986
987    body[body_len] = '\0';
988
989    if (info->metar_buffer == NULL)
990        info->metar_buffer = g_strdup(body);
991    else
992    {
993        temp = g_strdup(info->metar_buffer);
994        g_free(info->metar_buffer);
995        info->metar_buffer = g_strdup_printf("%s%s", temp, body);
996        g_free(temp);
997    }
998               
999    if (result == GNOME_VFS_ERROR_EOF)
1000    {
1001
1002        searchkey = g_strdup_printf("\n%s", loc->code);
1003
1004        metar = strstr(info->metar_buffer, searchkey);
1005        g_free (searchkey);
1006        if (metar == NULL) {
1007            success = FALSE;
1008        } else {
1009            metar += WEATHER_LOCATION_CODE_LEN + 2;
1010            eoln = strchr(metar, '\n');
1011            if (eoln != NULL)
1012                    *eoln = 0;
1013            success = metar_parse(metar, info);
1014            if (eoln != NULL)
1015                    *eoln = '\n';
1016        }
1017
1018        info->valid = success;
1019    }
1020    else if (result != GNOME_VFS_OK) {
1021        g_print("%s", gnome_vfs_result_to_string(result));
1022        g_warning(_("Failed to get METAR data.\n"));
1023    } else {
1024         gnome_vfs_async_read(handle, body, DATA_SIZE - 1, metar_finish_read, gw_applet);
1025         return;     
1026    }
1027   
1028    request_done(info->metar_handle, info);
1029    g_free (buffer);
1030    return;
1031}
1032
1033static void metar_finish_open (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data)
1034{
1035    GWeatherApplet *gw_applet = (GWeatherApplet *)data;
1036    WeatherInfo *info;
1037    WeatherLocation *loc;
1038    gchar *body;
1039    int body_len;
1040    gchar *metar, *eoln;
1041    gboolean success = FALSE;
1042
1043    g_return_if_fail(gw_applet != NULL);
1044    g_return_if_fail(gw_applet->gweather_info != NULL);
1045    g_return_if_fail(handle == gw_applet->gweather_info->metar_handle);
1046
1047    info = gw_applet->gweather_info;
1048   
1049    body = g_malloc0(DATA_SIZE);
1050   
1051    if (info->metar_buffer)
1052        g_free (info->metar_buffer);
1053    info->metar_buffer = NULL;
1054    loc = info->location;
1055    if (loc == NULL) {
1056            g_warning (_("WeatherInfo missing location"));
1057            request_done(info->metar_handle, info);
1058            requests_done_check(info);
1059            g_free (body);
1060            return;
1061    }
1062
1063    if (result != GNOME_VFS_OK) {
1064        g_warning(_("Failed to get METAR data.\n"));
1065        info->metar_handle = NULL;
1066        requests_done_check(info);
1067        g_free (body);
1068    } else {
1069        gnome_vfs_async_read(handle, body, DATA_SIZE - 1, metar_finish_read, gw_applet);
1070    }
1071    return;
1072}
1073
1074/* Read current conditions and fill in info structure */
1075static void metar_start_open (WeatherInfo *info)
1076{
1077    gchar *url;
1078    WeatherLocation *loc;
1079
1080    g_return_if_fail(info != NULL);
1081    info->valid = FALSE;
1082    loc = info->location;
1083    if (loc == NULL) {
1084            g_warning (_("WeatherInfo missing location"));
1085            return;
1086    }
1087
1088    url = g_strdup_printf("http://weather.noaa.gov/cgi-bin/mgetmetar.pl?cccc=%s", loc->code);
1089    gnome_vfs_async_open(&info->metar_handle, url, GNOME_VFS_OPEN_READ,
1090                         0, metar_finish_open, info->applet);
1091    g_free(url);
1092
1093}
1094
1095#define IWIN_RE_STR "([A-Z][A-Z]Z(([0-9]{3}>[0-9]{3}-)|([0-9]{3}-))+)+([0-9]{6}-)?"
1096
1097static regex_t iwin_re;
1098
1099static void iwin_init_re (void)
1100{
1101    static gboolean initialized = FALSE;
1102    if (initialized)
1103        return;
1104    initialized = TRUE;
1105
1106    regcomp(&iwin_re, IWIN_RE_STR, REG_EXTENDED);
1107}
1108
1109#define CONST_ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1110
1111static gboolean iwin_range_match (gchar *range, WeatherLocation *loc)
1112{
1113    gchar *zonep;
1114    gchar *endp;
1115    gchar zone_state[4], zone_num_str[4];
1116    gint zone_num;
1117
1118    endp = range + strcspn(range, " \t\r\n") - 1;
1119    if (strspn(endp - 6, CONST_DIGITS) == 6) {
1120        --endp;
1121        while (*endp != '-')
1122            --endp;
1123    }
1124    g_assert(range <= endp);
1125
1126    strncpy(zone_state, loc->zone, 3);
1127    zone_state[3] = 0;
1128    strncpy(zone_num_str, loc->zone + 3, 3);
1129    zone_num_str[3] = 0;
1130    zone_num = atoi(zone_num_str);
1131
1132    zonep = range;
1133    while (zonep < endp) {
1134        gchar from_str[4], to_str[4];
1135        gint from, to;
1136
1137        if (strncmp(zonep, zone_state, 3) != 0) {
1138            zonep += 3;
1139            zonep += strcspn(zonep, CONST_ALPHABET "\n");
1140            continue;
1141        }
1142
1143        zonep += 3;
1144
1145        do {
1146            strncpy(from_str, zonep, 3);
1147            from_str[3] = 0;
1148            from = atoi(from_str);
1149
1150            zonep += 3;
1151
1152            if (*zonep == '-') {
1153                ++zonep;
1154                to = from;
1155            } else if (*zonep == '>') {
1156                ++zonep;
1157                strncpy(to_str, zonep, 3);
1158                to_str[3] = 0;
1159                to = atoi(to_str);
1160                zonep += 4;
1161            } else {
1162                g_assert_not_reached();
1163                to = 0;  /* Hush compiler warning */
1164            }
1165
1166            if ((from <= zone_num) && (zone_num <= to))
1167                return TRUE;
1168
1169        } while (!isupper(*zonep) && (zonep < endp));
1170    }
1171
1172    return FALSE;
1173}
1174
1175static gchar *iwin_parse (gchar *iwin, WeatherLocation *loc)
1176{
1177    gchar *p, *rangep = NULL;
1178    regmatch_t match[1];
1179    gint ret;
1180
1181    g_return_val_if_fail(iwin != NULL, NULL);
1182    g_return_val_if_fail(loc != NULL, NULL);
1183    if (loc->untrans_name[0] == '-')
1184        return NULL;
1185       
1186    iwin_init_re();
1187
1188    p = iwin;
1189
1190    /* Strip CR from CRLF input */
1191    while ((p = strchr(p, '\r')) != NULL)
1192        *p = ' ';
1193
1194    p = iwin;
1195
1196    while ((ret = regexec(&iwin_re, p, 1, match, 0)) != REG_NOMATCH) {
1197        rangep = p + match[0].rm_so;
1198        p = strchr(rangep, '\n');
1199        if (iwin_range_match(rangep, loc))
1200        {
1201            break;
1202        }
1203    }
1204
1205    if (ret != REG_NOMATCH) {
1206        /*gchar *end = strstr(p, "\n</PRE>");
1207        if ((regexec(&iwin_re, p, 1, match, 0) != REG_NOMATCH) &&
1208            (end - p > match[0].rm_so))
1209            end = p + match[0].rm_so - 1;
1210        *end = 0;*/
1211        return g_strdup(rangep);
1212    } else {
1213        return NULL;
1214    }
1215
1216}
1217
1218static void iwin_finish_read(GnomeVFSAsyncHandle *handle, GnomeVFSResult result,
1219                             gpointer buffer, GnomeVFSFileSize requested,
1220                             GnomeVFSFileSize body_len, gpointer data)
1221{
1222    GWeatherApplet *gw_applet = (GWeatherApplet *)data;
1223    WeatherInfo *info;
1224    WeatherLocation *loc;
1225    gchar *body, *temp;
1226   
1227    g_return_if_fail(gw_applet != NULL);
1228    g_return_if_fail(gw_applet->gweather_info != NULL);
1229    g_return_if_fail(handle == gw_applet->gweather_info->iwin_handle);
1230
1231    info = gw_applet->gweather_info;
1232       
1233    info->forecast = NULL;
1234    loc = info->location;
1235    body = (gchar *)buffer;
1236    body[body_len] = '\0';
1237
1238    if (info->iwin_buffer == NULL)
1239        info->iwin_buffer = g_strdup(body);
1240    else
1241    {
1242        temp = g_strdup(info->iwin_buffer);
1243        g_free(info->iwin_buffer);
1244        info->iwin_buffer = g_strdup_printf("%s%s", temp, body);
1245        g_free(temp);
1246    }
1247       
1248    if (result == GNOME_VFS_ERROR_EOF)
1249    {
1250        info->forecast = formatWeatherMsg(g_strdup (info->iwin_buffer));
1251    }
1252    else if (result != GNOME_VFS_OK) {
1253         g_print("%s", gnome_vfs_result_to_string(result));
1254        g_warning("Failed to get IWIN data.\n");
1255    } else {
1256        gnome_vfs_async_read(handle, body, DATA_SIZE - 1, iwin_finish_read, gw_applet);
1257        return;
1258    }
1259   
1260    request_done(info->iwin_handle, info);
1261    g_free (buffer);
1262    return;
1263}
1264
1265
1266/**
1267 *  Human's don't deal well with .MONDAY...SUNNY AND BLAH BLAH.TUESDAY...THEN THIS AND THAT.WEDNESDAY...RAINY BLAH BLAH.
1268 *  This function makes it easier to read.
1269 */
1270static gchar* formatWeatherMsg (gchar* forecast) {
1271
1272    gchar* ptr = forecast;
1273    gchar* startLine = NULL;
1274
1275    while (0 != *ptr) {
1276        if (ptr[0] == '\n' && ptr[1] == '.') {
1277            if (NULL == startLine) {
1278                memmove(forecast, ptr, strlen(ptr) + 1);
1279                ptr[0] = ' ';
1280                ptr = forecast;
1281            }
1282            ptr[1] = '\n';
1283            ptr += 2;
1284            startLine = ptr;
1285
1286        } else if (ptr[0] == '.' && ptr[1] == '.' && ptr[2] == '.' && NULL != startLine) {
1287            memmove(startLine + 2, startLine, (ptr - startLine) * sizeof(gchar));
1288            startLine[0] = ' ';
1289            startLine[1] = '\n';
1290            ptr[2] = '\n';
1291
1292            ptr += 3;
1293
1294        } else if (ptr[0] == '$' && ptr[1] == '$') {
1295            ptr[0] = ptr[1] = ' ';
1296
1297        } else {
1298            ptr++;
1299        }
1300    }
1301
1302    return forecast;
1303}
1304
1305
1306static void iwin_finish_open (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data)
1307{
1308    GWeatherApplet *gw_applet = (GWeatherApplet *)data;
1309    WeatherInfo *info;
1310    WeatherLocation *loc;
1311    gchar *body;
1312    gint body_len;
1313    gchar *forecast;
1314
1315    g_return_if_fail(gw_applet != NULL);
1316    g_return_if_fail(gw_applet->gweather_info != NULL);
1317    g_return_if_fail(handle == gw_applet->gweather_info->iwin_handle);
1318
1319    info = gw_applet->gweather_info;
1320       
1321    body = g_malloc0(DATA_SIZE);
1322
1323    if (info->iwin_buffer)
1324        g_free (info->iwin_buffer);
1325    info->iwin_buffer = NULL;   
1326    if (info->forecast)
1327    g_free (info->forecast);
1328    info->forecast = NULL;
1329    loc = info->location;
1330    if (loc == NULL) {
1331            g_warning (_("WeatherInfo missing location"));
1332            request_done(info->iwin_handle, info);
1333            info->iwin_handle = NULL;
1334            requests_done_check(info);
1335            g_free (body);
1336            return;
1337    }
1338
1339    if (result != GNOME_VFS_OK) {
1340        /* forecast data is not really interesting anyway ;) */
1341          g_warning("Failed to get IWIN forecast data.\n");
1342        info->iwin_handle = NULL;
1343        requests_done_check (info);
1344        g_free (body);
1345    } else {
1346        gnome_vfs_async_read(handle, body, DATA_SIZE - 1, iwin_finish_read, gw_applet);
1347    }
1348    return;
1349}
1350
1351static char *met_reprocess(char *x, int len)
1352{
1353        char *p = x;
1354        char *o;
1355        int spacing = 0;
1356        static gchar *buf;
1357        static gint buflen = 0;
1358        gchar *lastspace = NULL;
1359        int count = 0;
1360        int lastcount = 0;
1361       
1362        if(buflen < len)
1363        {
1364                if(buf)
1365                        g_free(buf);
1366                buf=g_malloc(len + 1);
1367                buflen=len;
1368        }
1369               
1370        o=buf;
1371        x += len;       /* End mark */
1372
1373        while(*p && p < x)
1374        {
1375                if(isspace(*p))
1376                {
1377                        if(!spacing)
1378                        {
1379                                spacing = 1;
1380                                lastspace = o;
1381                                count++;
1382                                *o++ = ' ';
1383                        }
1384                        p++;
1385                        continue;
1386                }
1387                spacing = 0;
1388                if(count > 75 && lastspace)
1389                {
1390                        count = o - lastspace - 1;
1391                        *lastspace = '\n';
1392                        lastspace = NULL;
1393                }
1394
1395                if(*p=='&')
1396                {
1397                        if(strncasecmp(p, "&amp;", 5)==0)
1398                        {
1399                                *o++='&';
1400                                count++;
1401                                p+=5;
1402                                continue;
1403                        }
1404                        if(strncasecmp(p, "&lt;", 4)==0)
1405                        {
1406                                *o++='<';
1407                                count++;
1408                                p+=4;
1409                                continue;
1410                        }
1411                        if(strncasecmp(p, "&gt;", 4)==0)
1412                        {
1413                                *o++='>';
1414                                count++;
1415                                p+=4;
1416                                continue;
1417                        }
1418                }
1419                if(*p=='<')
1420                {
1421                        if(strncasecmp(p, "<BR>", 4)==0)
1422                        {
1423                                *o++='\n';
1424                                count = 0;
1425                        }
1426                        if(strncasecmp(p, "<B>", 3)==0)
1427                        {
1428                                *o++='\n';
1429                                *o++='\n';
1430                                count = 0;
1431                        }
1432                        p++;
1433                        while(*p && *p != '>')
1434                                p++;
1435                        if(*p)
1436                                p++;
1437                        continue;
1438                }
1439                *o++=*p++;
1440                count++;
1441        }
1442        *o=0;
1443        return buf;
1444}
1445
1446
1447/*
1448 * Parse the metoffice forecast info.
1449 * For gnome 3.0 we want to just embed an HTML bonobo component and
1450 * be done with this ;)
1451 */
1452
1453static gchar *met_parse (gchar *meto, WeatherLocation *loc)
1454{
1455    gchar *p;
1456    gchar *rp;
1457    gchar *r = g_strdup("Met Office Forecast\n");
1458    gchar *t;   
1459    gint i=0;
1460
1461    p = strstr(meto, "Summary: </b>");
1462    if(p == NULL)
1463            return r;
1464    p += 13;
1465    rp = strstr(p, "Text issued at:");
1466    if(rp == NULL)
1467            return r;
1468
1469    /* p to rp is the text block we want but in HTML malformat */
1470    t = g_strconcat(r, met_reprocess(p, rp-p), NULL);
1471    free(r);
1472    return t;
1473}
1474
1475static void met_finish_read(GnomeVFSAsyncHandle *handle, GnomeVFSResult result,
1476                            gpointer buffer, GnomeVFSFileSize requested,
1477                            GnomeVFSFileSize body_len, gpointer data)
1478{
1479    GWeatherApplet* gw_applet = (GWeatherApplet *)data;
1480    WeatherInfo *info;
1481    WeatherLocation *loc;
1482    gchar *body, *forecast, *temp;
1483
1484    g_return_if_fail(gw_applet != NULL);
1485        g_return_if_fail(gw_applet->gweather_info != NULL);
1486    g_return_if_fail(handle == gw_applet->gweather_info->met_handle);
1487
1488    info = gw_applet->gweather_info;
1489       
1490    info->forecast = NULL;
1491    loc = info->location;
1492    body = (gchar *)buffer;
1493    body[body_len] = '\0';
1494
1495    if (info->met_buffer == NULL)
1496        info->met_buffer = g_strdup(body);
1497    else
1498    {
1499        temp = g_strdup(info->met_buffer);
1500        g_free(info->met_buffer);
1501        info->met_buffer = g_strdup_printf("%s%s", temp, body);
1502        g_free(temp);
1503    }
1504       
1505    if (result == GNOME_VFS_ERROR_EOF)
1506    {
1507        forecast = met_parse(info->met_buffer, loc);
1508        info->forecast = forecast;
1509    }
1510    else if (result != GNOME_VFS_OK) {
1511        g_print("%s", gnome_vfs_result_to_string(result));
1512        info->met_handle = NULL;
1513        requests_done_check (info);
1514        g_warning("Failed to get Met Office data.\n");
1515    } else {
1516        gnome_vfs_async_read(handle, body, DATA_SIZE - 1, met_finish_read, gw_applet);
1517        return;
1518    }
1519   
1520    request_done(info->met_handle, info);
1521    g_free (buffer);
1522    return;
1523}
1524
1525static void met_finish_open (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data)
1526{
1527    GWeatherApplet* gw_applet = (GWeatherApplet *) data;
1528    WeatherInfo *info;
1529    WeatherLocation *loc;
1530    gchar *body;
1531    gint body_len;
1532    gchar *forecast;
1533
1534    g_return_if_fail(gw_applet != NULL);
1535    g_return_if_fail(gw_applet->gweather_info != NULL);
1536    g_return_if_fail(handle == gw_applet->gweather_info->met_handle);
1537
1538        info = gw_applet->gweather_info;
1539
1540    body = g_malloc0(DATA_SIZE);
1541
1542    info->met_buffer = NULL;
1543    if (info->forecast)
1544        g_free (info->forecast);       
1545    info->forecast = NULL;
1546    loc = info->location;
1547    g_return_if_fail(loc != NULL);
1548
1549    if (result != GNOME_VFS_OK) {
1550        g_warning("Failed to get Met Office forecast data.\n");
1551        info->met_handle = NULL;
1552        requests_done_check (info);
1553        g_free (body);
1554    } else {
1555        gnome_vfs_async_read(handle, body, DATA_SIZE - 1, met_finish_read, gw_applet);
1556    }
1557    return;
1558}
1559
1560static void metoffice_start_open (WeatherInfo *info)
1561{
1562    gchar *url;
1563    WeatherLocation *loc;
1564    loc = info->location;
1565 
1566    url = g_strdup_printf("http://www.metoffice.gov.uk/weather/europe/uk/%s.html", loc->zone+1);
1567
1568    gnome_vfs_async_open(&info->met_handle, url, GNOME_VFS_OPEN_READ,
1569                         0, met_finish_open, info->applet);
1570    g_free(url);
1571
1572    return;
1573}       
1574
1575static gchar *bom_parse (gchar *meto)
1576{
1577    gchar *p, *rp;
1578   
1579    p = strstr(meto, "<pre>");
1580    p += 5; /* skip the <pre> */
1581    rp = strstr(p, "</pre>");
1582
1583    return g_strndup(p, rp-p);
1584}
1585
1586static void bom_finish_read(GnomeVFSAsyncHandle *handle, GnomeVFSResult result,
1587                            gpointer buffer, GnomeVFSFileSize requested,
1588                            GnomeVFSFileSize body_len, gpointer data)
1589{
1590    GWeatherApplet *gw_applet = (GWeatherApplet *)data;
1591    WeatherInfo *info;
1592    WeatherLocation *loc;
1593    gchar *body, *forecast, *temp;
1594
1595    g_return_if_fail(gw_applet != NULL);
1596    g_return_if_fail(gw_applet->gweather_info != NULL);
1597    g_return_if_fail(handle == gw_applet->gweather_info->bom_handle);
1598
1599    info = gw_applet->gweather_info;
1600       
1601    info->forecast = NULL;
1602    loc = info->location;
1603    body = (gchar *)buffer;
1604    body[body_len] = '\0';
1605       
1606    if (info->bom_buffer == NULL)
1607        info->bom_buffer = g_strdup(body);
1608    else
1609    {
1610        temp = g_strdup(info->bom_buffer);
1611        g_free(info->bom_buffer);
1612        info->bom_buffer = g_strdup_printf("%s%s", temp, body);
1613        g_free(temp);
1614    }
1615       
1616    if (result == GNOME_VFS_ERROR_EOF)
1617    {
1618        forecast = bom_parse(info->bom_buffer);
1619        info->forecast = forecast;
1620    }
1621    else if (result != GNOME_VFS_OK) {
1622        info->bom_handle = NULL;
1623        requests_done_check (info);
1624        g_warning("Failed to get BOM data.\n");
1625    } else {
1626        gnome_vfs_async_read(handle, body, DATA_SIZE - 1, bom_finish_read, gw_applet);
1627
1628        return;
1629    }
1630   
1631    request_done(info->bom_handle, info);
1632    g_free (buffer);
1633    return;
1634}
1635
1636static void bom_finish_open (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data)
1637{
1638    GWeatherApplet *gw_applet = (GWeatherApplet *)data;
1639    WeatherInfo *info;
1640    WeatherLocation *loc;
1641    gchar *body;
1642    gint body_len;
1643    gchar *forecast;
1644
1645    g_return_if_fail(gw_applet != NULL);
1646    g_return_if_fail(gw_applet->gweather_info != NULL);
1647    g_return_if_fail(handle == gw_applet->gweather_info->bom_handle);
1648
1649    info = gw_applet->gweather_info;
1650
1651    body = g_malloc0(DATA_SIZE);
1652
1653    info->bom_buffer = NULL;
1654    if (info->forecast)
1655        g_free (info->forecast);       
1656    info->forecast = NULL;
1657    loc = info->location;
1658    g_return_if_fail(loc != NULL);
1659
1660    if (result != GNOME_VFS_OK) {
1661        g_warning("Failed to get BOM forecast data.\n");
1662        info->bom_handle = NULL;
1663        requests_done_check (info);
1664        g_free (body);
1665    } else {
1666        gnome_vfs_async_read(handle, body, DATA_SIZE - 1, bom_finish_read, gw_applet);
1667    }
1668    return;
1669}
1670
1671static void bom_start_open (WeatherInfo *info)
1672{
1673    gchar *url;
1674    WeatherLocation *loc;
1675    loc = info->location;
1676 
1677    url = g_strdup_printf("http://www.bom.gov.au/cgi-bin/wrap_fwo.pl?%s.txt",
1678                          loc->zone+1);
1679
1680    gnome_vfs_async_open(&info->bom_handle, url, GNOME_VFS_OPEN_READ,
1681                         0, bom_finish_open, info->applet);
1682    g_free(url);
1683
1684    return;
1685}       
1686
1687/* Get forecast into newly alloc'ed string */
1688static void iwin_start_open (WeatherInfo *info)
1689{
1690    gchar *url, *state, *zone;
1691    WeatherLocation *loc;
1692
1693    g_return_if_fail(info != NULL);
1694    loc = info->location;
1695    g_return_if_fail(loc != NULL);
1696
1697    if (loc->zone[0] == '-')
1698        return;
1699       
1700    if (loc->zone[0] == ':')    /* Met Office Region Names */
1701    {
1702        metoffice_start_open (info);
1703        return;
1704    }
1705    if (loc->zone[0] == '@')    /* Australian BOM forecasts */
1706    {
1707        bom_start_open (info);
1708        return;
1709    }
1710   
1711#if 0
1712    if (weather_forecast == FORECAST_ZONE)
1713        url = g_strdup_printf("http://iwin.nws.noaa.gov/iwin/%s/zone.html",
1714                        loc->zone);
1715    else
1716        url = g_strdup_printf("http://iwin.nws.noaa.gov/iwin/%s/state.html",
1717                        loc->zone);
1718#endif
1719   
1720    /* The zone for Pittsburgh (for example) is given as PAZ021 in the locations
1721    ** file (the PA stands for the state pennsylvania). The url used wants the state
1722    ** as pa, and the zone as lower case paz021.
1723    */
1724    zone = g_ascii_strdown (loc->zone, -1);
1725    state = g_strndup (zone, 2);
1726   
1727    url = g_strdup_printf ("http://weather.noaa.gov/pub/data/forecasts/zone/%s/%s.txt",
1728                           state, zone);
1729    g_free (zone);   
1730    g_free (state);
1731
1732    gnome_vfs_async_open(&info->iwin_handle, url, GNOME_VFS_OPEN_READ,
1733                         0, iwin_finish_open, info->applet);
1734    g_free(url);
1735
1736}
1737
1738static void wx_finish_read(GnomeVFSAsyncHandle *handle, GnomeVFSResult result,
1739                           gpointer buffer, GnomeVFSFileSize requested,
1740                           GnomeVFSFileSize body_len, gpointer data)
1741{
1742    GWeatherApplet * gw_applet = (GWeatherApplet *)data;
1743    WeatherInfo *info;
1744    WeatherLocation *loc;
1745    GdkPixmap *pixmap = NULL;
1746
1747    g_return_if_fail(gw_applet != NULL);
1748    g_return_if_fail(gw_applet->gweather_info != NULL);
1749    g_return_if_fail(handle == gw_applet->gweather_info->wx_handle);
1750
1751    info = gw_applet->gweather_info;
1752       
1753    info->radar = NULL;
1754    loc = info->location;
1755
1756    if (result == GNOME_VFS_OK && body_len != 0) {
1757        GError *error = NULL;
1758        gdk_pixbuf_loader_write (info->radar_loader, buffer, body_len, &error);
1759        if (error) {
1760            g_print ("%s \n", error->message);
1761            g_error_free (error);
1762        }
1763        gnome_vfs_async_read(handle, buffer, DATA_SIZE - 1, wx_finish_read, gw_applet);
1764        return;
1765    }
1766    else if (result == GNOME_VFS_ERROR_EOF)
1767    {
1768        GdkPixbuf *pixbuf;
1769       
1770        gdk_pixbuf_loader_close (info->radar_loader, NULL);
1771        pixbuf = gdk_pixbuf_loader_get_pixbuf (info->radar_loader);
1772        if (pixbuf != NULL) {
1773            if (info->radar)
1774                g_object_unref (info->radar);
1775            gdk_pixbuf_render_pixmap_and_mask (pixbuf, &info->radar, &info->radar_mask, 127);
1776        }
1777        g_object_unref (G_OBJECT (info->radar_loader));
1778       
1779    }
1780    else
1781    {
1782        g_print("%s", gnome_vfs_result_to_string(result));
1783        g_warning(_("Failed to get METAR data.\n"));
1784        info->wx_handle = NULL;
1785        requests_done_check (info);
1786        if(info->radar_loader)  g_object_unref (G_OBJECT (info->radar_loader));
1787    }
1788   
1789    request_done(info->wx_handle, info);
1790    g_free (buffer);   
1791    return;
1792}
1793
1794static void wx_finish_open (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data)
1795{
1796    GWeatherApplet *gw_applet = (GWeatherApplet *)data;
1797    WeatherInfo *info;
1798    WeatherLocation *loc;
1799    gchar *body;
1800    gint body_len;
1801    GdkPixmap *pixmap = NULL;
1802
1803    g_return_if_fail(gw_applet != NULL);
1804    g_return_if_fail(gw_applet->gweather_info != NULL);
1805    g_return_if_fail(handle == gw_applet->gweather_info->wx_handle);
1806
1807    info = gw_applet->gweather_info;
1808       
1809    body = g_malloc0(DATA_SIZE);
1810
1811    info->radar_buffer = NULL;
1812    info->radar = NULL;
1813    loc = info->location;
1814    g_return_if_fail(loc != NULL);
1815
1816    if (result != GNOME_VFS_OK) {
1817        g_warning("Failed to get radar map image.\n");
1818        info->wx_handle = NULL;
1819        requests_done_check (info);
1820        g_free (body);
1821    } else {
1822        gnome_vfs_async_read(handle, body, DATA_SIZE -1, wx_finish_read, gw_applet);
1823       
1824    }
1825     return;
1826}
1827
1828/* Get radar map and into newly allocated pixmap */
1829static void wx_start_open (WeatherInfo *info)
1830{
1831    gchar *url;
1832    WeatherLocation *loc;
1833   
1834    g_return_if_fail(info != NULL);
1835    info->radar = NULL;
1836    info->radar_loader = gdk_pixbuf_loader_new ();
1837    loc = info->location;
1838    g_return_if_fail(loc != NULL);
1839
1840   
1841    if (info->radar_url)
1842        url = g_strdup (info->radar_url);
1843    else {
1844         if (loc->radar[0] == '-') return;
1845         url = g_strdup_printf ("http://image.weather.com/web/radar/us_%s_closeradar_medium_usen.jpg", loc->radar);
1846    }
1847 
1848    gnome_vfs_async_open(&info->wx_handle, url, GNOME_VFS_OPEN_READ,
1849                         0, wx_finish_open, info->applet);
1850                                 
1851    g_free(url);
1852
1853    return;
1854}
1855
1856gboolean _weather_info_fill (GWeatherApplet *applet, WeatherInfo *info, WeatherLocation *location, WeatherInfoFunc cb)
1857{
1858    g_return_val_if_fail(((info == NULL) && (location != NULL)) || \
1859                         ((info != NULL) && (location == NULL)), FALSE);
1860
1861    /* FIXME: i'm not sure this works as intended anymore */
1862    if (!info) {
1863        info = g_new0(WeatherInfo, 1);
1864        info->metar_handle = NULL;
1865        info->iwin_handle = NULL;
1866        info->wx_handle = NULL;
1867        info->met_handle = NULL;
1868        info->bom_handle = NULL;
1869        info->requests_pending = FALSE;
1870        info->metar_buffer = NULL;
1871        info->iwin_buffer = NULL;
1872                info->met_buffer = NULL;
1873                info->bom_buffer = NULL;
1874        info->location = weather_location_clone(location);
1875    } else {
1876        location = info->location;
1877            if (info->forecast)
1878                g_free (info->forecast);
1879            info->forecast = NULL;
1880                if (info->radar != NULL) {
1881                        gdk_pixmap_unref (info->radar);
1882                        info->radar = NULL;
1883                }
1884    }
1885
1886    /* Update in progress */
1887    if (!requests_init(cb, info)) {
1888        return FALSE;
1889    }
1890
1891    /* Defaults (just in case...) */
1892    /* Well, no just in case anymore.  We may actually fail to fetch some
1893     * fields. */
1894    info->update = 0;
1895    info->sky = -1;
1896    info->cond.significant = FALSE;
1897    info->cond.phenomenon = PHENOMENON_NONE;
1898    info->cond.qualifier = QUALIFIER_NONE;
1899    info->temp = -1000.0;
1900    info->dew = -1000.0;
1901    info->wind = -1;
1902    info->windspeed = -1;
1903    info->pressure = -1.0;
1904    info->visibility = -1.0;
1905    info->forecast = NULL;
1906    info->radar = NULL;
1907    info->radar_url = NULL;
1908    if (applet->gweather_pref.use_custom_radar_url && applet->gweather_pref.radar) {
1909        info->radar_url = g_strdup (applet->gweather_pref.radar);
1910    }   
1911    info->metar_handle = NULL;
1912    info->iwin_handle = NULL;
1913    info->wx_handle = NULL;
1914    info->met_handle = NULL;
1915    info->bom_handle = NULL;
1916    info->requests_pending = TRUE;
1917    info->applet = applet;
1918    applet->gweather_info = info;
1919
1920    metar_start_open(info);
1921    iwin_start_open(info);
1922
1923    if (weather_radar)
1924        wx_start_open(info);
1925
1926    return TRUE;
1927}
1928
1929void weather_info_config_write (WeatherInfo *info)
1930{
1931    g_return_if_fail(info != NULL);
1932
1933    gnome_config_set_bool("valid", info->valid);
1934    gnome_config_set_int("update", info->update);
1935    gnome_config_set_int("sky", (gint)info->sky);
1936    gnome_config_set_bool("cond_significant", info->cond.significant);
1937    gnome_config_set_int("cond_phenomenon", (gint)info->cond.phenomenon);
1938    gnome_config_set_int("cond_qualifier", (gint)info->cond.qualifier);
1939    gnome_config_set_float("temp", info->temp);
1940    gnome_config_set_float("dew", info->dew);
1941    gnome_config_set_int("wind", (gint)info->wind);
1942    gnome_config_set_int("windspeed", info->windspeed);
1943    gnome_config_set_float("pressure", info->pressure);
1944    gnome_config_set_float("visibility", info->visibility);
1945    if (info->forecast)
1946        gnome_config_set_string("forecast", info->forecast);
1947    else
1948        gnome_config_set_string("forecast", "");
1949    /* info->radar = NULL; */
1950}
1951
1952WeatherInfo *weather_info_config_read (PanelApplet *applet)
1953{
1954    WeatherInfo *info = g_new(WeatherInfo, 1);
1955#if 0
1956    info->valid = gnome_config_get_bool("valid=FALSE");
1957    info->location = weather_location_config_read("location");
1958    info->update = (WeatherUpdate)gnome_config_get_int("update=0");
1959    info->sky = (WeatherSky)gnome_config_get_int("sky=0");
1960    info->cond.significant = gnome_config_get_bool("cond_significant=FALSE");
1961    info->cond.phenomenon = (WeatherConditionPhenomenon)gnome_config_get_int("cond_phenomenon=0");
1962    info->cond.qualifier = (WeatherConditionQualifier)gnome_config_get_int("cond_qualifier=0");
1963    info->temp = gnome_config_get_float("temp=0");
1964    info->dew = gnome_config_get_float("dew=0");
1965    info->wind = (WeatherWindDirection)gnome_config_get_int("wind=0");
1966    info->windspeed = gnome_config_get_int("windspeed=0");
1967    info->pressure = gnome_config_get_float("pressure=0");
1968    info->visibility = gnome_config_get_float("visibility=0");
1969    info->forecast = gnome_config_get_string("forecast=None");
1970    info->radar = NULL;
1971#endif
1972    info->valid = FALSE;
1973    info->location = weather_location_config_read(applet);
1974    info->update = (WeatherUpdate)0;
1975    info->sky = -1;
1976    info->cond.significant = FALSE;
1977    info->cond.phenomenon = (WeatherConditionPhenomenon)0;
1978    info->cond.qualifier = (WeatherConditionQualifier)0;
1979    info->temp = -1000.0;
1980    info->dew = -1000.0;
1981    info->wind = -1;
1982    info->windspeed = -1;
1983    info->pressure = -1.0;
1984    info->visibility = -1.0;
1985    info->forecast = g_strdup ("None");
1986    info->radar = NULL;  /* FIX */
1987    info->requests_pending = FALSE;
1988    info->metar_buffer = NULL;
1989    info->iwin_buffer = NULL;
1990
1991    info->met_buffer = NULL;
1992    info->bom_buffer = NULL;
1993
1994    info->met_handle = NULL;
1995    info->iwin_handle = NULL;
1996    info->met_handle = NULL;
1997    info->bom_handle = NULL;
1998
1999    return info;
2000}
2001
2002WeatherInfo *weather_info_clone (const WeatherInfo *info)
2003{
2004    WeatherInfo *clone;
2005   
2006    g_return_val_if_fail(info != NULL, NULL);
2007
2008    clone = g_new(WeatherInfo, 1);
2009
2010
2011    /* move everything */
2012    g_memmove(clone, info, sizeof(WeatherInfo));
2013
2014
2015    /* special moves */
2016    clone->location = weather_location_clone(info->location);
2017    /* This handles null correctly */
2018    clone->forecast = g_strdup(info->forecast);
2019    clone->radar_url = g_strdup (info->radar_url);
2020
2021    clone->radar = info->radar;
2022    if (clone->radar != NULL)
2023            gdk_pixmap_ref (clone->radar);
2024
2025    return clone;
2026}
2027
2028void weather_info_free (WeatherInfo *info)
2029{
2030    if (!info)
2031        return;
2032
2033    weather_location_free(info->location);
2034    info->location = NULL;
2035
2036    g_free(info->forecast);
2037    info->forecast = NULL;
2038
2039    if (info->radar != NULL) {
2040        gdk_pixmap_unref (info->radar);
2041        info->radar = NULL;
2042    }
2043       
2044        if (info->iwin_buffer)
2045            g_free (info->iwin_buffer);
2046
2047        if (info->metar_buffer)
2048            g_free (info->metar_buffer);
2049
2050    if (info->met_buffer)
2051        g_free (info->met_buffer);
2052
2053    if (info->bom_buffer)
2054        g_free (info->bom_buffer);
2055
2056    if (info->metar_handle)
2057        gnome_vfs_async_cancel (info->metar_handle);
2058
2059    if (info->iwin_handle)
2060        gnome_vfs_async_cancel (info->iwin_handle);
2061
2062    if (info->wx_handle)
2063        gnome_vfs_async_cancel (info->wx_handle);
2064
2065    if (info->met_handle)
2066        gnome_vfs_async_cancel (info->met_handle);
2067
2068    if (info->bom_handle)
2069        gnome_vfs_async_cancel (info->bom_handle);
2070       
2071    g_free(info);
2072}
2073
2074void weather_forecast_set (WeatherForecastType forecast)
2075{
2076    weather_forecast = forecast;
2077}
2078
2079WeatherForecastType weather_forecast_get (void)
2080{
2081    return weather_forecast;
2082}
2083
2084void weather_radar_set (gboolean enable)
2085{
2086    weather_radar = enable;
2087}
2088
2089gboolean weather_radar_get (void)
2090{
2091    return weather_radar;
2092}
2093
2094
2095const gchar *weather_info_get_location (WeatherInfo *info)
2096{
2097    g_return_val_if_fail(info != NULL, NULL);
2098    g_return_val_if_fail(info->location != NULL, NULL);
2099    return info->location->trans_name;
2100}
2101
2102const gchar *weather_info_get_update (WeatherInfo *info)
2103{
2104    static gchar buf[200];
2105
2106    g_return_val_if_fail(info != NULL, NULL);
2107
2108    if (!info->valid)
2109        return "-";
2110
2111    if (info->update != 0) {
2112        struct tm *tm;
2113        tm = localtime(&info->update);
2114        if (strftime(buf, sizeof(buf), _("%a, %b %d / %H:%M"), tm) <= 0)
2115                strcpy (buf, "???");
2116        buf[sizeof(buf)-1] = '\0';
2117    } else {
2118        strncpy(buf, _("Unknown observation time"), sizeof (buf));
2119        buf[sizeof(buf)-1] = '\0';
2120    }
2121
2122    return buf;
2123}
2124
2125const gchar *weather_info_get_sky (WeatherInfo *info)
2126{
2127    g_return_val_if_fail(info != NULL, NULL);
2128    if (!info->valid)
2129        return "-";
2130    if (info->sky < 0)
2131        return "Unknown";
2132    return weather_sky_string(info->sky);
2133}
2134
2135const gchar *weather_info_get_conditions (WeatherInfo *info)
2136{
2137    g_return_val_if_fail(info != NULL, NULL);
2138    if (!info->valid)
2139        return "-";
2140    return weather_conditions_string(info->cond);
2141}
2142
2143static const gchar *temperature_string (gfloat far, TempUnit to_unit, gboolean round)
2144{
2145        static gchar buf[100];
2146
2147    switch (to_unit) {
2148        case TEMP_UNIT_FAHRENHEIT:
2149                        if (!round) {
2150                if ( strcmp (_("%.1f F"), "%.1f F") != 0 ) {
2151                    /* TRANSLATOR: This is the temperature in degrees fahrenheit, use the degree */
2152                    /* symbol Unicode 00B0 if possible */
2153                    g_snprintf(buf, sizeof (buf), _("%.1f F"), far);
2154                } else {
2155                    g_snprintf(buf, sizeof (buf), "%.1f \302\260F", far);
2156                }
2157            } else {
2158                if ( strcmp (_("%dF"), "%dF") != 0 ) {
2159                    /* TRANSLATOR: This is the temperature in degrees fahrenheit, use the degree */
2160                    /* symbol Unicode 00B0 if possible */
2161                    g_snprintf(buf, sizeof (buf), _("%dF"), (int)floor(far + 0.5));
2162                } else {
2163                    g_snprintf(buf, sizeof (buf), "%d\302\260F", (int)floor(far + 0.5));
2164                }
2165            }
2166            break;
2167        case TEMP_UNIT_CENTIGRADE:
2168            if (!round) {
2169                if ( strcmp (_("%.1f C"), "%.1f C") != 0 ) {
2170                    /* TRANSLATOR: This is the temperature in degrees centigrade , use the degree */
2171                    /* symbol Unicode 00B0 if possible */
2172                    g_snprintf (buf, sizeof (buf), _("%.1f C"), TEMP_F_TO_C(far));
2173                } else {
2174                    g_snprintf (buf, sizeof (buf), "%.1f \302\260C", TEMP_F_TO_C(far));
2175                }
2176            } else {
2177                if ( strcmp (_("%dC"), "%dC") != 0 ) {
2178                    /* TRANSLATOR: This is the temperature in degrees centigrade , use the degree */
2179                    /* symbol Unicode 00B0 if possible */
2180                    g_snprintf (buf, sizeof (buf), _("%dC"), (int)floor(TEMP_F_TO_C(far) + 0.5));
2181                } else {
2182                    g_snprintf (buf, sizeof (buf), "%d\302\260C", (int)floor(TEMP_F_TO_C(far) + 0.5));
2183                                }
2184            }
2185            break;
2186        case TEMP_UNIT_KELVIN:
2187            if (!round) {
2188                /* TRANSLATOR: This is the temperature in Kelvin */
2189                g_snprintf (buf, sizeof (buf), _("%.1f K"), TEMP_F_TO_K(far));
2190            } else {
2191                /* TRANSLATOR: This is the temperature in Kelvin */
2192                g_snprintf (buf, sizeof (buf), _("%dK"), (int)floor(TEMP_F_TO_K(far)));
2193            }
2194            break;
2195
2196        case TEMP_UNIT_INVALID:
2197        case TEMP_UNIT_DEFAULT:
2198        default:
2199            g_warning("Conversion to illegal temperature unit: %d", to_unit);
2200            return (_("Unknown"));
2201            break;
2202    }
2203
2204    return buf;
2205}
2206
2207const gchar *weather_info_get_temp (WeatherInfo *info)
2208{
2209    g_return_val_if_fail(info != NULL, NULL);
2210
2211    if (!info->valid)
2212        return "-";
2213    if (info->temp < -500.0)
2214        return _("Unknown");
2215   
2216    return temperature_string (info->temp, info->applet->gweather_pref.temperature_unit, FALSE);
2217}
2218
2219const gchar *weather_info_get_dew (WeatherInfo *info)
2220{
2221    g_return_val_if_fail(info != NULL, NULL);
2222
2223    if (!info->valid)
2224        return "-";
2225    if (info->dew < -500.0)
2226        return _("Unknown");
2227
2228    return temperature_string (info->dew, info->applet->gweather_pref.temperature_unit, FALSE);
2229}
2230
2231const gchar *weather_info_get_humidity (WeatherInfo *info)
2232{
2233    static gchar buf[20];
2234    gdouble humidity;
2235    g_return_val_if_fail(info != NULL, NULL);
2236    if (!info->valid)
2237        return "-";
2238
2239    humidity = calc_humidity(info->temp, info->dew);
2240    if (humidity < 0.0)
2241        return _("Unknown");
2242
2243    /* TRANSLATOR: This is the humidity in percent */
2244    g_snprintf(buf, sizeof (buf), _("%.f%%"), humidity);
2245    return buf;
2246}
2247
2248const gchar *weather_info_get_apparent (WeatherInfo *info)
2249{
2250    gdouble apparent;
2251    g_return_val_if_fail(info != NULL, NULL);
2252    if (!info->valid)
2253        return "-";
2254
2255    apparent = calc_apparent(info);
2256    if (apparent < -500.0)
2257        return _("Unknown");
2258   
2259    return temperature_string (apparent, info->applet->gweather_pref.temperature_unit, FALSE);
2260}
2261
2262static const gchar *windspeed_string (gfloat knots, SpeedUnit to_unit)
2263{
2264    static gchar buf[100];
2265
2266    switch (to_unit) {
2267        case SPEED_UNIT_KNOTS:
2268            /* TRANSLATOR: This is the wind speed in knots */
2269            g_snprintf(buf, sizeof (buf), _("%0.1f knots"), knots);
2270            break;
2271        case SPEED_UNIT_MPH:
2272            /* TRANSLATOR: This is the wind speed in miles per hour */
2273            g_snprintf(buf, sizeof (buf), _("%.1f mph"), WINDSPEED_KNOTS_TO_MPH(knots));
2274            break;
2275        case SPEED_UNIT_KPH:
2276            /* TRANSLATOR: This is the wind speed in kilometers per hour */
2277            g_snprintf(buf, sizeof (buf), _("%.1f km/h"), WINDSPEED_KNOTS_TO_KPH(knots));
2278            break;
2279        case SPEED_UNIT_MS:
2280            /* TRANSLATOR: This is the wind speed in meters per second */
2281            g_snprintf(buf, sizeof (buf), _("%.1f m/s"), WINDSPEED_KNOTS_TO_MS(knots));
2282            break;
2283
2284        case SPEED_UNIT_INVALID:
2285        case SPEED_UNIT_DEFAULT:
2286        default:
2287            g_warning("Conversion to illegal speed unit: %d", to_unit);
2288            return _("Unknown");
2289    }
2290
2291    return buf;
2292}
2293const gchar *weather_info_get_wind (WeatherInfo *info)
2294{
2295    static gchar buf[200];
2296    g_return_val_if_fail(info != NULL, NULL);
2297    if (!info->valid)
2298        return "-";
2299    if (info->windspeed < 0.0 || info->wind < 0)
2300        return _("Unknown");
2301    if (info->windspeed == 0.00) {
2302        strncpy(buf, _("Calm"), sizeof(buf));
2303        buf[sizeof(buf)-1] = '\0';
2304    } else
2305        /* TRANSLATOR: This is 'wind direction' / 'wind speed' */
2306        g_snprintf(buf, sizeof(buf), _("%s / %s"),
2307                   weather_wind_direction_string(info->wind),
2308                   windspeed_string(info->windspeed, info->applet->gweather_pref.speed_unit));
2309    return buf;
2310}
2311
2312const gchar *weather_info_get_pressure (WeatherInfo *info)
2313{
2314    static gchar buf[100];
2315    g_return_val_if_fail(info != NULL, NULL);
2316    if (!info->valid)
2317        return "-";
2318    if (info->pressure < 0.0)
2319        return _("Unknown");
2320
2321    switch (info->applet->gweather_pref.pressure_unit) {
2322        case PRESSURE_UNIT_INCH_HG:
2323            /* TRANSLATOR: This is pressure in inches of mercury */
2324            g_snprintf (buf, sizeof (buf), _("%.2f inHg"), info->pressure);
2325            break;
2326        case PRESSURE_UNIT_MM_HG:
2327            /* TRANSLATOR: This is pressure in millimeters of mercury */
2328            g_snprintf (buf, sizeof (buf), _("%.1f mmHg"), PRESSURE_INCH_TO_MM(info->pressure));
2329            break;
2330        case PRESSURE_UNIT_KPA:
2331            /* TRANSLATOR: This is pressure in kiloPascals */
2332            g_snprintf (buf, sizeof (buf), _("%.2f kPa"), PRESSURE_INCH_TO_KPA(info->pressure));
2333            break;
2334        case PRESSURE_UNIT_HPA:
2335            /* TRANSLATOR: This is pressure in hectoPascals */
2336            g_snprintf (buf, sizeof (buf), _("%.2f hPa"), PRESSURE_INCH_TO_HPA(info->pressure));
2337            break;
2338        case PRESSURE_UNIT_MB:
2339            /* TRANSLATOR: This is pressure in millibars */
2340            g_snprintf (buf, sizeof (buf), _("%.2f mb"), PRESSURE_INCH_TO_MB(info->pressure));
2341            break;
2342
2343        case PRESSURE_UNIT_INVALID:
2344        case PRESSURE_UNIT_DEFAULT:
2345        default:
2346            g_warning("Conversion to illegal pressure unit: %d", info->applet->gweather_pref.pressure_unit);
2347            return _("Unknown");
2348    }
2349
2350    return buf;
2351}
2352
2353const gchar *weather_info_get_visibility (WeatherInfo *info)
2354{
2355    static gchar buf[100];
2356    g_return_val_if_fail(info != NULL, NULL);
2357    if (!info->valid)
2358        return "-";
2359    if (info->visibility < 0.0)
2360        return _("Unknown");
2361
2362    switch (info->applet->gweather_pref.distance_unit) {
2363        case DISTANCE_UNIT_MILES:
2364            /* TRANSLATOR: This is the visibility in miles */
2365            g_snprintf(buf, sizeof (buf), _("%.1f miles"), info->visibility);
2366            break;
2367        case DISTANCE_UNIT_KM:
2368            /* TRANSLATOR: This is the visibility in kilometers */
2369            g_snprintf(buf, sizeof (buf), _("%.1f km"), VISIBILITY_SM_TO_KM(info->visibility));
2370            break;
2371        case DISTANCE_UNIT_METERS:
2372            /* TRANSLATOR: This is the visibility in meters */
2373            g_snprintf(buf, sizeof (buf), _("%.0fm"), VISIBILITY_SM_TO_M(info->visibility));
2374            break;
2375
2376        case DISTANCE_UNIT_INVALID:
2377        case DISTANCE_UNIT_DEFAULT:
2378        default:
2379            g_warning("Conversion to illegal visibility unit: %d", info->applet->gweather_pref.pressure_unit);
2380            return _("Unknown");
2381    }
2382
2383    return buf;
2384}
2385
2386const gchar *weather_info_get_forecast (WeatherInfo *info)
2387{
2388    g_return_val_if_fail(info != NULL, NULL);
2389    return info->forecast;
2390}
2391
2392GdkPixmap *weather_info_get_radar (WeatherInfo *info)
2393{
2394    g_return_val_if_fail(info != NULL, NULL);
2395    return info->radar;
2396}
2397
2398const gchar *weather_info_get_temp_summary (WeatherInfo *info)
2399{
2400    if (!info)
2401        return NULL;
2402    if (!info->valid || info->temp < -500.0)
2403        return "--";
2404         
2405    return temperature_string (info->temp, info->applet->gweather_pref.temperature_unit, TRUE);
2406   
2407}
2408
2409gchar *weather_info_get_weather_summary (WeatherInfo *info)
2410{
2411    const gchar *buf;
2412    g_return_val_if_fail(info != NULL, NULL);
2413    if (!info->valid)
2414      return g_strdup (_("Retrieval failed"));
2415    buf = weather_info_get_conditions(info);
2416    if (!strcmp(buf, "-"))
2417        buf = weather_info_get_sky(info);
2418    return g_strdup_printf ("%s: %s", weather_info_get_location (info), buf);
2419}
2420
2421
2422static GdkPixbuf **weather_pixbufs_mini = NULL;
2423static GdkPixbuf **weather_pixbufs = NULL;
2424
2425#define PIX_UNKNOWN   0
2426#define PIX_SUN       1
2427#define PIX_SUNCLOUD  2
2428#define PIX_CLOUD     3
2429#define PIX_RAIN      4
2430#define PIX_TSTORM    5
2431#define PIX_SNOW      6
2432#define PIX_FOG       7
2433
2434#define NUM_PIX       8
2435
2436#if 0
2437static void
2438xpm_to_pixmap (char **xpm_data, GdkPixmap **pixmap, GdkBitmap **mask)
2439{
2440        GdkPixbuf *pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **)xpm_data);
2441
2442        *pixmap = NULL;
2443        *mask = NULL;
2444
2445        if (pixbuf != NULL) {
2446                gdk_pixbuf_render_pixmap_and_mask (pixbuf, pixmap, mask, 127);
2447
2448                gdk_pixbuf_unref (pixbuf);
2449        }
2450}
2451#endif
2452
2453static void init_pixbufs (void)
2454{
2455    static gboolean initialized = FALSE;
2456    GtkIconTheme *icon_theme;
2457
2458    if (initialized)
2459       return;
2460    initialized = TRUE;
2461
2462    icon_theme = gtk_icon_theme_get_default ();
2463
2464    weather_pixbufs_mini = g_new(GdkPixbuf *, NUM_PIX);
2465    weather_pixbufs_mini[PIX_UNKNOWN] = gtk_icon_theme_load_icon (icon_theme, "stock_unknown", 16, 0, NULL);
2466    weather_pixbufs_mini[PIX_SUN] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-sunny", 16, 0, NULL);
2467    weather_pixbufs_mini[PIX_SUNCLOUD] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-few-clouds", 16, 0, NULL);
2468    weather_pixbufs_mini[PIX_CLOUD] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-cloudy", 16, 0, NULL);
2469    weather_pixbufs_mini[PIX_RAIN] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-showers", 16, 0, NULL);
2470    weather_pixbufs_mini[PIX_TSTORM] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-storm", 16, 0, NULL);
2471    weather_pixbufs_mini[PIX_SNOW] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-snow", 16, 0, NULL);
2472    weather_pixbufs_mini[PIX_FOG] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-fog", 16, 0, NULL);
2473
2474    weather_pixbufs = g_new(GdkPixbuf *, NUM_PIX);
2475    weather_pixbufs[PIX_UNKNOWN] = gtk_icon_theme_load_icon (icon_theme, "stock_unknown", 48, 0, NULL);
2476    weather_pixbufs[PIX_SUN] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-sunny", 48, 0, NULL);
2477    weather_pixbufs[PIX_SUNCLOUD] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-few-clouds", 48, 0, NULL);
2478    weather_pixbufs[PIX_CLOUD] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-cloudy", 48, 0, NULL);
2479    weather_pixbufs[PIX_RAIN] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-showers", 48, 0, NULL);
2480    weather_pixbufs[PIX_TSTORM] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-storm", 48, 0, NULL);
2481    weather_pixbufs[PIX_SNOW] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-snow", 48, 0, NULL);
2482    weather_pixbufs[PIX_FOG] = gtk_icon_theme_load_icon (icon_theme, "stock_weather-fog", 48, 0, NULL);
2483}
2484
2485void _weather_info_get_pixbuf (WeatherInfo *info, gboolean mini, GdkPixbuf **pixbuf)
2486{
2487    GdkPixbuf **pixbufs;
2488    gint idx = -1;
2489
2490    g_return_if_fail(pixbuf != NULL);
2491
2492    init_pixbufs();
2493    pixbufs = mini ? weather_pixbufs_mini : weather_pixbufs;
2494
2495    if (!info || !info->valid) {
2496        idx = PIX_UNKNOWN;
2497    } else {
2498        WeatherConditions cond = info->cond;
2499        WeatherSky sky = info->sky;
2500
2501        if (cond.significant && (cond.phenomenon != PHENOMENON_NONE)) {
2502            switch (cond.qualifier) {
2503            case QUALIFIER_NONE:
2504                break;
2505            case QUALIFIER_VICINITY:
2506                break;
2507            case QUALIFIER_THUNDERSTORM:
2508                idx = PIX_TSTORM;
2509                break;
2510            case QUALIFIER_SHALLOW:
2511            case QUALIFIER_PATCHES:
2512            case QUALIFIER_PARTIAL:
2513            case QUALIFIER_BLOWING:
2514            case QUALIFIER_SHOWERS:
2515            case QUALIFIER_DRIFTING:
2516            case QUALIFIER_FREEZING:
2517                break;
2518            case QUALIFIER_LIGHT:
2519            case QUALIFIER_MODERATE:
2520            case QUALIFIER_HEAVY:
2521                break;
2522            default:
2523                g_assert_not_reached();
2524            }
2525
2526            if (idx < 0)
2527                switch (cond.phenomenon) {
2528                case PHENOMENON_DRIZZLE:
2529                case PHENOMENON_RAIN:
2530                case PHENOMENON_UNKNOWN_PRECIPITATION:
2531                    if ((cond.qualifier == QUALIFIER_SHALLOW) ||
2532                        (cond.qualifier == QUALIFIER_PATCHES) ||
2533                        (cond.qualifier == QUALIFIER_PARTIAL))
2534                        idx = PIX_RAIN;
2535                    else
2536                        idx = PIX_RAIN;
2537                    break;
2538                case PHENOMENON_SNOW:
2539                case PHENOMENON_SNOW_GRAINS:
2540                case PHENOMENON_ICE_PELLETS:
2541                case PHENOMENON_ICE_CRYSTALS:
2542                    idx = PIX_SNOW;
2543                    break;
2544                case PHENOMENON_HAIL:
2545                case PHENOMENON_SMALL_HAIL:
2546                    idx = PIX_RAIN;
2547                    break;
2548                case PHENOMENON_TORNADO:
2549                case PHENOMENON_SQUALL:
2550                    idx = PIX_TSTORM;
2551                    break;
2552                case PHENOMENON_MIST:
2553                case PHENOMENON_FOG:
2554                case PHENOMENON_SMOKE:
2555                case PHENOMENON_VOLCANIC_ASH:
2556                case PHENOMENON_SAND:
2557                case PHENOMENON_HAZE:
2558                case PHENOMENON_SPRAY:
2559                case PHENOMENON_DUST:
2560                case PHENOMENON_SANDSTORM:
2561                case PHENOMENON_DUSTSTORM:
2562                case PHENOMENON_FUNNEL_CLOUD:
2563                case PHENOMENON_DUST_WHIRLS:
2564                    idx = PIX_FOG;
2565                    break;
2566                default:
2567                    idx = PIX_UNKNOWN;
2568                }
2569        } else {
2570            switch (sky) {
2571            case SKY_CLEAR:
2572                idx = PIX_SUN;
2573                break;
2574            case SKY_BROKEN:
2575            case SKY_SCATTERED:
2576            case SKY_FEW:
2577                idx = PIX_SUNCLOUD;
2578                break;
2579            case SKY_OVERCAST:
2580                idx = PIX_CLOUD;
2581                break;
2582            default:
2583                idx = PIX_UNKNOWN;
2584            }
2585        }
2586    }
2587
2588    *pixbuf = pixbufs[idx];
2589}
Note: See TracBrowser for help on using the repository browser.