source: trunk/third/gnome-applets/gweather/gweather-xml.c @ 20910

Revision 20910, 16.0 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20909, which included commits to RCS files with non-trunk default branches.
Line 
1/* gweather-xml.c - Locations.xml parsing code
2 *
3 * Copyright (C) 2004 Gareth Owen
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20
21/* There is very little error checking in the parsing code below, it relies
22 * heavily on the locations file being in the correct format.  The format is
23 * as follows:
24 *
25 * <gweather format="1.0">
26 * <region>
27 *  <name>Name of the region</name>
28 *  <country>
29 *   <name>Name of the country</name>
30 *   <location>
31 *    <name>Name of the location</name>
32 *    <code>IWIN code</code>
33 *    <zone>Forecast code (North America, Australia, UK only)</zone>
34 *    <radar>Weather.com radar map code (North America only)</radar>
35 *   </location>
36 *   <state>
37 *     <location>
38 *       ....
39 *     </location>
40 *   </state>
41 *  </country>
42 * </region>
43 * <gweather>
44 *
45 * The thing to note is that each country can either contain different locations
46 * or be split into "states" which in turn contain a list of locations.
47 *
48 */
49
50#include <string.h>
51#include <locale.h>
52#include <gtk/gtk.h>
53#include <libxml/xmlreader.h>
54
55#include "weather.h"
56#include "gweather-pref.h"
57
58
59/* Location of the xml file */
60#define GWEATHER_XML_LOCATION "gweather/Locations.xml"
61
62/* If you change the format of the Locations.xml file that would break the current
63 * parsing then make sure you also add an extra define and maintain support for
64 * old format.
65 */
66#define GWEATHER_XML_FORMAT_1_0   "1.0"
67
68
69/* XML Node names */
70#define GWEATHER_XML_NODE_GWEATHER   "gweather"
71#define GWEATHER_XML_NODE_REGION     "region"
72#define GWEATHER_XML_NODE_COUNTRY    "country"
73#define GWEATHER_XML_NODE_STATE      "state"
74#define GWEATHER_XML_NODE_LOC        "location"
75#define GWEATHER_XML_NODE_NAME       "name"
76#define GWEATHER_XML_NODE_CODE       "code"
77#define GWEATHER_XML_NODE_ZONE       "zone"
78#define GWEATHER_XML_NODE_RADAR      "radar"
79
80
81/* XML Attributes */
82#define GWEATHER_XML_ATTR_FORMAT    "format"
83
84/*****************************************************************************
85 * Func:    gweather_xml_location_sort_func()
86 * Desc:    compare two locales to see if they match
87 * Parm:    model:      tree
88 *          a,b:        iterators to sort
89 *          user_data:  user data (unused)
90 */
91static gint gweather_xml_location_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
92{
93    gint res;
94    gchar *name_a, *name_b;
95    gchar *fold_a, *fold_b;
96       
97    gtk_tree_model_get (model, a, GWEATHER_PREF_COL_LOC, &name_a, -1);
98    gtk_tree_model_get (model, b, GWEATHER_PREF_COL_LOC, &name_b, -1);
99       
100    fold_a = g_utf8_casefold(name_a, -1);
101    fold_b = g_utf8_casefold(name_b, -1);
102       
103    res = g_utf8_collate(fold_a, fold_b);
104   
105    g_free(name_a);
106    g_free(name_b);
107    g_free(fold_a);
108    g_free(fold_b);
109   
110    return res;
111}
112 
113/*****************************************************************************
114 * Func:    gweather_xml_compare_locale()
115 * Desc:    compare two locales to see if they match
116 * Parm:    a, b: the two locales to compare
117 */
118static gint gweather_xml_compare_locale(gconstpointer a, gconstpointer b)
119{
120    return (strcmp(a, b));
121}
122
123/*****************************************************************************
124 * Func:    gweather_xml_get_value()
125 * Desc:    Extract the value from the xml
126 * Parm:    reader: xml text reader
127 */
128static xmlChar* gweather_xml_get_value (xmlTextReaderPtr reader)
129{
130    int ret, type;
131    xmlChar* value;
132
133    /* check for null node */
134    if ( xmlTextReaderIsEmptyElement (reader) ) {
135        return NULL;
136    }
137   
138    value = NULL;
139    /* the next "node" is the text node containing the value we want to get */
140    if (xmlTextReaderRead (reader) == 1) {
141        value = xmlTextReaderValue (reader);
142    }
143
144    /* move on to the end of this node */
145    while ( (xmlTextReaderRead(reader) == 1) &&
146            (xmlTextReaderNodeType (reader) != XML_READER_TYPE_END_ELEMENT) ) {
147    }
148   
149    return value;
150}
151
152/*****************************************************************************
153 * Func:    gweather_xml_parse_name()
154 * Desc:    Extract the name from the xml and add it to the tree
155 * Parm:
156 *      *locale         current locale
157 *      *tree:          tree to view locations
158 *      *loc:           currently selected location
159 *      reader:         xml text reader
160 *      *iter:          iterator to named node
161 *      **untrans_name: returns untranslated name
162 *      **trans_name:   returns translated name
163 *      *pref:          the preference of the current translation, the smaller the better
164 */
165static void gweather_xml_parse_name (const GList *locale, GtkTreeStore *tree, GtkTreeIter* iter, xmlTextReaderPtr reader, xmlChar **untrans_name, xmlChar **trans_name, gint *pref)
166{
167    int ret, type;
168    xmlChar *lang;
169    GList   *found_locale;
170    gint    position;
171   
172    /* First let's get the language */
173    lang = xmlTextReaderXmlLang (reader);
174   
175    /* the next "node" is text node containing the actual name */
176    ret = xmlTextReaderRead (reader);
177    if (ret == 1) {
178       
179        /* Get the name, if this is the untranslated name, or the locale matches
180         * the language
181         */
182        if ( lang == NULL ) {
183            *untrans_name = xmlTextReaderValue (reader);
184            /* set the column entry, incase there is no translation */
185            gtk_tree_store_set (tree, iter, GWEATHER_PREF_COL_LOC, *untrans_name, -1);
186        } else {
187            found_locale = g_list_find_custom((GList*)locale, lang, gweather_xml_compare_locale);
188            if (found_locale) {
189                position = g_list_position((GList*)locale, found_locale);
190               
191                /* If this is higer up the preference list then use it */
192                if ( (*pref == -1) || (*pref > position) ) {
193                    *trans_name = xmlTextReaderValue (reader);
194                    gtk_tree_store_set (tree, iter, GWEATHER_PREF_COL_LOC, *trans_name, -1);
195                    *pref = position;
196                }
197            }
198        }
199    }
200
201    xmlFree (lang);
202   
203    /* move on to the end of this node */
204    while ( (xmlTextReaderRead(reader) == 1) &&
205            (xmlTextReaderNodeType (reader) != XML_READER_TYPE_END_ELEMENT) ) {
206    }
207}
208
209
210/*****************************************************************************
211 * Func:    gweather_xml_parse_location()
212 * Desc:    Parse a location node, creating a leaf for it in the tree
213 * Parm:
214 *      *locale         current locale
215 *      *tree:          tree to view locations
216 *      *loc:           currently selected location
217 *      reader:         xml text reader
218 *      *iter:          Changed to iterator for this node
219 *      *p_iter:        parent node's iterator
220 *      *s_iter:        sibling node's iterator (who this node appears after)
221 */
222static void gweather_xml_parse_location (const GList* locale, GtkTreeView *tree, WeatherLocation *loc, xmlTextReaderPtr reader, GtkTreeIter* iter, GtkTreeIter* r_iter, GtkTreeIter* c_iter)
223{
224    int ret, type;
225    xmlChar *name, *untrans_name, *trans_name, *code, *zone, *radar;
226    GtkTreeStore *store;
227    WeatherLocation* new_loc;
228    gint pref;
229   
230    /* check for an empty element */
231    if ( xmlTextReaderIsEmptyElement (reader) ) {
232        return;
233    }
234   
235    /* initialise variables */
236    store = GTK_TREE_STORE (gtk_tree_view_get_model (tree));
237    untrans_name = NULL;
238    trans_name = NULL;
239    code = NULL;
240    zone = NULL;
241    radar = NULL;
242    pref = -1;
243
244    /* create a node in the tree for this region */
245    gtk_tree_store_insert_after(store, iter, r_iter, c_iter);
246
247    /* Loop through any sub elements (ie until we get to a close for this element */
248    ret = xmlTextReaderRead(reader);
249    type = XML_READER_TYPE_ELEMENT;
250    while ( (ret == 1) && (type != XML_READER_TYPE_END_ELEMENT) ) {
251               
252        type = xmlTextReaderNodeType (reader);
253       
254        /* skip non-element types */       
255        if ( type == XML_READER_TYPE_ELEMENT ) {
256            name = xmlTextReaderName (reader);
257           
258            if ( strcmp (name, GWEATHER_XML_NODE_NAME) == 0 ) {
259                gweather_xml_parse_name (locale, store, iter, reader, &untrans_name, &trans_name, &pref);
260            } else if ( strcmp (name, GWEATHER_XML_NODE_CODE) == 0 ) {
261                code = gweather_xml_get_value (reader);
262            } else if ( strcmp (name, GWEATHER_XML_NODE_ZONE) == 0 ) {
263                zone = gweather_xml_get_value (reader);
264            } else if ( strcmp (name, GWEATHER_XML_NODE_RADAR) == 0 ) {
265                radar = gweather_xml_get_value (reader);
266            }
267           
268            xmlFree (name);
269        }
270       
271        ret = xmlTextReaderRead(reader);
272
273    }
274   
275    /* Add an entry in the tree for the location */
276    new_loc = weather_location_new(untrans_name, trans_name, code, zone, radar);
277    gtk_tree_store_set (store, iter, GWEATHER_PREF_COL_POINTER, new_loc, -1);
278
279    /* Free xml attributes */
280    xmlFree(untrans_name);
281    xmlFree(trans_name);
282    xmlFree(code);
283    xmlFree(zone);
284    xmlFree(radar);
285
286    /* If this location is actually the currently selected one, select it */
287    if ( loc && weather_location_equal (new_loc, loc) ) {
288       
289        GtkTreePath *path;
290
291        path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter);
292        gtk_tree_view_expand_to_path (tree, path);
293        gtk_tree_view_set_cursor (tree, path, NULL, FALSE);
294        gtk_tree_view_scroll_to_cell (tree, path, NULL, TRUE, 0.5, 0.5);
295        gtk_tree_path_free (path);
296    }
297}
298
299/*****************************************************************************
300 * Func:    gweather_xml_parse_node()
301 * Desc:    Parse a region/country/state node, creating a leaf for it in the tree
302 *          then parsing any sub-nodes.
303 *          This is called recursively as each node parses it's sub-nodes
304 * Parm:
305 *      *locale         current_locale
306 *      *tree:          tree to view locations
307 *      *loc:           currently selected location
308 *      reader:         xml text reader
309 *      *iter:          Changed to iterator for this node
310 *      *p_iter:        iterator to parent node
311 *      *s_iter:        iterator to last sibling node
312 */
313static void gweather_xml_parse_node (const GList *locale, GtkTreeView *tree, WeatherLocation *loc, xmlTextReaderPtr reader, GtkTreeIter* iter, GtkTreeIter* p_iter, GtkTreeIter* s_iter)
314{
315    int ret, type;
316    xmlChar *name, *untrans_name, *trans_name;
317    GtkTreeStore *model;
318    GtkTreeIter last_child;
319    gboolean first;
320    gint pref;
321
322    /* check for an empty element */
323    if ( xmlTextReaderIsEmptyElement (reader) ) {
324        return;
325    }
326       
327    /* initialise variables */
328    model = GTK_TREE_STORE (gtk_tree_view_get_model (tree));
329    first = TRUE;
330    pref = -1;
331   
332    /* create a node in the tree for this region */
333    gtk_tree_store_insert_after(model, iter, p_iter, s_iter);
334   
335    ret = xmlTextReaderRead(reader);
336    type = XML_READER_TYPE_ELEMENT;
337    while ( (ret == 1) && (type != XML_READER_TYPE_END_ELEMENT) ) {
338
339        type = xmlTextReaderNodeType (reader);
340
341        /* skip non-element types */
342        if ( type == XML_READER_TYPE_ELEMENT ) {
343            name = xmlTextReaderName (reader);
344            if ( strcmp (name, GWEATHER_XML_NODE_NAME) == 0 ) {
345                gweather_xml_parse_name (locale, model, iter, reader, &untrans_name, &trans_name, &pref);
346            } else if ( strcmp (name, GWEATHER_XML_NODE_COUNTRY) == 0 ||
347                        strcmp (name, GWEATHER_XML_NODE_STATE)   == 0 ) {
348                if (first) {
349                    gweather_xml_parse_node (locale, tree, loc, reader, &last_child, iter , NULL);
350                    first = FALSE;
351                } else {
352                    gweather_xml_parse_node (locale, tree, loc, reader, &last_child, iter, &last_child);
353                }
354               
355            } else if ( strcmp (name, GWEATHER_XML_NODE_LOC) == 0 ) {
356                if (first) {
357                    gweather_xml_parse_location (locale, tree, loc, reader, &last_child, iter, NULL);
358                    first = FALSE;
359                } else {
360                    gweather_xml_parse_location (locale, tree, loc, reader, &last_child, iter, &last_child);
361                }
362            }
363           
364            xmlFree (name);
365        }
366       
367        ret = xmlTextReaderRead (reader);
368    }   
369}
370
371/*****************************************************************************
372 * Func:    gweather_xml_parse()
373 * Desc:    Top of the xml parser
374 * Parm:
375 *      *tree:          tree to view locations
376 *      *loc:           currently selected location
377 *      reader:         xml text reader
378 *      locale:         current locale
379 */
380static void gweather_xml_parse (GtkTreeView *tree, WeatherLocation *loc, xmlTextReaderPtr reader, const GList* locale)
381{
382    int     ret, type;
383    xmlChar *name;
384    GtkTreeIter iter;
385    gboolean first;
386       
387    /* at the start there are no regions in the tree */
388    first = TRUE;
389   
390    /* We are expecting a region element anything else we simply skip */
391    for (ret = xmlTextReaderRead(reader); ret == 1; ret = xmlTextReaderRead(reader) ) {
392        type = xmlTextReaderNodeType (reader);
393        if (type == XML_READER_TYPE_ELEMENT) {
394            name = xmlTextReaderName (reader);
395            if ( strcmp(name, GWEATHER_XML_NODE_REGION) == 0 ) {
396                if (first) {
397                    gweather_xml_parse_node (locale, tree, loc, reader, &iter, NULL, NULL);
398                    first = FALSE;
399                }
400                else {
401                    gweather_xml_parse_node (locale, tree, loc, reader, &iter, NULL, &iter);
402                }
403            }
404            xmlFree (name);
405        }
406    }
407}
408
409/*****************************************************************************
410 * Func:    gweather_xml_load_locations()
411 * Desc:    Main entry point for loading the locations from the XML file
412 * Parm:
413 *      *tree:  tree to view locations
414 *      *loc:   currently selected location
415 */
416void gweather_xml_load_locations (GtkTreeView *tree, WeatherLocation *loc)
417{
418    xmlTextReaderPtr reader;
419    gchar *file;
420    int ret;
421    xmlChar *name, *format;
422        const GList *locale;
423    GtkTreeSortable *sortable;
424
425    /* Get the current locale */
426    locale = gnome_i18n_get_language_list("LC_MESSAGES");
427   
428    /* Open the xml file containing the different locations */
429    file = gnome_datadir_file (GWEATHER_XML_LOCATION);
430    g_return_if_fail (file);
431    reader = xmlNewTextReaderFilename (file);
432    g_return_if_fail (reader);
433   
434    /* The first node that is read is <gweather> */
435    ret = xmlTextReaderRead (reader);
436    if ( ret == 1 ) {
437        /* check the name and format */
438        name = xmlTextReaderName(reader);
439        if ( strcmp(GWEATHER_XML_NODE_GWEATHER, name) == 0 ) {
440            format = xmlTextReaderGetAttribute(reader, GWEATHER_XML_ATTR_FORMAT);
441            if ( format != NULL ) {
442                /* Parse depending on the format */
443                if ( strcmp (format, GWEATHER_XML_FORMAT_1_0) == 0 ) {
444                    gweather_xml_parse (tree, loc, reader, locale);
445                }
446                else {
447                    g_warning ("Unknown gweather xml format %s", format);
448                }
449                xmlFree (format);
450            }
451        }
452        xmlFree (name);
453    }
454    xmlFreeTextReader (reader);
455   
456    /* Sort the tree */
457    sortable = GTK_TREE_SORTABLE (GTK_TREE_STORE(gtk_tree_view_get_model (tree)));
458    gtk_tree_sortable_set_default_sort_func(sortable, &gweather_xml_location_sort_func, NULL, NULL);
459    gtk_tree_sortable_set_sort_column_id(sortable,GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,GTK_SORT_ASCENDING);
460}
Note: See TracBrowser for help on using the repository browser.