theme-parser.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Metacity theme parsing */
00004 
00005 /*
00006  * Copyright (C) 2001 Havoc Pennington
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License as
00010  * published by the Free Software Foundation; either version 2 of the
00011  * License, or (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00021  * 02111-1307, USA.
00022  */
00023 
00024 #include <config.h>
00025 #include "theme-parser.h"
00026 #include "util.h"
00027 #include <string.h>
00028 #include <stdlib.h>
00029 
00030 typedef enum
00031 {
00032   STATE_START,
00033   STATE_THEME,
00034   /* info section */
00035   STATE_INFO,
00036   STATE_NAME,
00037   STATE_AUTHOR,
00038   STATE_COPYRIGHT,
00039   STATE_DATE,
00040   STATE_DESCRIPTION,
00041   /* constants */
00042   STATE_CONSTANT,
00043   /* geometry */
00044   STATE_FRAME_GEOMETRY,
00045   STATE_DISTANCE,
00046   STATE_BORDER,
00047   STATE_ASPECT_RATIO,
00048   /* draw ops */
00049   STATE_DRAW_OPS,
00050   STATE_LINE,
00051   STATE_RECTANGLE,
00052   STATE_ARC,
00053   STATE_CLIP,
00054   STATE_TINT,
00055   STATE_GRADIENT,
00056   STATE_IMAGE,
00057   STATE_GTK_ARROW,
00058   STATE_GTK_BOX,
00059   STATE_GTK_VLINE,
00060   STATE_ICON,
00061   STATE_TITLE,
00062   STATE_INCLUDE, /* include another draw op list */
00063   STATE_TILE,    /* tile another draw op list */
00064   /* sub-parts of gradient */
00065   STATE_COLOR,
00066   /* frame style */
00067   STATE_FRAME_STYLE,
00068   STATE_PIECE,
00069   STATE_BUTTON,
00070   /* style set */
00071   STATE_FRAME_STYLE_SET,
00072   STATE_FRAME,
00073   /* assigning style sets to windows */
00074   STATE_WINDOW,
00075   /* and menu icons */
00076   STATE_MENU_ICON,
00077   /* fallback icons */
00078   STATE_FALLBACK
00079 } ParseState;
00080 
00081 typedef struct
00082 {
00083   GSList *states;
00084 
00085   const char *theme_name;       /* name of theme (directory it's in) */
00086   char *theme_file;             /* theme filename */
00087   char *theme_dir;              /* dir the theme is inside */
00088   MetaTheme *theme;             /* theme being parsed */
00089   guint format_version;         /* version of format of theme file */  
00090   char *name;                   /* name of named thing being parsed */
00091   MetaFrameLayout *layout;      /* layout being parsed if any */
00092   MetaDrawOpList *op_list;      /* op list being parsed if any */
00093   MetaDrawOp *op;               /* op being parsed if any */
00094   MetaFrameStyle *style;        /* frame style being parsed if any */
00095   MetaFrameStyleSet *style_set; /* frame style set being parsed if any */
00096   MetaFramePiece piece;         /* position of piece being parsed */
00097   MetaButtonType button_type;   /* type of button/menuitem being parsed */
00098   MetaButtonState button_state; /* state of button being parsed */
00099 } ParseInfo;
00100 
00101 static void set_error (GError             **err,
00102                        GMarkupParseContext *context,
00103                        int                  error_domain,
00104                        int                  error_code,
00105                        const char          *format,
00106                        ...) G_GNUC_PRINTF (5, 6);
00107 
00108 static void add_context_to_error (GError             **err,
00109                                   GMarkupParseContext *context);
00110 
00111 static void       parse_info_init (ParseInfo *info);
00112 static void       parse_info_free (ParseInfo *info);
00113 
00114 static void       push_state (ParseInfo  *info,
00115                               ParseState  state);
00116 static void       pop_state  (ParseInfo  *info);
00117 static ParseState peek_state (ParseInfo  *info);
00118 
00119 
00120 static void parse_toplevel_element  (GMarkupParseContext  *context,
00121                                      const gchar          *element_name,
00122                                      const gchar         **attribute_names,
00123                                      const gchar         **attribute_values,
00124                                      ParseInfo            *info,
00125                                      GError              **error);
00126 static void parse_info_element      (GMarkupParseContext  *context,
00127                                      const gchar          *element_name,
00128                                      const gchar         **attribute_names,
00129                                      const gchar         **attribute_values,
00130                                      ParseInfo            *info,
00131                                      GError              **error);
00132 static void parse_geometry_element  (GMarkupParseContext  *context,
00133                                      const gchar          *element_name,
00134                                      const gchar         **attribute_names,
00135                                      const gchar         **attribute_values,
00136                                      ParseInfo            *info,
00137                                      GError              **error);
00138 static void parse_draw_op_element   (GMarkupParseContext  *context,
00139                                      const gchar          *element_name,
00140                                      const gchar         **attribute_names,
00141                                      const gchar         **attribute_values,
00142                                      ParseInfo            *info,
00143                                      GError              **error);
00144 static void parse_gradient_element  (GMarkupParseContext  *context,
00145                                      const gchar          *element_name,
00146                                      const gchar         **attribute_names,
00147                                      const gchar         **attribute_values,
00148                                      ParseInfo            *info,
00149                                      GError              **error);
00150 static void parse_style_element     (GMarkupParseContext  *context,
00151                                      const gchar          *element_name,
00152                                      const gchar         **attribute_names,
00153                                      const gchar         **attribute_values,
00154                                      ParseInfo            *info,
00155                                      GError              **error);
00156 static void parse_style_set_element (GMarkupParseContext  *context,
00157                                      const gchar          *element_name,
00158                                      const gchar         **attribute_names,
00159                                      const gchar         **attribute_values,
00160                                      ParseInfo            *info,
00161                                      GError              **error);
00162 
00163 static void parse_piece_element     (GMarkupParseContext  *context,
00164                                      const gchar          *element_name,
00165                                      const gchar         **attribute_names,
00166                                      const gchar         **attribute_values,
00167                                      ParseInfo            *info,
00168                                      GError              **error);
00169 
00170 static void parse_button_element    (GMarkupParseContext  *context,
00171                                      const gchar          *element_name,
00172                                      const gchar         **attribute_names,
00173                                      const gchar         **attribute_values,
00174                                      ParseInfo            *info,
00175                                      GError              **error);
00176 
00177 static void parse_menu_icon_element (GMarkupParseContext  *context,
00178                                      const gchar          *element_name,
00179                                      const gchar         **attribute_names,
00180                                      const gchar         **attribute_values,
00181                                      ParseInfo            *info,
00182                                      GError              **error);
00183 
00184 static void start_element_handler (GMarkupParseContext  *context,
00185                                    const gchar          *element_name,
00186                                    const gchar         **attribute_names,
00187                                    const gchar         **attribute_values,
00188                                    gpointer              user_data,
00189                                    GError              **error);
00190 static void end_element_handler   (GMarkupParseContext  *context,
00191                                    const gchar          *element_name,
00192                                    gpointer              user_data,
00193                                    GError              **error);
00194 static void text_handler          (GMarkupParseContext  *context,
00195                                    const gchar          *text,
00196                                    gsize                 text_len,
00197                                    gpointer              user_data,
00198                                    GError              **error);
00199 
00200 static GMarkupParser metacity_theme_parser = {
00201   start_element_handler,
00202   end_element_handler,
00203   text_handler,
00204   NULL,
00205   NULL
00206 };
00207 
00208 static void
00209 set_error (GError             **err,
00210            GMarkupParseContext *context,
00211            int                  error_domain,
00212            int                  error_code,
00213            const char          *format,
00214            ...)
00215 {
00216   int line, ch;
00217   va_list args;
00218   char *str;
00219   
00220   g_markup_parse_context_get_position (context, &line, &ch);
00221 
00222   va_start (args, format);
00223   str = g_strdup_vprintf (format, args);
00224   va_end (args);
00225 
00226   g_set_error (err, error_domain, error_code,
00227                _("Line %d character %d: %s"),
00228                line, ch, str);
00229 
00230   g_free (str);
00231 }
00232 
00233 static void
00234 add_context_to_error (GError             **err,
00235                       GMarkupParseContext *context)
00236 {
00237   int line, ch;
00238   char *str;
00239 
00240   if (err == NULL || *err == NULL)
00241     return;
00242 
00243   g_markup_parse_context_get_position (context, &line, &ch);
00244 
00245   str = g_strdup_printf (_("Line %d character %d: %s"),
00246                          line, ch, (*err)->message);
00247   g_free ((*err)->message);
00248   (*err)->message = str;
00249 }
00250 
00251 static void
00252 parse_info_init (ParseInfo *info)
00253 {
00254   info->theme_file = NULL;
00255   info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
00256   info->theme = NULL;
00257   info->name = NULL;
00258   info->layout = NULL;
00259   info->op_list = NULL;
00260   info->op = NULL;
00261   info->style = NULL;
00262   info->style_set = NULL;
00263   info->piece = META_FRAME_PIECE_LAST;
00264   info->button_type = META_BUTTON_TYPE_LAST;
00265   info->button_state = META_BUTTON_STATE_LAST;
00266 }
00267 
00268 static void
00269 parse_info_free (ParseInfo *info)
00270 {
00271   g_free (info->theme_file);
00272   g_free (info->theme_dir);
00273 
00274   g_slist_free (info->states);
00275   
00276   if (info->theme)
00277     meta_theme_free (info->theme);
00278 
00279   if (info->layout)
00280     meta_frame_layout_unref (info->layout);
00281 
00282   if (info->op_list)
00283     meta_draw_op_list_unref (info->op_list);
00284 
00285   if (info->op)
00286     meta_draw_op_free (info->op);
00287   
00288   if (info->style)
00289     meta_frame_style_unref (info->style);
00290 
00291   if (info->style_set)
00292     meta_frame_style_set_unref (info->style_set);
00293 }
00294 
00295 static void
00296 push_state (ParseInfo  *info,
00297             ParseState  state)
00298 {
00299   info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
00300 }
00301 
00302 static void
00303 pop_state (ParseInfo *info)
00304 {
00305   g_return_if_fail (info->states != NULL);
00306   
00307   info->states = g_slist_remove (info->states, info->states->data);
00308 }
00309 
00310 static ParseState
00311 peek_state (ParseInfo *info)
00312 {
00313   g_return_val_if_fail (info->states != NULL, STATE_START);
00314 
00315   return GPOINTER_TO_INT (info->states->data);
00316 }
00317 
00318 #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
00319 
00320 typedef struct
00321 {
00322   const char  *name;
00323   const char **retloc;
00324 } LocateAttr;
00325 
00326 static gboolean
00327 locate_attributes (GMarkupParseContext *context,
00328                    const char  *element_name,
00329                    const char **attribute_names,
00330                    const char **attribute_values,
00331                    GError     **error,
00332                    const char  *first_attribute_name,
00333                    const char **first_attribute_retloc,
00334                    ...)
00335 {
00336   va_list args;
00337   const char *name;
00338   const char **retloc;
00339   int n_attrs;
00340 #define MAX_ATTRS 24
00341   LocateAttr attrs[MAX_ATTRS];
00342   gboolean retval;
00343   int i;
00344 
00345   g_return_val_if_fail (first_attribute_name != NULL, FALSE);
00346   g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
00347 
00348   retval = TRUE;
00349 
00350   n_attrs = 1;
00351   attrs[0].name = first_attribute_name;
00352   attrs[0].retloc = first_attribute_retloc;
00353   *first_attribute_retloc = NULL;
00354   
00355   va_start (args, first_attribute_retloc);
00356 
00357   name = va_arg (args, const char*);
00358   retloc = va_arg (args, const char**);
00359 
00360   while (name != NULL)
00361     {
00362       g_return_val_if_fail (retloc != NULL, FALSE);
00363 
00364       g_assert (n_attrs < MAX_ATTRS);
00365       
00366       attrs[n_attrs].name = name;
00367       attrs[n_attrs].retloc = retloc;
00368       n_attrs += 1;
00369       *retloc = NULL;      
00370 
00371       name = va_arg (args, const char*);
00372       retloc = va_arg (args, const char**);
00373     }
00374 
00375   va_end (args);
00376 
00377   i = 0;
00378   while (attribute_names[i])
00379     {
00380       int j;
00381       gboolean found;
00382 
00383       found = FALSE;
00384       j = 0;
00385       while (j < n_attrs)
00386         {
00387           if (strcmp (attrs[j].name, attribute_names[i]) == 0)
00388             {
00389               retloc = attrs[j].retloc;
00390 
00391               if (*retloc != NULL)
00392                 {
00393                   set_error (error, context,
00394                              G_MARKUP_ERROR,
00395                              G_MARKUP_ERROR_PARSE,
00396                              _("Attribute \"%s\" repeated twice on the same <%s> element"),
00397                              attrs[j].name, element_name);
00398                   retval = FALSE;
00399                   goto out;
00400                 }
00401 
00402               *retloc = attribute_values[i];
00403               found = TRUE;
00404             }
00405 
00406           ++j;
00407         }
00408 
00409       if (!found)
00410         {
00411           set_error (error, context,
00412                      G_MARKUP_ERROR,
00413                      G_MARKUP_ERROR_PARSE,
00414                      _("Attribute \"%s\" is invalid on <%s> element in this context"),
00415                      attribute_names[i], element_name);
00416           retval = FALSE;
00417           goto out;
00418         }
00419 
00420       ++i;
00421     }
00422 
00423  out:
00424   return retval;
00425 }
00426 
00427 static gboolean
00428 check_no_attributes (GMarkupParseContext *context,
00429                      const char  *element_name,
00430                      const char **attribute_names,
00431                      const char **attribute_values,
00432                      GError     **error)
00433 {
00434   if (attribute_names[0] != NULL)
00435     {
00436       set_error (error, context,
00437                  G_MARKUP_ERROR,
00438                  G_MARKUP_ERROR_PARSE,
00439                  _("Attribute \"%s\" is invalid on <%s> element in this context"),
00440                  attribute_names[0], element_name);
00441       return FALSE;
00442     }
00443 
00444   return TRUE;
00445 }
00446 
00447 #define MAX_REASONABLE 4096
00448 static gboolean
00449 parse_positive_integer (const char          *str,
00450                         int                 *val,
00451                         GMarkupParseContext *context,
00452                         MetaTheme           *theme,
00453                         GError             **error)
00454 {
00455   char *end;
00456   long l;
00457   int j;
00458 
00459   *val = 0;
00460   
00461   end = NULL;
00462   
00463   /* Is str a constant? */
00464 
00465   if (META_THEME_ALLOWS (theme, META_THEME_UBIQUITOUS_CONSTANTS) &&
00466       meta_theme_lookup_int_constant (theme, str, &j))
00467     {
00468       /* Yes. */
00469       l = j;
00470     }
00471   else
00472     {
00473       /* No. Let's try parsing it instead. */
00474 
00475       l = strtol (str, &end, 10);
00476 
00477       if (end == NULL || end == str)
00478       {
00479         set_error (error, context, G_MARKUP_ERROR,
00480                    G_MARKUP_ERROR_PARSE,
00481                    _("Could not parse \"%s\" as an integer"),
00482                    str);
00483         return FALSE;
00484       }
00485 
00486     if (*end != '\0')
00487       {
00488         set_error (error, context, G_MARKUP_ERROR,
00489                    G_MARKUP_ERROR_PARSE,
00490                    _("Did not understand trailing characters \"%s\" in string \"%s\""),
00491                    end, str);
00492         return FALSE;
00493       }
00494     }
00495 
00496   if (l < 0)
00497     {
00498       set_error (error, context, G_MARKUP_ERROR,
00499                  G_MARKUP_ERROR_PARSE,
00500                  _("Integer %ld must be positive"), l);
00501       return FALSE;
00502     }
00503 
00504   if (l > MAX_REASONABLE)
00505     {
00506       set_error (error, context, G_MARKUP_ERROR,
00507                  G_MARKUP_ERROR_PARSE,
00508                  _("Integer %ld is too large, current max is %d"),
00509                  l, MAX_REASONABLE);
00510       return FALSE;
00511     }
00512   
00513   *val = (int) l;
00514 
00515   return TRUE;
00516 }
00517 
00518 static gboolean
00519 parse_double (const char          *str,
00520               double              *val,
00521               GMarkupParseContext *context,
00522               GError             **error)
00523 {
00524   char *end;
00525 
00526   *val = 0;
00527   
00528   end = NULL;
00529   
00530   *val = g_ascii_strtod (str, &end);
00531 
00532   if (end == NULL || end == str)
00533     {
00534       set_error (error, context, G_MARKUP_ERROR,
00535                  G_MARKUP_ERROR_PARSE,
00536                  _("Could not parse \"%s\" as a floating point number"),
00537                  str);
00538       return FALSE;
00539     }
00540 
00541   if (*end != '\0')
00542     {
00543       set_error (error, context, G_MARKUP_ERROR,
00544                  G_MARKUP_ERROR_PARSE,
00545                  _("Did not understand trailing characters \"%s\" in string \"%s\""),
00546                  end, str);
00547       return FALSE;
00548     }
00549 
00550   return TRUE;
00551 }
00552 
00553 static gboolean
00554 parse_boolean (const char          *str,
00555                gboolean            *val,
00556                GMarkupParseContext *context,
00557                GError             **error)
00558 {
00559   if (strcmp ("true", str) == 0)
00560     *val = TRUE;
00561   else if (strcmp ("false", str) == 0)
00562     *val = FALSE;
00563   else
00564     {
00565       set_error (error, context, G_MARKUP_ERROR,
00566                  G_MARKUP_ERROR_PARSE,
00567                  _("Boolean values must be \"true\" or \"false\" not \"%s\""),
00568                  str);
00569       return FALSE;
00570     }
00571   
00572   return TRUE;
00573 }
00574 
00575 static gboolean
00576 parse_rounding (const char          *str,
00577                 guint               *val,
00578                 GMarkupParseContext *context,
00579                 MetaTheme           *theme,
00580                 GError             **error)
00581 {
00582   if (strcmp ("true", str) == 0)
00583     *val = 5; /* historical "true" value */
00584   else if (strcmp ("false", str) == 0)
00585     *val = 0;
00586   else
00587     {
00588       int tmp;
00589       gboolean result;
00590        if (!META_THEME_ALLOWS (theme, META_THEME_VARIED_ROUND_CORNERS))
00591          {
00592            /* Not known in this version, so bail. */
00593            set_error (error, context, G_MARKUP_ERROR,
00594                       G_MARKUP_ERROR_PARSE,
00595                       _("Boolean values must be \"true\" or \"false\" not \"%s\""),
00596                       str);
00597            return FALSE;
00598          }
00599    
00600       result = parse_positive_integer (str, &tmp, context, theme, error);
00601 
00602       *val = tmp;
00603 
00604       return result;    
00605     }
00606   
00607   return TRUE;
00608 }
00609 
00610 static gboolean
00611 parse_angle (const char          *str,
00612              double              *val,
00613              GMarkupParseContext *context,
00614              GError             **error)
00615 {
00616   if (!parse_double (str, val, context, error))
00617     return FALSE;
00618 
00619   if (*val < (0.0 - 1e6) || *val > (360.0 + 1e6))
00620     {
00621       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
00622                  _("Angle must be between 0.0 and 360.0, was %g\n"),
00623                  *val);
00624       return FALSE;
00625     }
00626 
00627   return TRUE;
00628 }
00629 
00630 static gboolean
00631 parse_alpha (const char             *str,
00632              MetaAlphaGradientSpec **spec_ret,
00633              GMarkupParseContext    *context,
00634              GError                **error)
00635 {
00636   char **split;
00637   int i;
00638   int n_alphas;
00639   MetaAlphaGradientSpec *spec;
00640 
00641   *spec_ret = NULL;
00642   
00643   split = g_strsplit (str, ":", -1);
00644 
00645   i = 0;
00646   while (split[i])
00647     ++i;
00648 
00649   if (i == 0)
00650     {
00651       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
00652                  _("Could not parse \"%s\" as a floating point number"),
00653                  str);
00654 
00655       g_strfreev (split);
00656       
00657       return FALSE;
00658     }
00659 
00660   n_alphas = i;
00661 
00662   /* FIXME allow specifying horizontal/vertical/diagonal in theme format,
00663    * once we implement vertical/diagonal in gradient.c
00664    */
00665   spec = meta_alpha_gradient_spec_new (META_GRADIENT_HORIZONTAL,
00666                                        n_alphas);
00667 
00668   i = 0;
00669   while (i < n_alphas)
00670     {
00671       double v;
00672       
00673       if (!parse_double (split[i], &v, context, error))
00674         {
00675           /* clear up, but don't set error: it was set by parse_double */
00676           g_strfreev (split);
00677           meta_alpha_gradient_spec_free (spec);
00678           
00679           return FALSE;
00680         }
00681 
00682       if (v < (0.0 - 1e-6) || v > (1.0 + 1e-6))
00683         {
00684           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
00685                      _("Alpha must be between 0.0 (invisible) and 1.0 (fully opaque), was %g\n"),
00686                      v);
00687 
00688           g_strfreev (split);
00689           meta_alpha_gradient_spec_free (spec);          
00690           
00691           return FALSE;
00692         }
00693 
00694       spec->alphas[i] = (unsigned char) (v * 255);
00695       
00696       ++i;
00697     }  
00698 
00699   g_strfreev (split);
00700   
00701   *spec_ret = spec;
00702   
00703   return TRUE;
00704 }
00705 
00706 static MetaColorSpec*
00707 parse_color (MetaTheme *theme,
00708              const char        *str,
00709              GError           **err)
00710 {
00711   char* referent;
00712 
00713   if (META_THEME_ALLOWS (theme, META_THEME_COLOR_CONSTANTS) &&
00714       meta_theme_lookup_color_constant (theme, str, &referent))
00715     {
00716       if (referent)
00717         return meta_color_spec_new_from_string (referent, err);
00718       
00719       /* no need to free referent: it's a pointer into the actual hash table */
00720     }
00721   
00722   return meta_color_spec_new_from_string (str, err);
00723 }
00724 
00725 static gboolean
00726 parse_title_scale (const char          *str,
00727                    double              *val,
00728                    GMarkupParseContext *context,
00729                    GError             **error)
00730 {
00731   double factor;
00732   
00733   if (strcmp (str, "xx-small") == 0)
00734     factor = PANGO_SCALE_XX_SMALL;
00735   else if (strcmp (str, "x-small") == 0)
00736     factor = PANGO_SCALE_X_SMALL;
00737   else if (strcmp (str, "small") == 0)
00738     factor = PANGO_SCALE_SMALL;
00739   else if (strcmp (str, "medium") == 0)
00740     factor = PANGO_SCALE_MEDIUM;
00741   else if (strcmp (str, "large") == 0)
00742     factor = PANGO_SCALE_LARGE;
00743   else if (strcmp (str, "x-large") == 0)
00744     factor = PANGO_SCALE_X_LARGE;
00745   else if (strcmp (str, "xx-large") == 0)
00746     factor = PANGO_SCALE_XX_LARGE;
00747   else
00748     {
00749       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
00750                  _("Invalid title scale \"%s\" (must be one of xx-small,x-small,small,medium,large,x-large,xx-large)\n"),
00751                  str);
00752       return FALSE;
00753     }
00754 
00755   *val = factor;
00756   
00757   return TRUE;
00758 }
00759 
00760 static void
00761 parse_toplevel_element (GMarkupParseContext  *context,
00762                         const gchar          *element_name,
00763                         const gchar         **attribute_names,
00764                         const gchar         **attribute_values,
00765                         ParseInfo            *info,
00766                         GError              **error)
00767 {
00768   g_return_if_fail (peek_state (info) == STATE_THEME);
00769 
00770   if (ELEMENT_IS ("info"))
00771     {
00772       if (!check_no_attributes (context, element_name,
00773                                 attribute_names, attribute_values,
00774                                 error))
00775         return;
00776 
00777       push_state (info, STATE_INFO);
00778     }
00779   else if (ELEMENT_IS ("constant"))
00780     {
00781       const char *name;
00782       const char *value;
00783       int ival = 0;
00784       double dval = 0.0;
00785       
00786       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
00787                               error,
00788                               "name", &name, "value", &value,
00789                               NULL))
00790         return;
00791 
00792       if (name == NULL)
00793         {
00794           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
00795                      _("No \"%s\" attribute on <%s> element"),
00796                      "name", element_name);
00797           return;
00798         }
00799       
00800       if (value == NULL)
00801         {
00802           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
00803                      _("No \"%s\" attribute on <%s> element"),
00804                      "value", element_name);
00805           return;
00806         }
00807 
00808       if (strchr (value, '.') && parse_double (value, &dval, context, error))
00809         {
00810           g_clear_error (error);
00811 
00812           if (!meta_theme_define_float_constant (info->theme,
00813                                                  name,
00814                                                  dval,
00815                                                  error))
00816             {
00817               add_context_to_error (error, context);
00818               return;
00819             }
00820         }
00821       else if (parse_positive_integer (value, &ival, context, info->theme, error))
00822         {
00823           g_clear_error (error);
00824 
00825           if (!meta_theme_define_int_constant (info->theme,
00826                                                name,
00827                                                ival,
00828                                                error))
00829             {
00830               add_context_to_error (error, context);
00831               return;
00832             }
00833         }
00834       else
00835         {
00836           g_clear_error (error);
00837 
00838           if (!meta_theme_define_color_constant (info->theme,
00839                                                  name,
00840                                                  value,
00841                                                  error))
00842             {
00843               add_context_to_error (error, context);
00844               return;
00845             }
00846         }
00847 
00848       push_state (info, STATE_CONSTANT);
00849     }
00850   else if (ELEMENT_IS ("frame_geometry"))
00851     {
00852       const char *name = NULL;
00853       const char *parent = NULL;
00854       const char *has_title = NULL;
00855       const char *title_scale = NULL;
00856       const char *rounded_top_left = NULL;
00857       const char *rounded_top_right = NULL;
00858       const char *rounded_bottom_left = NULL;
00859       const char *rounded_bottom_right = NULL;
00860       const char *hide_buttons = NULL;
00861       gboolean has_title_val;
00862       guint rounded_top_left_val;
00863       guint rounded_top_right_val;
00864       guint rounded_bottom_left_val;
00865       guint rounded_bottom_right_val;
00866       gboolean hide_buttons_val;
00867       double title_scale_val;
00868       MetaFrameLayout *parent_layout;
00869 
00870       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
00871                               error,
00872                               "name", &name, "parent", &parent,
00873                               "has_title", &has_title, "title_scale", &title_scale,
00874                               "rounded_top_left", &rounded_top_left,
00875                               "rounded_top_right", &rounded_top_right,
00876                               "rounded_bottom_left", &rounded_bottom_left,
00877                               "rounded_bottom_right", &rounded_bottom_right,
00878                               "hide_buttons", &hide_buttons,
00879                               NULL))
00880         return;
00881 
00882       if (name == NULL)
00883         {
00884           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
00885                      _("No \"%s\" attribute on <%s> element"),
00886                      "name", element_name);
00887           return;
00888         }
00889 
00890       has_title_val = TRUE;
00891       if (has_title && !parse_boolean (has_title, &has_title_val, context, error))
00892         return;
00893 
00894       hide_buttons_val = FALSE;
00895       if (hide_buttons && !parse_boolean (hide_buttons, &hide_buttons_val, context, error))
00896         return;
00897 
00898       rounded_top_left_val = 0;
00899       rounded_top_right_val = 0;
00900       rounded_bottom_left_val = 0;
00901       rounded_bottom_right_val = 0;
00902 
00903       if (rounded_top_left && !parse_rounding (rounded_top_left, &rounded_top_left_val, context, info->theme, error))
00904         return;
00905       if (rounded_top_right && !parse_rounding (rounded_top_right, &rounded_top_right_val, context, info->theme, error))
00906         return;
00907       if (rounded_bottom_left && !parse_rounding (rounded_bottom_left, &rounded_bottom_left_val, context, info->theme, error))
00908         return;      
00909       if (rounded_bottom_right && !parse_rounding (rounded_bottom_right, &rounded_bottom_right_val, context, info->theme, error))
00910         return;
00911       
00912       title_scale_val = 1.0;
00913       if (title_scale && !parse_title_scale (title_scale, &title_scale_val, context, error))
00914         return;
00915       
00916       if (meta_theme_lookup_layout (info->theme, name))
00917         {
00918           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
00919                      _("<%s> name \"%s\" used a second time"),
00920                      element_name, name);
00921           return;
00922         }
00923 
00924       parent_layout = NULL;
00925       if (parent)
00926         {
00927           parent_layout = meta_theme_lookup_layout (info->theme, parent);
00928           if (parent_layout == NULL)
00929             {
00930               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
00931                          _("<%s> parent \"%s\" has not been defined"),
00932                          element_name, parent);
00933               return;
00934             }
00935         }
00936 
00937       g_assert (info->layout == NULL);
00938 
00939       if (parent_layout)
00940         info->layout = meta_frame_layout_copy (parent_layout);
00941       else
00942         info->layout = meta_frame_layout_new ();
00943 
00944       if (has_title) /* only if explicit, otherwise inherit */
00945         info->layout->has_title = has_title_val;
00946 
00947       if (META_THEME_ALLOWS (info->theme, META_THEME_HIDDEN_BUTTONS) && hide_buttons_val)
00948           info->layout->hide_buttons = hide_buttons_val;
00949 
00950       if (title_scale)
00951         info->layout->title_scale = title_scale_val;
00952 
00953       if (rounded_top_left)
00954         info->layout->top_left_corner_rounded_radius = rounded_top_left_val;
00955 
00956       if (rounded_top_right)
00957         info->layout->top_right_corner_rounded_radius = rounded_top_right_val;
00958 
00959       if (rounded_bottom_left)
00960         info->layout->bottom_left_corner_rounded_radius = rounded_bottom_left_val;
00961 
00962       if (rounded_bottom_right)
00963         info->layout->bottom_right_corner_rounded_radius = rounded_bottom_right_val;
00964       
00965       meta_theme_insert_layout (info->theme, name, info->layout);
00966 
00967       push_state (info, STATE_FRAME_GEOMETRY);
00968     }
00969   else if (ELEMENT_IS ("draw_ops"))
00970     {
00971       const char *name = NULL;
00972 
00973       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
00974                               error,
00975                               "name", &name,
00976                               NULL))
00977         return;
00978 
00979       if (name == NULL)
00980         {
00981           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
00982                      _("No \"%s\" attribute on <%s> element"),
00983                      "name", element_name);
00984           return;
00985         }
00986 
00987       if (meta_theme_lookup_draw_op_list (info->theme, name))
00988         {
00989           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
00990                      _("<%s> name \"%s\" used a second time"),
00991                      element_name, name);
00992           return;
00993         }
00994 
00995       g_assert (info->op_list == NULL);
00996       info->op_list = meta_draw_op_list_new (2);
00997 
00998       meta_theme_insert_draw_op_list (info->theme, name, info->op_list);
00999 
01000       push_state (info, STATE_DRAW_OPS);
01001     }
01002   else if (ELEMENT_IS ("frame_style"))
01003     {
01004       const char *name = NULL;
01005       const char *parent = NULL;
01006       const char *geometry = NULL;
01007       const char *background = NULL;
01008       const char *alpha = NULL;
01009       MetaFrameStyle *parent_style;
01010       MetaFrameLayout *layout;
01011 
01012       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
01013                               error,
01014                               "name", &name, "parent", &parent,
01015                               "geometry", &geometry,
01016                               "background", &background,
01017                               "alpha", &alpha,
01018                               NULL))
01019         return;
01020 
01021       if (name == NULL)
01022         {
01023           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01024                      _("No \"%s\" attribute on <%s> element"),
01025                      "name", element_name);
01026           return;
01027         }
01028 
01029       if (meta_theme_lookup_style (info->theme, name))
01030         {
01031           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01032                      _("<%s> name \"%s\" used a second time"),
01033                      element_name, name);
01034           return;
01035         }
01036 
01037       parent_style = NULL;
01038       if (parent)
01039         {
01040           parent_style = meta_theme_lookup_style (info->theme, parent);
01041           if (parent_style == NULL)
01042             {
01043               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01044                          _("<%s> parent \"%s\" has not been defined"),
01045                          element_name, parent);
01046               return;
01047             }
01048         }
01049 
01050       layout = NULL;
01051       if (geometry)
01052         {
01053           layout = meta_theme_lookup_layout (info->theme, geometry);
01054           if (layout == NULL)
01055             {
01056               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01057                          _("<%s> geometry \"%s\" has not been defined"),
01058                          element_name, geometry);
01059               return;
01060             }
01061         }
01062       else if (parent_style)
01063         {
01064           layout = parent_style->layout;
01065         }
01066 
01067       if (layout == NULL)
01068         {
01069           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01070                      _("<%s> must specify either a geometry or a parent that has a geometry"),
01071                      element_name);
01072           return;
01073         }
01074 
01075       g_assert (info->style == NULL);
01076 
01077       info->style = meta_frame_style_new (parent_style);
01078       g_assert (info->style->layout == NULL);
01079       meta_frame_layout_ref (layout);
01080       info->style->layout = layout;
01081 
01082       if (background != NULL && META_THEME_ALLOWS (info->theme, META_THEME_FRAME_BACKGROUNDS))
01083         {
01084           info->style->window_background_color = meta_color_spec_new_from_string (background, error);
01085           if (!info->style->window_background_color)
01086             return;
01087 
01088           if (alpha != NULL)
01089             {
01090             
01091                gboolean success;
01092                MetaAlphaGradientSpec *alpha_vector;
01093                
01094                g_clear_error (error);
01095                /* fortunately, we already have a routine to parse alpha values,
01096                 * though it produces a vector of them, which is a superset of
01097                 * what we want.
01098                 */
01099                success = parse_alpha (alpha, &alpha_vector, context, error); 
01100                if (!success)
01101                  return;
01102 
01103                /* alpha_vector->alphas must contain at least one element */
01104                info->style->window_background_alpha = alpha_vector->alphas[0];
01105 
01106                meta_alpha_gradient_spec_free (alpha_vector);
01107             }
01108         }
01109       else if (alpha != NULL)
01110         {
01111           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01112                      _("You must specify a background for an alpha value to be meaningful"));
01113           return;
01114         }
01115 
01116       meta_theme_insert_style (info->theme, name, info->style);
01117 
01118       push_state (info, STATE_FRAME_STYLE);
01119     }
01120   else if (ELEMENT_IS ("frame_style_set"))
01121     {
01122       const char *name = NULL;
01123       const char *parent = NULL;
01124       MetaFrameStyleSet *parent_set;
01125 
01126       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
01127                               error,
01128                               "name", &name, "parent", &parent,
01129                               NULL))
01130         return;
01131 
01132       if (name == NULL)
01133         {
01134           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01135                      _("No \"%s\" attribute on <%s> element"),
01136                      "name", element_name);
01137           return;
01138         }
01139 
01140       if (meta_theme_lookup_style_set (info->theme, name))
01141         {
01142           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01143                      _("<%s> name \"%s\" used a second time"),
01144                      element_name, name);
01145           return;
01146         }
01147 
01148       parent_set = NULL;
01149       if (parent)
01150         {
01151           parent_set = meta_theme_lookup_style_set (info->theme, parent);
01152           if (parent_set == NULL)
01153             {
01154               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01155                          _("<%s> parent \"%s\" has not been defined"),
01156                          element_name, parent);
01157               return;
01158             }
01159         }
01160 
01161       g_assert (info->style_set == NULL);
01162 
01163       info->style_set = meta_frame_style_set_new (parent_set);
01164 
01165       meta_theme_insert_style_set (info->theme, name, info->style_set);
01166 
01167       push_state (info, STATE_FRAME_STYLE_SET);
01168     }
01169   else if (ELEMENT_IS ("window"))
01170     {
01171       const char *type_name = NULL;
01172       const char *style_set_name = NULL;
01173       MetaFrameStyleSet *style_set;
01174       MetaFrameType type;
01175 
01176       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
01177                               error,
01178                               "type", &type_name, "style_set", &style_set_name,
01179                               NULL))
01180         return;
01181 
01182       if (type_name == NULL)
01183         {
01184           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01185                      _("No \"%s\" attribute on <%s> element"),
01186                      "type", element_name);
01187           return;
01188         }
01189 
01190       if (style_set_name == NULL)
01191         {
01192           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01193                      _("No \"%s\" attribute on <%s> element"),
01194                      "style_set", element_name);
01195           return;
01196         }
01197 
01198       type = meta_frame_type_from_string (type_name);
01199 
01200       if (type == META_FRAME_TYPE_LAST)
01201         {
01202           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01203                      _("Unknown type \"%s\" on <%s> element"),
01204                      type_name, element_name);
01205           return;
01206         }
01207 
01208       style_set = meta_theme_lookup_style_set (info->theme,
01209                                                style_set_name);
01210 
01211       if (style_set == NULL)
01212         {
01213           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01214                      _("Unknown style_set \"%s\" on <%s> element"),
01215                      style_set_name, element_name);
01216           return;
01217         }
01218 
01219       if (info->theme->style_sets_by_type[type] != NULL)
01220         {
01221           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01222                      _("Window type \"%s\" has already been assigned a style set"),
01223                      type_name);
01224           return;
01225         }
01226 
01227       meta_frame_style_set_ref (style_set);
01228       info->theme->style_sets_by_type[type] = style_set;
01229 
01230       push_state (info, STATE_WINDOW);
01231     }
01232   else if (ELEMENT_IS ("menu_icon"))
01233     {
01234       /* Not supported any more, but we have to parse it if they include it,
01235        * for backwards compatibility.
01236        */
01237       g_assert (info->op_list == NULL);
01238       
01239       push_state (info, STATE_MENU_ICON);
01240     }
01241   else if (ELEMENT_IS ("fallback"))
01242     {
01243       const char *icon = NULL;
01244       const char *mini_icon = NULL;
01245       
01246       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
01247                               error,
01248                               "icon", &icon,
01249                               "mini_icon", &mini_icon,
01250                               NULL))
01251         return;
01252 
01253       if (icon)
01254         {
01255           if (info->theme->fallback_icon != NULL)
01256             {
01257               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01258                          _("Theme already has a fallback icon"));
01259               return;
01260             }
01261 
01262           info->theme->fallback_icon = meta_theme_load_image(info->theme, icon, 64, error);
01263         }
01264 
01265       if (mini_icon)
01266         {
01267           if (info->theme->fallback_mini_icon != NULL)
01268             {
01269               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01270                          _("Theme already has a fallback mini_icon"));
01271               return;
01272             }
01273 
01274           info->theme->fallback_mini_icon = meta_theme_load_image(info->theme, mini_icon, 16, error);
01275         }
01276 
01277       push_state (info, STATE_FALLBACK);
01278     }
01279    else
01280     {
01281       set_error (error, context,
01282                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01283                  _("Element <%s> is not allowed below <%s>"),
01284                  element_name, "metacity_theme");
01285     }
01286 }
01287 
01288 static void
01289 parse_info_element (GMarkupParseContext  *context,
01290                     const gchar          *element_name,
01291                     const gchar         **attribute_names,
01292                     const gchar         **attribute_values,
01293                     ParseInfo            *info,
01294                     GError              **error)
01295 {
01296   g_return_if_fail (peek_state (info) == STATE_INFO);
01297 
01298   if (ELEMENT_IS ("name"))
01299     {
01300       if (!check_no_attributes (context, element_name,
01301                                 attribute_names, attribute_values,
01302                                 error))
01303         return;
01304 
01305       push_state (info, STATE_NAME);
01306     }
01307   else if (ELEMENT_IS ("author"))
01308     {
01309       if (!check_no_attributes (context, element_name,
01310                                 attribute_names, attribute_values,
01311                                 error))
01312         return;
01313 
01314       push_state (info, STATE_AUTHOR);
01315     }
01316   else if (ELEMENT_IS ("copyright"))
01317     {
01318       if (!check_no_attributes (context, element_name,
01319                                 attribute_names, attribute_values,
01320                                 error))
01321         return;
01322 
01323       push_state (info, STATE_COPYRIGHT);
01324     }
01325   else if (ELEMENT_IS ("description"))
01326     {
01327       if (!check_no_attributes (context, element_name,
01328                                 attribute_names, attribute_values,
01329                                 error))
01330         return;
01331 
01332       push_state (info, STATE_DESCRIPTION);
01333     }
01334   else if (ELEMENT_IS ("date"))
01335     {
01336       if (!check_no_attributes (context, element_name,
01337                                 attribute_names, attribute_values,
01338                                 error))
01339         return;
01340 
01341       push_state (info, STATE_DATE);
01342     }
01343   else
01344     {
01345       set_error (error, context,
01346                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01347                  _("Element <%s> is not allowed below <%s>"),
01348                  element_name, "info");
01349     }
01350 }
01351 
01352 static void
01353 parse_distance (GMarkupParseContext  *context,
01354                 const gchar          *element_name,
01355                 const gchar         **attribute_names,
01356                 const gchar         **attribute_values,
01357                 ParseInfo            *info,
01358                 GError              **error)
01359 {
01360   const char *name;
01361   const char *value;
01362   int val;
01363   
01364   if (!locate_attributes (context, element_name, attribute_names, attribute_values,
01365                           error,
01366                           "name", &name, "value", &value,
01367                           NULL))
01368     return;
01369 
01370   if (name == NULL)
01371     {
01372       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01373                  _("No \"name\" attribute on element <%s>"), element_name);
01374       return;
01375     }
01376 
01377   if (value == NULL)
01378     {
01379       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01380                  _("No \"value\" attribute on element <%s>"), element_name);
01381       return;
01382     }
01383 
01384   val = 0;
01385   if (!parse_positive_integer (value, &val, context, info->theme, error))
01386     return;
01387 
01388   g_assert (val >= 0); /* yeah, "non-negative" not "positive" get over it */
01389   g_assert (info->layout);
01390 
01391   if (strcmp (name, "left_width") == 0)
01392     info->layout->left_width = val;
01393   else if (strcmp (name, "right_width") == 0)
01394     info->layout->right_width = val;
01395   else if (strcmp (name, "bottom_height") == 0)
01396     info->layout->bottom_height = val;
01397   else if (strcmp (name, "title_vertical_pad") == 0)
01398     info->layout->title_vertical_pad = val;
01399   else if (strcmp (name, "right_titlebar_edge") == 0)
01400     info->layout->right_titlebar_edge = val;
01401   else if (strcmp (name, "left_titlebar_edge") == 0)
01402     info->layout->left_titlebar_edge = val;
01403   else if (strcmp (name, "button_width") == 0)
01404     {
01405       info->layout->button_width = val;
01406             
01407       if (!(info->layout->button_sizing == META_BUTTON_SIZING_LAST ||
01408             info->layout->button_sizing == META_BUTTON_SIZING_FIXED))
01409         {
01410           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01411                      _("Cannot specify both button_width/button_height and aspect ratio for buttons"));
01412           return;      
01413         }
01414 
01415       info->layout->button_sizing = META_BUTTON_SIZING_FIXED;
01416     }
01417   else if (strcmp (name, "button_height") == 0)
01418     {
01419       info->layout->button_height = val;
01420       
01421       if (!(info->layout->button_sizing == META_BUTTON_SIZING_LAST ||
01422             info->layout->button_sizing == META_BUTTON_SIZING_FIXED))
01423         {
01424           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01425                      _("Cannot specify both button_width/button_height and aspect ratio for buttons"));
01426           return;      
01427         }
01428 
01429       info->layout->button_sizing = META_BUTTON_SIZING_FIXED;
01430     }
01431   else
01432     {
01433       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01434                  _("Distance \"%s\" is unknown"), name);
01435       return;
01436     }
01437 }
01438 
01439 static void
01440 parse_aspect_ratio (GMarkupParseContext  *context,
01441                     const gchar          *element_name,
01442                     const gchar         **attribute_names,
01443                     const gchar         **attribute_values,
01444                     ParseInfo            *info,
01445                     GError              **error)
01446 {
01447   const char *name;
01448   const char *value;
01449   double val;
01450   
01451   if (!locate_attributes (context, element_name, attribute_names, attribute_values,
01452                           error,
01453                           "name", &name, "value", &value,
01454                           NULL))
01455     return;
01456 
01457   if (name == NULL)
01458     {
01459       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01460                  _("No \"name\" attribute on element <%s>"), element_name);
01461       return;
01462     }
01463 
01464   if (value == NULL)
01465     {
01466       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01467                  _("No \"value\" attribute on element <%s>"), element_name);
01468       return;
01469     }
01470 
01471   val = 0;
01472   if (!parse_double (value, &val, context, error))
01473     return;
01474 
01475   g_assert (info->layout);
01476   
01477   if (strcmp (name, "button") == 0)
01478     {
01479       info->layout->button_aspect = val;
01480 
01481       if (info->layout->button_sizing != META_BUTTON_SIZING_LAST)
01482         {
01483           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01484                      _("Cannot specify both button_width/button_height and aspect ratio for buttons"));
01485           return;
01486         }
01487       
01488       info->layout->button_sizing = META_BUTTON_SIZING_ASPECT;
01489     }
01490   else
01491     {
01492       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01493                  _("Aspect ratio \"%s\" is unknown"), name);
01494       return;
01495     }
01496 }
01497 
01498 static void
01499 parse_border (GMarkupParseContext  *context,
01500               const gchar          *element_name,
01501               const gchar         **attribute_names,
01502               const gchar         **attribute_values,
01503               ParseInfo            *info,
01504               GError              **error)
01505 {
01506   const char *name;
01507   const char *top;
01508   const char *bottom;
01509   const char *left;
01510   const char *right;
01511   int top_val;
01512   int bottom_val;
01513   int left_val;
01514   int right_val;
01515   GtkBorder *border;
01516   
01517   if (!locate_attributes (context, element_name, attribute_names, attribute_values,
01518                           error,
01519                           "name", &name,
01520                           "top", &top,
01521                           "bottom", &bottom,
01522                           "left", &left,
01523                           "right", &right,
01524                           NULL))
01525     return;
01526   
01527   if (name == NULL)
01528     {
01529       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01530                  _("No \"name\" attribute on element <%s>"), element_name);
01531       return;
01532     }
01533 
01534   if (top == NULL)
01535     {
01536       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01537                  _("No \"top\" attribute on element <%s>"), element_name);
01538       return;
01539     }
01540 
01541   if (bottom == NULL)
01542     {
01543       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01544                  _("No \"bottom\" attribute on element <%s>"), element_name);
01545       return;
01546     }
01547 
01548   if (left == NULL)
01549     {
01550       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01551                  _("No \"left\" attribute on element <%s>"), element_name);
01552       return;
01553     }
01554 
01555   if (right == NULL)
01556     {
01557       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01558                  _("No \"right\" attribute on element <%s>"), element_name);
01559       return;
01560     }
01561 
01562   top_val = 0;
01563   if (!parse_positive_integer (top, &top_val, context, info->theme, error))
01564     return;
01565 
01566   bottom_val = 0;
01567   if (!parse_positive_integer (bottom, &bottom_val, context, info->theme, error))
01568     return;
01569 
01570   left_val = 0;
01571   if (!parse_positive_integer (left, &left_val, context, info->theme, error))
01572     return;
01573 
01574   right_val = 0;
01575   if (!parse_positive_integer (right, &right_val, context, info->theme, error))
01576     return;
01577   
01578   g_assert (info->layout);
01579 
01580   border = NULL;
01581   
01582   if (strcmp (name, "title_border") == 0)
01583     border = &info->layout->title_border;
01584   else if (strcmp (name, "button_border") == 0)
01585     border = &info->layout->button_border;
01586 
01587   if (border == NULL)
01588     {
01589       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01590                  _("Border \"%s\" is unknown"), name);
01591       return;
01592     }
01593 
01594   border->top = top_val;
01595   border->bottom = bottom_val;
01596   border->left = left_val;
01597   border->right = right_val;
01598 }
01599 
01600 static void
01601 parse_geometry_element (GMarkupParseContext  *context,
01602                         const gchar          *element_name,
01603                         const gchar         **attribute_names,
01604                         const gchar         **attribute_values,
01605                         ParseInfo            *info,
01606                         GError              **error)
01607 {
01608   g_return_if_fail (peek_state (info) == STATE_FRAME_GEOMETRY);
01609 
01610   if (ELEMENT_IS ("distance"))
01611     {
01612       parse_distance (context, element_name,
01613                       attribute_names, attribute_values,
01614                       info, error);
01615       push_state (info, STATE_DISTANCE);
01616     }
01617   else if (ELEMENT_IS ("border"))
01618     {
01619       parse_border (context, element_name,
01620                     attribute_names, attribute_values,
01621                     info, error);
01622       push_state (info, STATE_BORDER);
01623     }
01624   else if (ELEMENT_IS ("aspect_ratio"))
01625     {
01626       parse_aspect_ratio (context, element_name,
01627                           attribute_names, attribute_values,
01628                           info, error);
01629 
01630       push_state (info, STATE_ASPECT_RATIO);
01631     }
01632   else
01633     {
01634       set_error (error, context,
01635                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01636                  _("Element <%s> is not allowed below <%s>"),
01637                  element_name, "frame_geometry");
01638     }
01639 }
01640 
01641 #if 0
01642 static gboolean
01643 check_expression (PosToken            *tokens,
01644                   int                  n_tokens,
01645                   gboolean             has_object,
01646                   MetaTheme           *theme,
01647                   GMarkupParseContext *context,
01648                   GError             **error)
01649 {
01650   MetaPositionExprEnv env;
01651   int x, y;
01652 
01653   /* We set it all to 0 to try and catch divide-by-zero screwups.
01654    * it's possible we should instead guarantee that widths and heights
01655    * are at least 1.
01656    */
01657   
01658   env.rect = meta_rect (0, 0, 0, 0);
01659   if (has_object)
01660     {
01661       env.object_width = 0;
01662       env.object_height = 0;
01663     }
01664   else
01665     {
01666       env.object_width = -1;
01667       env.object_height = -1;
01668     }
01669 
01670   env.left_width = 0;
01671   env.right_width = 0;
01672   env.top_height = 0;
01673   env.bottom_height = 0;
01674   env.title_width = 0;
01675   env.title_height = 0;
01676   
01677   env.icon_width = 0;
01678   env.icon_height = 0;
01679   env.mini_icon_width = 0;
01680   env.mini_icon_height = 0;
01681   env.theme = theme;
01682   
01683   if (!meta_parse_position_expression (tokens, n_tokens,
01684                                        &env,
01685                                        &x, &y,
01686                                        error))
01687     {
01688       add_context_to_error (error, context);
01689       return FALSE;
01690     }
01691 
01692   return TRUE;
01693 }
01694 #endif
01695 
01696 static void
01697 parse_draw_op_element (GMarkupParseContext  *context,
01698                        const gchar          *element_name,
01699                        const gchar         **attribute_names,
01700                        const gchar         **attribute_values,
01701                        ParseInfo            *info,
01702                        GError              **error)
01703 {  
01704   g_return_if_fail (peek_state (info) == STATE_DRAW_OPS);
01705 
01706   if (ELEMENT_IS ("line"))
01707     {
01708       MetaDrawOp *op;
01709       const char *color;
01710       const char *x1;
01711       const char *y1;
01712       const char *x2;
01713       const char *y2;
01714       const char *dash_on_length;
01715       const char *dash_off_length;
01716       const char *width;
01717       MetaColorSpec *color_spec;
01718       int dash_on_val;
01719       int dash_off_val;
01720       int width_val;
01721       
01722       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
01723                               error,
01724                               "color", &color,
01725                               "x1", &x1, "y1", &y1,
01726                               "x2", &x2, "y2", &y2,
01727                               "dash_on_length", &dash_on_length,
01728                               "dash_off_length", &dash_off_length,
01729                               "width", &width,
01730                               NULL))
01731         return;
01732 
01733       if (color == NULL)
01734         {
01735           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01736                      _("No \"color\" attribute on element <%s>"), element_name);
01737           return;
01738         }
01739       
01740       if (x1 == NULL)
01741         {
01742           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01743                      _("No \"x1\" attribute on element <%s>"), element_name);
01744           return;
01745         }
01746 
01747       if (y1 == NULL)
01748         {
01749           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01750                      _("No \"y1\" attribute on element <%s>"), element_name);
01751           return;
01752         }
01753 
01754       if (x2 == NULL)
01755         {
01756           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01757                      _("No \"x2\" attribute on element <%s>"), element_name);
01758           return;
01759         }
01760 
01761       if (y2 == NULL)
01762         {
01763           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01764                      _("No \"y2\" attribute on element <%s>"), element_name);
01765           return;
01766         }
01767 
01768 #if 0
01769       if (!check_expression (x1, FALSE, info->theme, context, error))
01770         return;
01771 
01772       if (!check_expression (y1, FALSE, info->theme, context, error))
01773         return;
01774 
01775       if (!check_expression (x2, FALSE, info->theme, context, error))
01776         return;
01777       
01778       if (!check_expression (y2, FALSE, info->theme, context, error))
01779         return;
01780 #endif
01781  
01782       dash_on_val = 0;
01783       if (dash_on_length &&
01784           !parse_positive_integer (dash_on_length, &dash_on_val, context, info->theme, error))
01785         return;
01786 
01787       dash_off_val = 0;
01788       if (dash_off_length &&
01789           !parse_positive_integer (dash_off_length, &dash_off_val, context, info->theme, error))
01790         return;
01791 
01792       width_val = 0;
01793       if (width &&
01794           !parse_positive_integer (width, &width_val, context, info->theme, error))
01795         return;
01796 
01797       /* Check last so we don't have to free it when other
01798        * stuff fails
01799        */
01800       color_spec = parse_color (info->theme, color, error);
01801       if (color_spec == NULL)
01802         {
01803           add_context_to_error (error, context);
01804           return;
01805         }
01806       
01807       op = meta_draw_op_new (META_DRAW_LINE);
01808 
01809       op->data.line.color_spec = color_spec;
01810 
01811       op->data.line.x1 = meta_draw_spec_new (info->theme, x1, NULL);
01812       op->data.line.y1 = meta_draw_spec_new (info->theme, y1, NULL);
01813       op->data.line.x2 = meta_draw_spec_new (info->theme, x2, NULL);
01814       op->data.line.y2 = meta_draw_spec_new (info->theme, y2, NULL);
01815 
01816       op->data.line.width = width_val;
01817       op->data.line.dash_on_length = dash_on_val;
01818       op->data.line.dash_off_length = dash_off_val;
01819 
01820       g_assert (info->op_list);
01821       
01822       meta_draw_op_list_append (info->op_list, op);
01823 
01824       push_state (info, STATE_LINE);
01825     }
01826   else if (ELEMENT_IS ("rectangle"))
01827     {
01828       MetaDrawOp *op;
01829       const char *color;
01830       const char *x;
01831       const char *y;
01832       const char *width;
01833       const char *height;
01834       const char *filled;
01835       gboolean filled_val;
01836       MetaColorSpec *color_spec;
01837       
01838       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
01839                               error,
01840                               "color", &color,
01841                               "x", &x, "y", &y,
01842                               "width", &width, "height", &height,
01843                               "filled", &filled,
01844                               NULL))
01845         return;
01846 
01847       if (color == NULL)
01848         {
01849           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01850                      _("No \"color\" attribute on element <%s>"), element_name);
01851           return;
01852         }
01853       
01854       if (x == NULL)
01855         {
01856           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01857                      _("No \"x\" attribute on element <%s>"), element_name);
01858           return;
01859         }
01860 
01861       if (y == NULL)
01862         {
01863           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01864                      _("No \"y\" attribute on element <%s>"), element_name);
01865           return;
01866         }
01867 
01868       if (width == NULL)
01869         {
01870           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01871                      _("No \"width\" attribute on element <%s>"), element_name);
01872           return;
01873         }
01874 
01875       if (height == NULL)
01876         {
01877           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01878                      _("No \"height\" attribute on element <%s>"), element_name);
01879           return;
01880         }
01881 
01882 #if 0
01883       if (!check_expression (x, FALSE, info->theme, context, error))
01884         return;
01885 
01886       if (!check_expression (y, FALSE, info->theme, context, error))
01887         return;
01888 
01889       if (!check_expression (width, FALSE, info->theme, context, error))
01890         return;
01891       
01892       if (!check_expression (height, FALSE, info->theme, context, error))
01893         return;
01894 #endif
01895 
01896       filled_val = FALSE;
01897       if (filled && !parse_boolean (filled, &filled_val, context, error))
01898         return;
01899       
01900       /* Check last so we don't have to free it when other
01901        * stuff fails
01902        */
01903       color_spec = parse_color (info->theme, color, error);
01904       if (color_spec == NULL)
01905         {
01906           add_context_to_error (error, context);
01907           return;
01908         }
01909       
01910       op = meta_draw_op_new (META_DRAW_RECTANGLE);
01911 
01912       op->data.rectangle.color_spec = color_spec;
01913       op->data.rectangle.x = meta_draw_spec_new (info->theme, x, NULL);
01914       op->data.rectangle.y = meta_draw_spec_new (info->theme, y, NULL);
01915       op->data.rectangle.width = meta_draw_spec_new (info->theme, width, NULL);
01916       op->data.rectangle.height = meta_draw_spec_new (info->theme, 
01917                                                       height, NULL);
01918 
01919       op->data.rectangle.filled = filled_val;
01920 
01921       g_assert (info->op_list);
01922       
01923       meta_draw_op_list_append (info->op_list, op);
01924 
01925       push_state (info, STATE_RECTANGLE);
01926     }
01927   else if (ELEMENT_IS ("arc"))
01928     {
01929       MetaDrawOp *op;
01930       const char *color;
01931       const char *x;
01932       const char *y;
01933       const char *width;
01934       const char *height;
01935       const char *filled;
01936       const char *start_angle;
01937       const char *extent_angle;
01938       const char *from;
01939       const char *to;
01940       gboolean filled_val;
01941       double start_angle_val;
01942       double extent_angle_val;
01943       MetaColorSpec *color_spec;
01944       
01945       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
01946                               error,
01947                               "color", &color,
01948                               "x", &x, "y", &y,
01949                               "width", &width, "height", &height,
01950                               "filled", &filled,
01951                               "start_angle", &start_angle,
01952                               "extent_angle", &extent_angle,
01953                               "from", &from,
01954                               "to", &to,
01955                               NULL))
01956         return;
01957 
01958       if (color == NULL)
01959         {
01960           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01961                      _("No \"color\" attribute on element <%s>"), element_name);
01962           return;
01963         }
01964       
01965       if (x == NULL)
01966         {
01967           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01968                      _("No \"x\" attribute on element <%s>"), element_name);
01969           return;
01970         }
01971 
01972       if (y == NULL)
01973         {
01974           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01975                      _("No \"y\" attribute on element <%s>"), element_name);
01976           return;
01977         }
01978 
01979       if (width == NULL)
01980         {
01981           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01982                      _("No \"width\" attribute on element <%s>"), element_name);
01983           return;
01984         }
01985 
01986       if (height == NULL)
01987         {
01988           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01989                      _("No \"height\" attribute on element <%s>"), element_name);
01990           return;
01991         }
01992 
01993       if (META_THEME_ALLOWS (info->theme, META_THEME_DEGREES_IN_ARCS) )
01994         {
01995           if (start_angle == NULL && from == NULL)
01996             {
01997               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
01998                          _("No \"start_angle\" or \"from\" attribute on element <%s>"), element_name);
01999               return;
02000             }
02001 
02002           if (extent_angle == NULL && to == NULL)
02003             {
02004               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02005                          _("No \"extent_angle\" or \"to\" attribute on element <%s>"), element_name);
02006               return;
02007             }
02008         }
02009       else
02010         {
02011           if (start_angle == NULL)
02012             {
02013               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02014                          _("No \"start_angle\" attribute on element <%s>"), element_name);
02015               return;
02016             }
02017 
02018           if (extent_angle == NULL)
02019             {
02020               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02021                          _("No \"extent_angle\" attribute on element <%s>"), element_name);
02022               return;
02023             }
02024         }
02025 
02026 #if 0     
02027       if (!check_expression (x, FALSE, info->theme, context, error))
02028         return;
02029 
02030       if (!check_expression (y, FALSE, info->theme, context, error))
02031         return;
02032 
02033       if (!check_expression (width, FALSE, info->theme, context, error))
02034         return;
02035       
02036       if (!check_expression (height, FALSE, info->theme, context, error))
02037         return;
02038 #endif
02039 
02040       if (start_angle == NULL)
02041         {
02042           if (!parse_angle (from, &start_angle_val, context, error))
02043             return;
02044           
02045           start_angle_val = (180-start_angle_val)/360.0;
02046         }
02047       else
02048         {
02049           if (!parse_angle (start_angle, &start_angle_val, context, error))
02050             return;
02051         }
02052       
02053       if (extent_angle == NULL)
02054         {
02055           if (!parse_angle (to, &extent_angle_val, context, error))
02056             return;
02057           
02058           extent_angle_val = ((180-extent_angle_val)/360.0) - start_angle_val;
02059         }
02060       else
02061         {
02062            if (!parse_angle (extent_angle, &extent_angle_val, context, error))
02063              return;
02064         }
02065      
02066       filled_val = FALSE;
02067       if (filled && !parse_boolean (filled, &filled_val, context, error))
02068         return;
02069       
02070       /* Check last so we don't have to free it when other
02071        * stuff fails
02072        */
02073       color_spec = parse_color (info->theme, color, error);
02074       if (color_spec == NULL)
02075         {
02076           add_context_to_error (error, context);
02077           return;
02078         }
02079       
02080       op = meta_draw_op_new (META_DRAW_ARC);
02081 
02082       op->data.arc.color_spec = color_spec;
02083 
02084       op->data.arc.x = meta_draw_spec_new (info->theme, x, NULL);
02085       op->data.arc.y = meta_draw_spec_new (info->theme, y, NULL);
02086       op->data.arc.width = meta_draw_spec_new (info->theme, width, NULL);
02087       op->data.arc.height = meta_draw_spec_new (info->theme, height, NULL);
02088 
02089       op->data.arc.filled = filled_val;
02090       op->data.arc.start_angle = start_angle_val;
02091       op->data.arc.extent_angle = extent_angle_val;
02092       
02093       g_assert (info->op_list);
02094       
02095       meta_draw_op_list_append (info->op_list, op);
02096 
02097       push_state (info, STATE_ARC);
02098     }
02099   else if (ELEMENT_IS ("clip"))
02100     {
02101       MetaDrawOp *op;
02102       const char *x;
02103       const char *y;
02104       const char *width;
02105       const char *height;
02106       
02107       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
02108                               error,
02109                               "x", &x, "y", &y,
02110                               "width", &width, "height", &height,
02111                               NULL))
02112         return;
02113       
02114       if (x == NULL)
02115         {
02116           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02117                      _("No \"x\" attribute on element <%s>"), element_name);
02118           return;
02119         }
02120 
02121       if (y == NULL)
02122         {
02123           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02124                      _("No \"y\" attribute on element <%s>"), element_name);
02125           return;
02126         }
02127 
02128       if (width == NULL)
02129         {
02130           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02131                      _("No \"width\" attribute on element <%s>"), element_name);
02132           return;
02133         }
02134 
02135       if (height == NULL)
02136         {
02137           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02138                      _("No \"height\" attribute on element <%s>"), element_name);
02139           return;
02140         }
02141 
02142 #if 0
02143       if (!check_expression (x, FALSE, info->theme, context, error))
02144         return;
02145 
02146       if (!check_expression (y, FALSE, info->theme, context, error))
02147         return;
02148 
02149       if (!check_expression (width, FALSE, info->theme, context, error))
02150         return;
02151       
02152       if (!check_expression (height, FALSE, info->theme, context, error))
02153         return;
02154 #endif 
02155       op = meta_draw_op_new (META_DRAW_CLIP);
02156 
02157       op->data.clip.x = meta_draw_spec_new (info->theme, x, NULL);
02158       op->data.clip.y = meta_draw_spec_new (info->theme, y, NULL);
02159       op->data.clip.width = meta_draw_spec_new (info->theme, width, NULL);
02160       op->data.clip.height = meta_draw_spec_new (info->theme, height, NULL);
02161 
02162       g_assert (info->op_list);
02163       
02164       meta_draw_op_list_append (info->op_list, op);
02165 
02166       push_state (info, STATE_CLIP);
02167     }
02168   else if (ELEMENT_IS ("tint"))
02169     {
02170       MetaDrawOp *op;
02171       const char *color;
02172       const char *x;
02173       const char *y;
02174       const char *width;
02175       const char *height;
02176       const char *alpha;
02177       MetaAlphaGradientSpec *alpha_spec;
02178       MetaColorSpec *color_spec;
02179       
02180       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
02181                               error,
02182                               "color", &color,
02183                               "x", &x, "y", &y,
02184                               "width", &width, "height", &height,
02185                               "alpha", &alpha,
02186                               NULL))
02187         return;
02188 
02189       if (color == NULL)
02190         {
02191           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02192                      _("No \"color\" attribute on element <%s>"), element_name);
02193           return;
02194         }
02195       
02196       if (x == NULL)
02197         {
02198           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02199                      _("No \"x\" attribute on element <%s>"), element_name);
02200           return;
02201         }
02202 
02203       if (y == NULL)
02204         {
02205           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02206                      _("No \"y\" attribute on element <%s>"), element_name);
02207           return;
02208         }
02209 
02210       if (width == NULL)
02211         {
02212           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02213                      _("No \"width\" attribute on element <%s>"), element_name);
02214           return;
02215         }
02216 
02217       if (height == NULL)
02218         {
02219           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02220                      _("No \"height\" attribute on element <%s>"), element_name);
02221           return;
02222         }
02223 
02224       if (alpha == NULL)
02225         {
02226           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02227                      _("No \"alpha\" attribute on element <%s>"), element_name);
02228           return;
02229         }
02230 #if 0
02231       if (!check_expression (x, FALSE, info->theme, context, error))
02232         return;
02233 
02234       if (!check_expression (y, FALSE, info->theme, context, error))
02235         return;
02236 
02237       if (!check_expression (width, FALSE, info->theme, context, error))
02238         return;
02239       
02240       if (!check_expression (height, FALSE, info->theme, context, error))
02241         return;
02242 #endif
02243       alpha_spec = NULL;
02244       if (!parse_alpha (alpha, &alpha_spec, context, error))
02245         return;
02246       
02247       /* Check last so we don't have to free it when other
02248        * stuff fails
02249        */
02250       color_spec = parse_color (info->theme, color, error);
02251       if (color_spec == NULL)
02252         {
02253           if (alpha_spec)
02254             meta_alpha_gradient_spec_free (alpha_spec);
02255           
02256           add_context_to_error (error, context);
02257           return;
02258         }
02259       
02260       op = meta_draw_op_new (META_DRAW_TINT);
02261 
02262       op->data.tint.color_spec = color_spec;
02263       op->data.tint.alpha_spec = alpha_spec;
02264 
02265       op->data.tint.x = meta_draw_spec_new (info->theme, x, NULL);
02266       op->data.tint.y = meta_draw_spec_new (info->theme, y, NULL);
02267       op->data.tint.width = meta_draw_spec_new (info->theme, width, NULL);
02268       op->data.tint.height = meta_draw_spec_new (info->theme, height, NULL);
02269 
02270       g_assert (info->op_list);
02271       
02272       meta_draw_op_list_append (info->op_list, op);
02273 
02274       push_state (info, STATE_TINT);
02275     }
02276   else if (ELEMENT_IS ("gradient"))
02277     {
02278       const char *x;
02279       const char *y;
02280       const char *width;
02281       const char *height;
02282       const char *type;
02283       const char *alpha;
02284       MetaAlphaGradientSpec *alpha_spec;
02285       MetaGradientType type_val;
02286       
02287       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
02288                               error,
02289                               "type", &type,
02290                               "x", &x, "y", &y,
02291                               "width", &width, "height", &height,
02292                               "alpha", &alpha,
02293                               NULL))
02294         return;
02295 
02296       if (type == NULL)
02297         {
02298           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02299                      _("No \"type\" attribute on element <%s>"), element_name);
02300           return;
02301         }
02302       
02303       if (x == NULL)
02304         {
02305           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02306                      _("No \"x\" attribute on element <%s>"), element_name);
02307           return;
02308         }
02309 
02310       if (y == NULL)
02311         {
02312           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02313                      _("No \"y\" attribute on element <%s>"), element_name);
02314           return;
02315         }
02316 
02317       if (width == NULL)
02318         {
02319           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02320                      _("No \"width\" attribute on element <%s>"), element_name);
02321           return;
02322         }
02323 
02324       if (height == NULL)
02325         {
02326           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02327                      _("No \"height\" attribute on element <%s>"), element_name);
02328           return;
02329         }
02330 
02331 #if 0
02332       if (!check_expression (x, FALSE, info->theme, context, error))
02333         return;
02334 
02335       if (!check_expression (y, FALSE, info->theme, context, error))
02336         return;
02337 
02338       if (!check_expression (width, FALSE, info->theme, context, error))
02339         return;
02340       
02341       if (!check_expression (height, FALSE, info->theme, context, error))
02342         return;
02343 #endif
02344   
02345       type_val = meta_gradient_type_from_string (type);
02346       if (type_val == META_GRADIENT_LAST)
02347         {
02348           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02349                      _("Did not understand value \"%s\" for type of gradient"),
02350                      type);
02351           return;
02352         }
02353 
02354       alpha_spec = NULL;
02355       if (alpha && !parse_alpha (alpha, &alpha_spec, context, error))
02356         return;
02357       
02358       g_assert (info->op == NULL);
02359       info->op = meta_draw_op_new (META_DRAW_GRADIENT);
02360 
02361       info->op->data.gradient.x = meta_draw_spec_new (info->theme, x, NULL);
02362       info->op->data.gradient.y = meta_draw_spec_new (info->theme, y, NULL);
02363       info->op->data.gradient.width = meta_draw_spec_new (info->theme, 
02364                                                         width, NULL);
02365       info->op->data.gradient.height = meta_draw_spec_new (info->theme,
02366                                                          height, NULL);
02367 
02368       info->op->data.gradient.gradient_spec = meta_gradient_spec_new (type_val);
02369 
02370       info->op->data.gradient.alpha_spec = alpha_spec;
02371       
02372       push_state (info, STATE_GRADIENT);
02373 
02374       /* op gets appended on close tag */
02375     }
02376   else if (ELEMENT_IS ("image"))
02377     {
02378       MetaDrawOp *op;
02379       const char *filename;
02380       const char *x;
02381       const char *y;
02382       const char *width;
02383       const char *height;
02384       const char *alpha;
02385       const char *colorize;
02386       const char *fill_type;
02387       MetaAlphaGradientSpec *alpha_spec;
02388       GdkPixbuf *pixbuf;
02389       MetaColorSpec *colorize_spec = NULL;
02390       MetaImageFillType fill_type_val;
02391       int h, w, c;
02392       int pixbuf_width, pixbuf_height, pixbuf_n_channels, pixbuf_rowstride;
02393       guchar *pixbuf_pixels;
02394       
02395       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
02396                               error,
02397                               "x", &x, "y", &y,
02398                               "width", &width, "height", &height,
02399                               "alpha", &alpha, "filename", &filename,
02400                               "colorize", &colorize,
02401                               "fill_type", &fill_type,
02402                               NULL))
02403         return;
02404       
02405       if (x == NULL)
02406         {
02407           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02408                      _("No \"x\" attribute on element <%s>"), element_name);
02409           return;
02410         }
02411 
02412       if (y == NULL)
02413         {
02414           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02415                      _("No \"y\" attribute on element <%s>"), element_name);
02416           return;
02417         }
02418 
02419       if (width == NULL)
02420         {
02421           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02422                      _("No \"width\" attribute on element <%s>"), element_name);
02423           return;
02424         }
02425 
02426       if (height == NULL)
02427         {
02428           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02429                      _("No \"height\" attribute on element <%s>"), element_name);
02430           return;
02431         }
02432 
02433       if (filename == NULL)
02434         {
02435           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02436                      _("No \"filename\" attribute on element <%s>"), element_name);
02437           return;
02438         }
02439 #if 0      
02440       if (!check_expression (x, TRUE, info->theme, context, error))
02441         return;
02442 
02443       if (!check_expression (y, TRUE, info->theme, context, error))
02444         return;
02445 
02446       if (!check_expression (width, TRUE, info->theme, context, error))
02447         return;
02448       
02449       if (!check_expression (height, TRUE, info->theme, context, error))
02450         return;
02451 #endif
02452       fill_type_val = META_IMAGE_FILL_SCALE;
02453       if (fill_type)
02454         {
02455           fill_type_val = meta_image_fill_type_from_string (fill_type);
02456           
02457           if (((int) fill_type_val) == -1)
02458             {
02459               set_error (error, context, G_MARKUP_ERROR,
02460                          G_MARKUP_ERROR_PARSE,
02461                          _("Did not understand fill type \"%s\" for <%s> element"),
02462                          fill_type, element_name);
02463             }
02464         }
02465       
02466       /* Check last so we don't have to free it when other
02467        * stuff fails.
02468        *
02469        * If it's a theme image, ask for it at 64px, which is
02470        * the largest possible. We scale it anyway.
02471        */
02472       pixbuf = meta_theme_load_image (info->theme, filename, 64, error);
02473 
02474       if (pixbuf == NULL)
02475         {
02476           add_context_to_error (error, context);
02477           return;
02478         }
02479 
02480       if (colorize)
02481         {
02482           colorize_spec = parse_color (info->theme, colorize, error);
02483           
02484           if (colorize_spec == NULL)
02485             {
02486               add_context_to_error (error, context);
02487               g_object_unref (G_OBJECT (pixbuf));
02488               return;
02489             }
02490         }
02491 
02492       alpha_spec = NULL;
02493       if (alpha && !parse_alpha (alpha, &alpha_spec, context, error))
02494         {
02495           g_object_unref (G_OBJECT (pixbuf));
02496           return;
02497         }
02498       
02499       op = meta_draw_op_new (META_DRAW_IMAGE);
02500 
02501       op->data.image.pixbuf = pixbuf;
02502       op->data.image.colorize_spec = colorize_spec;
02503 
02504       op->data.image.x = meta_draw_spec_new (info->theme, x, NULL);
02505       op->data.image.y = meta_draw_spec_new (info->theme, y, NULL);
02506       op->data.image.width = meta_draw_spec_new (info->theme, width, NULL);
02507       op->data.image.height = meta_draw_spec_new (info->theme, height, NULL);
02508 
02509       op->data.image.alpha_spec = alpha_spec;
02510       op->data.image.fill_type = fill_type_val;
02511       
02512       /* Check for vertical & horizontal stripes */
02513       pixbuf_n_channels = gdk_pixbuf_get_n_channels(pixbuf);
02514       pixbuf_width = gdk_pixbuf_get_width(pixbuf);
02515       pixbuf_height = gdk_pixbuf_get_height(pixbuf);
02516       pixbuf_rowstride = gdk_pixbuf_get_rowstride(pixbuf);
02517       pixbuf_pixels = gdk_pixbuf_get_pixels(pixbuf);
02518 
02519       /* Check for horizontal stripes */
02520       for (h = 0; h < pixbuf_height; h++)
02521         {
02522           for (w = 1; w < pixbuf_width; w++)
02523             {
02524               for (c = 0; c < pixbuf_n_channels; c++)
02525                 {
02526                   if (pixbuf_pixels[(h * pixbuf_rowstride) + c] !=
02527                       pixbuf_pixels[(h * pixbuf_rowstride) + w + c])
02528                     break;
02529                 }
02530               if (c < pixbuf_n_channels)
02531                 break;
02532             }
02533           if (w < pixbuf_width)
02534             break;
02535         }
02536 
02537       if (h >= pixbuf_height)
02538         {
02539           op->data.image.horizontal_stripes = TRUE; 
02540         }
02541       else
02542         {
02543           op->data.image.horizontal_stripes = FALSE; 
02544         }
02545 
02546       /* Check for vertical stripes */
02547       for (w = 0; w < pixbuf_width; w++)
02548         {
02549           for (h = 1; h < pixbuf_height; h++)
02550             {
02551               for (c = 0; c < pixbuf_n_channels; c++)
02552                 {
02553                   if (pixbuf_pixels[w + c] !=
02554                       pixbuf_pixels[(h * pixbuf_rowstride) + w + c])
02555                     break;
02556                 }
02557               if (c < pixbuf_n_channels)
02558                 break;
02559             }
02560           if (h < pixbuf_height)
02561             break;
02562         }
02563 
02564       if (w >= pixbuf_width)
02565         {
02566           op->data.image.vertical_stripes = TRUE; 
02567         }
02568       else
02569         {
02570           op->data.image.vertical_stripes = FALSE; 
02571         }
02572       
02573       g_assert (info->op_list);
02574       
02575       meta_draw_op_list_append (info->op_list, op);
02576 
02577       push_state (info, STATE_IMAGE);
02578     }
02579   else if (ELEMENT_IS ("gtk_arrow"))
02580     {
02581       MetaDrawOp *op;
02582       const char *state;
02583       const char *shadow;
02584       const char *arrow;
02585       const char *x;
02586       const char *y;
02587       const char *width;
02588       const char *height;
02589       const char *filled;
02590       gboolean filled_val;
02591       GtkStateType state_val;
02592       GtkShadowType shadow_val;
02593       GtkArrowType arrow_val;
02594       
02595       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
02596                               error,
02597                               "state", &state,
02598                               "shadow", &shadow,
02599                               "arrow", &arrow,
02600                               "x", &x, "y", &y,
02601                               "width", &width, "height", &height,
02602                               "filled", &filled,
02603                               NULL))
02604         return;
02605 
02606       if (state == NULL)
02607         {
02608           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02609                      _("No \"state\" attribute on element <%s>"), element_name);
02610           return;
02611         }
02612 
02613       if (shadow == NULL)
02614         {
02615           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02616                      _("No \"shadow\" attribute on element <%s>"), element_name);
02617           return;
02618         }
02619 
02620       if (arrow == NULL)
02621         {
02622           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02623                      _("No \"arrow\" attribute on element <%s>"), element_name);
02624           return;
02625         }
02626       
02627       if (x == NULL)
02628         {
02629           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02630                      _("No \"x\" attribute on element <%s>"), element_name);
02631           return;
02632         }
02633 
02634       if (y == NULL)
02635         {
02636           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02637                      _("No \"y\" attribute on element <%s>"), element_name);
02638           return;
02639         }
02640 
02641       if (width == NULL)
02642         {
02643           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02644                      _("No \"width\" attribute on element <%s>"), element_name);
02645           return;
02646         }
02647 
02648       if (height == NULL)
02649         {
02650           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02651                      _("No \"height\" attribute on element <%s>"), element_name);
02652           return;
02653         }
02654 #if 0
02655       if (!check_expression (x, FALSE, info->theme, context, error))
02656         return;
02657 
02658       if (!check_expression (y, FALSE, info->theme, context, error))
02659         return;
02660 
02661       if (!check_expression (width, FALSE, info->theme, context, error))
02662         return;
02663       
02664       if (!check_expression (height, FALSE, info->theme, context, error))
02665         return;
02666 #endif
02667       filled_val = TRUE;
02668       if (filled && !parse_boolean (filled, &filled_val, context, error))
02669         return;
02670 
02671       state_val = meta_gtk_state_from_string (state);
02672       if (((int) state_val) == -1)
02673         {
02674           set_error (error, context, G_MARKUP_ERROR,
02675                      G_MARKUP_ERROR_PARSE,
02676                      _("Did not understand state \"%s\" for <%s> element"),
02677                      state, element_name);
02678           return;
02679         }
02680 
02681       shadow_val = meta_gtk_shadow_from_string (shadow);
02682       if (((int) shadow_val) == -1)
02683         {
02684           set_error (error, context, G_MARKUP_ERROR,
02685                      G_MARKUP_ERROR_PARSE,
02686                      _("Did not understand shadow \"%s\" for <%s> element"),
02687                      shadow, element_name);
02688           return;
02689         }
02690 
02691       arrow_val = meta_gtk_arrow_from_string (arrow);
02692       if (((int) arrow_val) == -1)
02693         {
02694           set_error (error, context, G_MARKUP_ERROR,
02695                      G_MARKUP_ERROR_PARSE,
02696                      _("Did not understand arrow \"%s\" for <%s> element"),
02697                      arrow, element_name);
02698           return;
02699         }
02700       
02701       op = meta_draw_op_new (META_DRAW_GTK_ARROW);
02702 
02703       op->data.gtk_arrow.x = meta_draw_spec_new (info->theme, x, NULL);
02704       op->data.gtk_arrow.y = meta_draw_spec_new (info->theme, y, NULL);
02705       op->data.gtk_arrow.width = meta_draw_spec_new (info->theme, width, NULL);
02706       op->data.gtk_arrow.height = meta_draw_spec_new (info->theme, 
02707                                                       height, NULL);
02708 
02709       op->data.gtk_arrow.filled = filled_val;
02710       op->data.gtk_arrow.state = state_val;
02711       op->data.gtk_arrow.shadow = shadow_val;
02712       op->data.gtk_arrow.arrow = arrow_val;
02713       
02714       g_assert (info->op_list);
02715       
02716       meta_draw_op_list_append (info->op_list, op);
02717 
02718       push_state (info, STATE_GTK_ARROW);
02719     }
02720   else if (ELEMENT_IS ("gtk_box"))
02721     {
02722       MetaDrawOp *op;
02723       const char *state;
02724       const char *shadow;
02725       const char *x;
02726       const char *y;
02727       const char *width;
02728       const char *height;
02729       GtkStateType state_val;
02730       GtkShadowType shadow_val;
02731       
02732       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
02733                               error,
02734                               "state", &state,
02735                               "shadow", &shadow,
02736                               "x", &x, "y", &y,
02737                               "width", &width, "height", &height,
02738                               NULL))
02739         return;
02740 
02741       if (state == NULL)
02742         {
02743           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02744                      _("No \"state\" attribute on element <%s>"), element_name);
02745           return;
02746         }
02747 
02748       if (shadow == NULL)
02749         {
02750           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02751                      _("No \"shadow\" attribute on element <%s>"), element_name);
02752           return;
02753         }
02754       
02755       if (x == NULL)
02756         {
02757           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02758                      _("No \"x\" attribute on element <%s>"), element_name);
02759           return;
02760         }
02761 
02762       if (y == NULL)
02763         {
02764           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02765                      _("No \"y\" attribute on element <%s>"), element_name);
02766           return;
02767         }
02768 
02769       if (width == NULL)
02770         {
02771           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02772                      _("No \"width\" attribute on element <%s>"), element_name);
02773           return;
02774         }
02775 
02776       if (height == NULL)
02777         {
02778           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02779                      _("No \"height\" attribute on element <%s>"), element_name);
02780           return;
02781         }
02782 #if 0
02783       if (!check_expression (x, FALSE, info->theme, context, error))
02784         return;
02785 
02786       if (!check_expression (y, FALSE, info->theme, context, error))
02787         return;
02788 
02789       if (!check_expression (width, FALSE, info->theme, context, error))
02790         return;
02791       
02792       if (!check_expression (height, FALSE, info->theme, context, error))
02793         return;
02794 #endif
02795       state_val = meta_gtk_state_from_string (state);
02796       if (((int) state_val) == -1)
02797         {
02798           set_error (error, context, G_MARKUP_ERROR,
02799                      G_MARKUP_ERROR_PARSE,
02800                      _("Did not understand state \"%s\" for <%s> element"),
02801                      state, element_name);
02802           return;
02803         }
02804 
02805       shadow_val = meta_gtk_shadow_from_string (shadow);
02806       if (((int) shadow_val) == -1)
02807         {
02808           set_error (error, context, G_MARKUP_ERROR,
02809                      G_MARKUP_ERROR_PARSE,
02810                      _("Did not understand shadow \"%s\" for <%s> element"),
02811                      shadow, element_name);
02812           return;
02813         }
02814       
02815       op = meta_draw_op_new (META_DRAW_GTK_BOX);
02816 
02817       op->data.gtk_box.x = meta_draw_spec_new (info->theme, x, NULL);
02818       op->data.gtk_box.y = meta_draw_spec_new (info->theme, y, NULL);
02819       op->data.gtk_box.width = meta_draw_spec_new (info->theme, width, NULL);
02820       op->data.gtk_box.height = meta_draw_spec_new (info->theme, height, NULL);
02821 
02822       op->data.gtk_box.state = state_val;
02823       op->data.gtk_box.shadow = shadow_val;
02824       
02825       g_assert (info->op_list);
02826       
02827       meta_draw_op_list_append (info->op_list, op);
02828 
02829       push_state (info, STATE_GTK_BOX);
02830     }
02831   else if (ELEMENT_IS ("gtk_vline"))
02832     {
02833       MetaDrawOp *op;
02834       const char *state;
02835       const char *x;
02836       const char *y1;
02837       const char *y2;
02838       GtkStateType state_val;
02839       
02840       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
02841                               error,
02842                               "state", &state,
02843                               "x", &x, "y1", &y1, "y2", &y2,
02844                               NULL))
02845         return;
02846 
02847       if (state == NULL)
02848         {
02849           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02850                      _("No \"state\" attribute on element <%s>"), element_name);
02851           return;
02852         }
02853       
02854       if (x == NULL)
02855         {
02856           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02857                      _("No \"x\" attribute on element <%s>"), element_name);
02858           return;
02859         }
02860 
02861       if (y1 == NULL)
02862         {
02863           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02864                      _("No \"y1\" attribute on element <%s>"), element_name);
02865           return;
02866         }
02867 
02868       if (y2 == NULL)
02869         {
02870           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02871                      _("No \"y2\" attribute on element <%s>"), element_name);
02872           return;
02873         }
02874       
02875 #if 0
02876       if (!check_expression (x, FALSE, info->theme, context, error))
02877         return;
02878 
02879       if (!check_expression (y1, FALSE, info->theme, context, error))
02880         return;
02881 
02882       if (!check_expression (y2, FALSE, info->theme, context, error))
02883         return;
02884 #endif
02885 
02886       state_val = meta_gtk_state_from_string (state);
02887       if (((int) state_val) == -1)
02888         {
02889           set_error (error, context, G_MARKUP_ERROR,
02890                      G_MARKUP_ERROR_PARSE,
02891                      _("Did not understand state \"%s\" for <%s> element"),
02892                      state, element_name);
02893           return;
02894         }
02895       
02896       op = meta_draw_op_new (META_DRAW_GTK_VLINE);
02897 
02898       op->data.gtk_vline.x = meta_draw_spec_new (info->theme, x, NULL);
02899       op->data.gtk_vline.y1 = meta_draw_spec_new (info->theme, y1, NULL);
02900       op->data.gtk_vline.y2 = meta_draw_spec_new (info->theme, y2, NULL);
02901 
02902       op->data.gtk_vline.state = state_val;
02903       
02904       g_assert (info->op_list);
02905       
02906       meta_draw_op_list_append (info->op_list, op);
02907 
02908       push_state (info, STATE_GTK_VLINE);
02909     }
02910   else if (ELEMENT_IS ("icon"))
02911     {
02912       MetaDrawOp *op;
02913       const char *x;
02914       const char *y;
02915       const char *width;
02916       const char *height;
02917       const char *alpha;
02918       const char *fill_type;
02919       MetaAlphaGradientSpec *alpha_spec;
02920       MetaImageFillType fill_type_val;
02921       
02922       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
02923                               error,
02924                               "x", &x, "y", &y,
02925                               "width", &width, "height", &height,
02926                               "alpha", &alpha,
02927                               "fill_type", &fill_type,
02928                               NULL))
02929         return;
02930       
02931       if (x == NULL)
02932         {
02933           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02934                      _("No \"x\" attribute on element <%s>"), element_name);
02935           return;
02936         }
02937 
02938       if (y == NULL)
02939         {
02940           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02941                      _("No \"y\" attribute on element <%s>"), element_name);
02942           return;
02943         }
02944 
02945       if (width == NULL)
02946         {
02947           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02948                      _("No \"width\" attribute on element <%s>"), element_name);
02949           return;
02950         }
02951 
02952       if (height == NULL)
02953         {
02954           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
02955                      _("No \"height\" attribute on element <%s>"), element_name);
02956           return;
02957         }
02958 #if 0      
02959       if (!check_expression (x, FALSE, info->theme, context, error))
02960         return;
02961 
02962       if (!check_expression (y, FALSE, info->theme, context, error))
02963         return;
02964 
02965       if (!check_expression (width, FALSE, info->theme, context, error))
02966         return;
02967       
02968       if (!check_expression (height, FALSE, info->theme, context, error))
02969         return;
02970 #endif
02971       fill_type_val = META_IMAGE_FILL_SCALE;
02972       if (fill_type)
02973         {
02974           fill_type_val = meta_image_fill_type_from_string (fill_type);
02975 
02976           if (((int) fill_type_val) == -1)
02977             {
02978               set_error (error, context, G_MARKUP_ERROR,
02979                          G_MARKUP_ERROR_PARSE,
02980                          _("Did not understand fill type \"%s\" for <%s> element"),
02981                          fill_type, element_name);
02982             }
02983         }
02984       
02985       alpha_spec = NULL;
02986       if (alpha && !parse_alpha (alpha, &alpha_spec, context, error))
02987         return;
02988       
02989       op = meta_draw_op_new (META_DRAW_ICON);
02990       
02991       op->data.icon.x = meta_draw_spec_new (info->theme, x, NULL);
02992       op->data.icon.y = meta_draw_spec_new (info->theme, y, NULL);
02993       op->data.icon.width = meta_draw_spec_new (info->theme, width, NULL);
02994       op->data.icon.height = meta_draw_spec_new (info->theme, height, NULL);
02995 
02996       op->data.icon.alpha_spec = alpha_spec;
02997       op->data.icon.fill_type = fill_type_val;
02998       
02999       g_assert (info->op_list);
03000       
03001       meta_draw_op_list_append (info->op_list, op);
03002 
03003       push_state (info, STATE_ICON);
03004     }
03005   else if (ELEMENT_IS ("title"))
03006     {
03007       MetaDrawOp *op;
03008       const char *color;
03009       const char *x;
03010       const char *y;
03011       MetaColorSpec *color_spec;
03012       
03013       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
03014                               error,
03015                               "color", &color,
03016                               "x", &x, "y", &y,
03017                               NULL))
03018         return;
03019 
03020       if (color == NULL)
03021         {
03022           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03023                      _("No \"color\" attribute on element <%s>"), element_name);
03024           return;
03025         }
03026       
03027       if (x == NULL)
03028         {
03029           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03030                      _("No \"x\" attribute on element <%s>"), element_name);
03031           return;
03032         }
03033 
03034       if (y == NULL)
03035         {
03036           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03037                      _("No \"y\" attribute on element <%s>"), element_name);
03038           return;
03039         }
03040       
03041 #if 0
03042       if (!check_expression (x, FALSE, info->theme, context, error))
03043         return;
03044 
03045       if (!check_expression (y, FALSE, info->theme, context, error))
03046         return;
03047 #endif
03048 
03049       /* Check last so we don't have to free it when other
03050        * stuff fails
03051        */
03052       color_spec = parse_color (info->theme, color, error);
03053       if (color_spec == NULL)
03054         {
03055           add_context_to_error (error, context);
03056           return;
03057         }
03058       
03059       op = meta_draw_op_new (META_DRAW_TITLE);
03060 
03061       op->data.title.color_spec = color_spec;
03062 
03063       op->data.title.x = meta_draw_spec_new (info->theme, x, NULL);
03064       op->data.title.y = meta_draw_spec_new (info->theme, y, NULL);
03065 
03066       g_assert (info->op_list);
03067       
03068       meta_draw_op_list_append (info->op_list, op);
03069 
03070       push_state (info, STATE_TITLE);
03071     }
03072   else if (ELEMENT_IS ("include"))
03073     {
03074       MetaDrawOp *op;
03075       const char *name;
03076       const char *x;
03077       const char *y;
03078       const char *width;
03079       const char *height;
03080       MetaDrawOpList *op_list;
03081       
03082       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
03083                               error,
03084                               "x", &x, "y", &y,
03085                               "width", &width, "height", &height,
03086                               "name", &name,
03087                               NULL))
03088         return;
03089 
03090       if (name == NULL)
03091         {
03092           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03093                      _("No \"%s\" attribute on <%s> element"), "name", element_name);
03094           return;
03095         }
03096 
03097       /* x/y/width/height default to 0,0,width,height - should
03098        * probably do this for all the draw ops
03099        */
03100 #if 0      
03101       if (x && !check_expression (x, FALSE, info->theme, context, error))
03102         return;
03103 
03104       if (y && !check_expression (y, FALSE, info->theme, context, error))
03105         return;
03106 
03107       if (width && !check_expression (width, FALSE, info->theme, context, error))
03108         return;
03109       
03110       if (height && !check_expression (height, FALSE, info->theme, context, error))
03111         return;
03112 #endif
03113 
03114       op_list = meta_theme_lookup_draw_op_list (info->theme,
03115                                                 name);
03116       if (op_list == NULL)
03117         {
03118           set_error (error, context, G_MARKUP_ERROR,
03119                      G_MARKUP_ERROR_PARSE,
03120                      _("No <draw_ops> called \"%s\" has been defined"),
03121                      name);
03122           return;
03123         }
03124 
03125       g_assert (info->op_list);
03126       
03127       if (op_list == info->op_list ||
03128           meta_draw_op_list_contains (op_list, info->op_list))
03129         {
03130           set_error (error, context, G_MARKUP_ERROR,
03131                      G_MARKUP_ERROR_PARSE,
03132                      _("Including draw_ops \"%s\" here would create a circular reference"),
03133                      name);
03134           return;
03135         }
03136       
03137       op = meta_draw_op_new (META_DRAW_OP_LIST);
03138 
03139       meta_draw_op_list_ref (op_list);
03140       op->data.op_list.op_list = op_list;      
03141 
03142       op->data.op_list.x = meta_draw_spec_new (info->theme, x ? x : "0", NULL);
03143       op->data.op_list.y = meta_draw_spec_new (info->theme, y ? y : "0", NULL);
03144       op->data.op_list.width = meta_draw_spec_new (info->theme, 
03145                                                    width ? width : "width",
03146                                                    NULL);
03147       op->data.op_list.height = meta_draw_spec_new (info->theme,
03148                                                     height ? height : "height",
03149                                                     NULL);
03150 
03151       meta_draw_op_list_append (info->op_list, op);
03152 
03153       push_state (info, STATE_INCLUDE);
03154     }
03155   else if (ELEMENT_IS ("tile"))
03156     {
03157       MetaDrawOp *op;
03158       const char *name;
03159       const char *x;
03160       const char *y;
03161       const char *width;
03162       const char *height;
03163       const char *tile_xoffset;
03164       const char *tile_yoffset;
03165       const char *tile_width;
03166       const char *tile_height;
03167       MetaDrawOpList *op_list;
03168       
03169       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
03170                               error,
03171                               "x", &x, "y", &y,
03172                               "width", &width, "height", &height,
03173                               "name", &name,
03174                               "tile_xoffset", &tile_xoffset,
03175                               "tile_yoffset", &tile_yoffset,
03176                               "tile_width", &tile_width,
03177                               "tile_height", &tile_height,
03178                               NULL))
03179         return;
03180 
03181       if (name == NULL)
03182         {
03183           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03184                      _("No \"%s\" attribute on <%s> element"), "name", element_name);
03185           return;
03186         }
03187 
03188       if (tile_width == NULL)
03189         {
03190           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03191                      _("No \"%s\" attribute on <%s> element"), "tile_width", element_name);
03192           return;
03193         }
03194 
03195       if (tile_height == NULL)
03196         {
03197           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03198                      _("No \"%s\" attribute on <%s> element"), "tile_height", element_name);
03199           return;
03200         }
03201 
03202       /* These default to 0 */
03203 #if 0
03204       if (tile_xoffset && !check_expression (tile_xoffset, FALSE, info->theme, context, error))
03205         return;
03206 
03207       if (tile_yoffset && !check_expression (tile_yoffset, FALSE, info->theme, context, error))
03208         return;
03209       
03210       /* x/y/width/height default to 0,0,width,height - should
03211        * probably do this for all the draw ops
03212        */
03213       if (x && !check_expression (x, FALSE, info->theme, context, error))
03214         return;
03215 
03216       if (y && !check_expression (y, FALSE, info->theme, context, error))
03217         return;
03218 
03219       if (width && !check_expression (width, FALSE, info->theme, context, error))
03220         return;
03221       
03222       if (height && !check_expression (height, FALSE, info->theme, context, error))
03223         return;
03224 
03225       if (!check_expression (tile_width, FALSE, info->theme, context, error))
03226         return;
03227 
03228       if (!check_expression (tile_height, FALSE, info->theme, context, error))
03229         return;
03230 #endif 
03231       op_list = meta_theme_lookup_draw_op_list (info->theme,
03232                                                 name);
03233       if (op_list == NULL)
03234         {
03235           set_error (error, context, G_MARKUP_ERROR,
03236                      G_MARKUP_ERROR_PARSE,
03237                      _("No <draw_ops> called \"%s\" has been defined"),
03238                      name);
03239           return;
03240         }
03241 
03242       g_assert (info->op_list);
03243       
03244       if (op_list == info->op_list ||
03245           meta_draw_op_list_contains (op_list, info->op_list))
03246         {
03247           set_error (error, context, G_MARKUP_ERROR,
03248                      G_MARKUP_ERROR_PARSE,
03249                      _("Including draw_ops \"%s\" here would create a circular reference"),
03250                      name);
03251           return;
03252         }
03253       
03254       op = meta_draw_op_new (META_DRAW_TILE);
03255 
03256       meta_draw_op_list_ref (op_list);
03257 
03258       op->data.tile.x = meta_draw_spec_new (info->theme, x ? x : "0", NULL);
03259       op->data.tile.y = meta_draw_spec_new (info->theme, y ? y : "0", NULL);
03260       op->data.tile.width = meta_draw_spec_new (info->theme,
03261                                                 width ? width : "width",
03262                                                 NULL);
03263       op->data.tile.height = meta_draw_spec_new (info->theme,
03264                                                  height ? height : "height",
03265                                                  NULL);
03266       op->data.tile.tile_xoffset = meta_draw_spec_new (info->theme,
03267                                                        tile_xoffset ? tile_xoffset : "0",
03268                                                        NULL);
03269       op->data.tile.tile_yoffset = meta_draw_spec_new (info->theme,
03270                                                        tile_yoffset ? tile_yoffset : "0",
03271                                                        NULL);
03272       op->data.tile.tile_width = meta_draw_spec_new (info->theme, tile_width, NULL);
03273       op->data.tile.tile_height = meta_draw_spec_new (info->theme, tile_height, NULL);
03274 
03275       op->data.tile.op_list = op_list;      
03276       
03277       meta_draw_op_list_append (info->op_list, op);
03278 
03279       push_state (info, STATE_TILE);
03280     }
03281   else
03282     {
03283       set_error (error, context,
03284                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03285                  _("Element <%s> is not allowed below <%s>"),
03286                  element_name, "draw_ops");
03287     }
03288 }
03289 
03290 static void
03291 parse_gradient_element (GMarkupParseContext  *context,
03292                         const gchar          *element_name,
03293                         const gchar         **attribute_names,
03294                         const gchar         **attribute_values,
03295                         ParseInfo            *info,
03296                         GError              **error)
03297 {
03298   g_return_if_fail (peek_state (info) == STATE_GRADIENT);
03299 
03300   if (ELEMENT_IS ("color"))
03301     {
03302       const char *value = NULL;
03303       MetaColorSpec *color_spec;
03304 
03305       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
03306                               error,
03307                               "value", &value,
03308                               NULL))
03309         return;
03310 
03311       if (value == NULL)
03312         {
03313           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03314                      _("No \"value\" attribute on <%s> element"),
03315                      element_name);
03316           return;
03317         }
03318 
03319       color_spec = parse_color (info->theme, value, error);
03320       if (color_spec == NULL)
03321         {
03322           add_context_to_error (error, context);
03323           return;
03324         }
03325 
03326       g_assert (info->op);
03327       g_assert (info->op->type == META_DRAW_GRADIENT);
03328       g_assert (info->op->data.gradient.gradient_spec != NULL);
03329       info->op->data.gradient.gradient_spec->color_specs =
03330         g_slist_append (info->op->data.gradient.gradient_spec->color_specs,
03331                         color_spec);
03332       
03333       push_state (info, STATE_COLOR);
03334     }
03335   else
03336     {
03337       set_error (error, context,
03338                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03339                  _("Element <%s> is not allowed below <%s>"),
03340                  element_name, "gradient");
03341     }
03342 }
03343 
03344 static void
03345 parse_style_element (GMarkupParseContext  *context,
03346                      const gchar          *element_name,
03347                      const gchar         **attribute_names,
03348                      const gchar         **attribute_values,
03349                      ParseInfo            *info,
03350                      GError              **error)
03351 {
03352   g_return_if_fail (peek_state (info) == STATE_FRAME_STYLE);
03353 
03354   g_assert (info->style);
03355   
03356   if (ELEMENT_IS ("piece"))
03357     {
03358       const char *position = NULL;
03359       const char *draw_ops = NULL;
03360       
03361       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
03362                               error,
03363                               "position", &position,
03364                               "draw_ops", &draw_ops,
03365                               NULL))
03366         return;
03367 
03368       if (position == NULL)
03369         {
03370           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03371                      _("No \"position\" attribute on <%s> element"),
03372                      element_name);
03373           return;
03374         }
03375 
03376       info->piece = meta_frame_piece_from_string (position);
03377       if (info->piece == META_FRAME_PIECE_LAST)
03378         {
03379           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03380                      _("Unknown position \"%s\" for frame piece"),
03381                      position);
03382           return;
03383         }
03384       
03385       if (info->style->pieces[info->piece] != NULL)
03386         {
03387           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03388                      _("Frame style already has a piece at position %s"),
03389                      position);
03390           return;
03391         }
03392 
03393       g_assert (info->op_list == NULL);
03394       
03395       if (draw_ops)
03396         {
03397           MetaDrawOpList *op_list;
03398 
03399           op_list = meta_theme_lookup_draw_op_list (info->theme,
03400                                                     draw_ops);
03401 
03402           if (op_list == NULL)
03403             {
03404               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03405                          _("No <draw_ops> with the name \"%s\" has been defined"),
03406                          draw_ops);
03407               return;
03408             }
03409 
03410           meta_draw_op_list_ref (op_list);
03411           info->op_list = op_list;
03412         }
03413       
03414       push_state (info, STATE_PIECE);
03415     }
03416   else if (ELEMENT_IS ("button"))
03417     {
03418       const char *function = NULL;
03419       const char *state = NULL;
03420       const char *draw_ops = NULL;
03421       
03422       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
03423                               error,
03424                               "function", &function,
03425                               "state", &state,
03426                               "draw_ops", &draw_ops,
03427                               NULL))
03428         return;
03429 
03430       if (function == NULL)
03431         {
03432           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03433                      _("No \"function\" attribute on <%s> element"),
03434                      element_name);
03435           return;
03436         }
03437 
03438       if (state == NULL)
03439         {
03440           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03441                      _("No \"state\" attribute on <%s> element"),
03442                      element_name);
03443           return;
03444         }
03445       
03446       info->button_type = meta_button_type_from_string (function, info->theme);
03447       if (info->button_type == META_BUTTON_TYPE_LAST)
03448         {
03449           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03450                      _("Unknown function \"%s\" for button"),
03451                      function);
03452           return;
03453         }
03454 
03455       if (meta_theme_earliest_version_with_button (info->button_type) >
03456           info->theme->format_version)
03457         {
03458           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03459                      _("Button function \"%s\" does not exist in this version (%d, need %d)"),
03460                      function,
03461                      info->theme->format_version,
03462                      meta_theme_earliest_version_with_button (info->button_type)
03463                      );
03464           return;
03465         }
03466 
03467       info->button_state = meta_button_state_from_string (state);
03468       if (info->button_state == META_BUTTON_STATE_LAST)
03469         {
03470           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03471                      _("Unknown state \"%s\" for button"),
03472                      state);
03473           return;
03474         }
03475       
03476       if (info->style->buttons[info->button_type][info->button_state] != NULL)
03477         {
03478           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03479                      _("Frame style already has a button for function %s state %s"),
03480                      function, state);
03481           return;
03482         }
03483 
03484       g_assert (info->op_list == NULL);
03485       
03486       if (draw_ops)
03487         {
03488           MetaDrawOpList *op_list;
03489 
03490           op_list = meta_theme_lookup_draw_op_list (info->theme,
03491                                                     draw_ops);
03492 
03493           if (op_list == NULL)
03494             {
03495               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03496                          _("No <draw_ops> with the name \"%s\" has been defined"),
03497                          draw_ops);
03498               return;
03499             }
03500 
03501           meta_draw_op_list_ref (op_list);
03502           info->op_list = op_list;
03503         }
03504       
03505       push_state (info, STATE_BUTTON);
03506     }
03507   else
03508     {
03509       set_error (error, context,
03510                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03511                  _("Element <%s> is not allowed below <%s>"),
03512                  element_name, "frame_style");
03513     }
03514 }
03515 
03516 static void
03517 parse_style_set_element (GMarkupParseContext  *context,
03518                          const gchar          *element_name,
03519                          const gchar         **attribute_names,
03520                          const gchar         **attribute_values,
03521                          ParseInfo            *info,
03522                          GError              **error)
03523 {
03524   g_return_if_fail (peek_state (info) == STATE_FRAME_STYLE_SET);
03525 
03526   if (ELEMENT_IS ("frame"))
03527     {
03528       const char *focus = NULL;
03529       const char *state = NULL;
03530       const char *resize = NULL;
03531       const char *style = NULL;
03532       MetaFrameFocus frame_focus;
03533       MetaFrameState frame_state;
03534       MetaFrameResize frame_resize;
03535       MetaFrameStyle *frame_style;
03536       
03537       if (!locate_attributes (context, element_name, attribute_names, attribute_values,
03538                               error,
03539                               "focus", &focus,
03540                               "state", &state,
03541                               "resize", &resize,
03542                               "style", &style,
03543                               NULL))
03544         return;
03545 
03546       if (focus == NULL)
03547         {
03548           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03549                      _("No \"focus\" attribute on <%s> element"),
03550                      element_name);
03551           return;
03552         }
03553 
03554       if (state == NULL)
03555         {
03556           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03557                      _("No \"state\" attribute on <%s> element"),
03558                      element_name);
03559           return;
03560         }
03561       
03562       if (style == NULL)
03563         {
03564           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03565                      _("No \"style\" attribute on <%s> element"),
03566                      element_name);
03567           return;
03568         }
03569 
03570       frame_focus = meta_frame_focus_from_string (focus);
03571       if (frame_focus == META_FRAME_FOCUS_LAST)
03572         {
03573           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03574                      _("\"%s\" is not a valid value for focus attribute"),
03575                      focus);
03576           return;
03577         }
03578       
03579       frame_state = meta_frame_state_from_string (state);
03580       if (frame_state == META_FRAME_STATE_LAST)
03581         {
03582           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03583                      _("\"%s\" is not a valid value for state attribute"),
03584                      focus);
03585           return;
03586         }
03587 
03588       frame_style = meta_theme_lookup_style (info->theme, style);
03589 
03590       if (frame_style == NULL)
03591         {
03592           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03593                      _("A style called \"%s\" has not been defined"),
03594                      style);
03595           return;
03596         }
03597 
03598       switch (frame_state)
03599         {
03600         case META_FRAME_STATE_NORMAL:
03601           if (resize == NULL)
03602             {
03603               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03604                          _("No \"resize\" attribute on <%s> element"),
03605                          element_name);
03606               return;
03607             }
03608 
03609           
03610           frame_resize = meta_frame_resize_from_string (resize);
03611           if (frame_resize == META_FRAME_RESIZE_LAST)
03612             {
03613               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03614                          _("\"%s\" is not a valid value for resize attribute"),
03615                          focus);
03616               return;
03617             }
03618           
03619           break;
03620 
03621         case META_FRAME_STATE_SHADED:
03622           if (META_THEME_ALLOWS (info->theme, META_THEME_UNRESIZABLE_SHADED_STYLES))
03623             {
03624               if (resize == NULL)
03625                 /* In state="normal" we would complain here. But instead we accept
03626                  * not having a resize attribute and default to resize="both", since
03627                  * that most closely mimics what we did in v1, and thus people can
03628                  * upgrade a theme to v2 without as much hassle.
03629                  */
03630                 frame_resize = META_FRAME_RESIZE_BOTH;
03631               else
03632                 {
03633                   frame_resize = meta_frame_resize_from_string (resize);
03634                   if (frame_resize == META_FRAME_RESIZE_LAST)
03635                     {
03636                       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03637                                  _("\"%s\" is not a valid value for resize attribute"),
03638                                  focus);
03639                       return;
03640                     }
03641                 }
03642             }
03643           else /* v1 theme */
03644             {
03645               if (resize != NULL)
03646                 {
03647                   set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03648                        _("Should not have \"resize\" attribute on <%s> element for maximized/shaded states"),
03649                       element_name);
03650                   return;
03651                 }
03652 
03653               /* resize="both" is equivalent to the old behaviour */
03654               frame_resize = META_FRAME_RESIZE_BOTH;
03655             }
03656           break;
03657           
03658         default:
03659           if (resize != NULL)
03660             {
03661               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03662                          _("Should not have \"resize\" attribute on <%s> element for maximized states"),
03663                          element_name);
03664               return;
03665             }
03666 
03667           frame_resize = META_FRAME_RESIZE_LAST;
03668         }
03669       
03670       switch (frame_state)
03671         {
03672         case META_FRAME_STATE_NORMAL:
03673           if (info->style_set->normal_styles[frame_resize][frame_focus])
03674             {
03675               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03676                          _("Style has already been specified for state %s resize %s focus %s"),
03677                          state, resize, focus);
03678               return;
03679             }
03680           meta_frame_style_ref (frame_style);
03681           info->style_set->normal_styles[frame_resize][frame_focus] = frame_style;
03682           break;
03683         case META_FRAME_STATE_MAXIMIZED:
03684           if (info->style_set->maximized_styles[frame_focus])
03685             {
03686               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03687                          _("Style has already been specified for state %s focus %s"),
03688                          state, focus);
03689               return;
03690             }
03691           meta_frame_style_ref (frame_style);
03692           info->style_set->maximized_styles[frame_focus] = frame_style;
03693           break;
03694         case META_FRAME_STATE_SHADED:
03695           if (info->style_set->shaded_styles[frame_resize][frame_focus])
03696             {
03697               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03698                          _("Style has already been specified for state %s resize %s focus %s"),
03699                          state, resize, focus);
03700               return;
03701             }
03702           meta_frame_style_ref (frame_style);
03703           info->style_set->shaded_styles[frame_resize][frame_focus] = frame_style;
03704           break;
03705         case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
03706           if (info->style_set->maximized_and_shaded_styles[frame_focus])
03707             {
03708               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03709                          _("Style has already been specified for state %s focus %s"),
03710                          state, focus);
03711               return;
03712             }
03713           meta_frame_style_ref (frame_style);
03714           info->style_set->maximized_and_shaded_styles[frame_focus] = frame_style;
03715           break;
03716         case META_FRAME_STATE_LAST:
03717           g_assert_not_reached ();
03718           break;
03719         }
03720 
03721       push_state (info, STATE_FRAME);      
03722     }
03723   else
03724     {
03725       set_error (error, context,
03726                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03727                  _("Element <%s> is not allowed below <%s>"),
03728                  element_name, "frame_style_set");
03729     }
03730 }
03731 
03732 static void
03733 parse_piece_element (GMarkupParseContext  *context,
03734                      const gchar          *element_name,
03735                      const gchar         **attribute_names,
03736                      const gchar         **attribute_values,
03737                      ParseInfo            *info,
03738                      GError              **error)
03739 {
03740   g_return_if_fail (peek_state (info) == STATE_PIECE);
03741 
03742   if (ELEMENT_IS ("draw_ops"))
03743     {
03744       if (info->op_list)
03745         {
03746           set_error (error, context,
03747                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03748                      _("Can't have a two draw_ops for a <piece> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)"));
03749           return;
03750         }
03751             
03752       if (!check_no_attributes (context, element_name, attribute_names, attribute_values,
03753                                 error))
03754         return;
03755 
03756       g_assert (info->op_list == NULL);
03757       info->op_list = meta_draw_op_list_new (2);
03758 
03759       push_state (info, STATE_DRAW_OPS);
03760     }
03761   else
03762     {
03763       set_error (error, context,
03764                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03765                  _("Element <%s> is not allowed below <%s>"),
03766                  element_name, "piece");
03767     }
03768 }
03769 
03770 static void
03771 parse_button_element (GMarkupParseContext  *context,
03772                       const gchar          *element_name,
03773                       const gchar         **attribute_names,
03774                       const gchar         **attribute_values,
03775                       ParseInfo            *info,
03776                       GError              **error)
03777 {
03778   g_return_if_fail (peek_state (info) == STATE_BUTTON);
03779   
03780   if (ELEMENT_IS ("draw_ops"))
03781     {
03782       if (info->op_list)
03783         {
03784           set_error (error, context,
03785                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03786                      _("Can't have a two draw_ops for a <button> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)"));
03787           return;
03788         }
03789             
03790       if (!check_no_attributes (context, element_name, attribute_names, attribute_values,
03791                                 error))
03792         return;
03793 
03794       g_assert (info->op_list == NULL);
03795       info->op_list = meta_draw_op_list_new (2);
03796 
03797       push_state (info, STATE_DRAW_OPS);
03798     }
03799   else
03800     {
03801       set_error (error, context,
03802                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03803                  _("Element <%s> is not allowed below <%s>"),
03804                  element_name, "button");
03805     }
03806 }
03807 
03808 static void
03809 parse_menu_icon_element (GMarkupParseContext  *context,
03810                          const gchar          *element_name,
03811                          const gchar         **attribute_names,
03812                          const gchar         **attribute_values,
03813                          ParseInfo            *info,
03814                          GError              **error)
03815 {
03816   g_return_if_fail (peek_state (info) == STATE_MENU_ICON);
03817 
03818   if (ELEMENT_IS ("draw_ops"))
03819     {
03820       if (info->op_list)
03821         {
03822           set_error (error, context,
03823                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03824                      _("Can't have a two draw_ops for a <menu_icon> element (theme specified a draw_ops attribute and also a <draw_ops> element, or specified two elements)"));
03825           return;
03826         }
03827             
03828       if (!check_no_attributes (context, element_name, attribute_names, attribute_values,
03829                                 error))
03830         return;
03831 
03832       g_assert (info->op_list == NULL);
03833       info->op_list = meta_draw_op_list_new (2);
03834 
03835       push_state (info, STATE_DRAW_OPS);
03836     }
03837   else
03838     {
03839       set_error (error, context,
03840                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03841                  _("Element <%s> is not allowed below <%s>"),
03842                  element_name, "menu_icon");
03843     }
03844 }
03845 
03846 
03847 static void
03848 start_element_handler (GMarkupParseContext *context,
03849                        const gchar         *element_name,
03850                        const gchar        **attribute_names,
03851                        const gchar        **attribute_values,
03852                        gpointer             user_data,
03853                        GError             **error)
03854 {
03855   ParseInfo *info = user_data;
03856 
03857   switch (peek_state (info))
03858     {
03859     case STATE_START:
03860       if (strcmp (element_name, "metacity_theme") == 0)
03861         {
03862           info->theme = meta_theme_new ();
03863           info->theme->name = g_strdup (info->theme_name);
03864           info->theme->filename = g_strdup (info->theme_file);
03865           info->theme->dirname = g_strdup (info->theme_dir);
03866           info->theme->format_version = info->format_version;
03867           
03868           push_state (info, STATE_THEME);
03869         }
03870       else
03871         set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03872                    _("Outermost element in theme must be <metacity_theme> not <%s>"),
03873                    element_name);
03874       break;
03875 
03876     case STATE_THEME:
03877       parse_toplevel_element (context, element_name,
03878                               attribute_names, attribute_values,
03879                               info, error);
03880       break;
03881     case STATE_INFO:
03882       parse_info_element (context, element_name,
03883                           attribute_names, attribute_values,
03884                           info, error);
03885       break;
03886     case STATE_NAME:
03887     case STATE_AUTHOR:
03888     case STATE_COPYRIGHT:
03889     case STATE_DATE:
03890     case STATE_DESCRIPTION:
03891       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03892                  _("Element <%s> is not allowed inside a name/author/date/description element"),
03893                  element_name);
03894       break;
03895     case STATE_CONSTANT:
03896       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03897                  _("Element <%s> is not allowed inside a <constant> element"),
03898                  element_name);
03899       break;
03900     case STATE_FRAME_GEOMETRY:
03901       parse_geometry_element (context, element_name,
03902                               attribute_names, attribute_values,
03903                               info, error);
03904       break;
03905     case STATE_DISTANCE:
03906     case STATE_BORDER:
03907     case STATE_ASPECT_RATIO:
03908       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03909                  _("Element <%s> is not allowed inside a distance/border/aspect_ratio element"),
03910                  element_name);
03911       break;
03912     case STATE_DRAW_OPS:
03913       parse_draw_op_element (context, element_name,
03914                              attribute_names, attribute_values,
03915                              info, error);
03916       break;
03917     case STATE_LINE:
03918     case STATE_RECTANGLE:
03919     case STATE_ARC:
03920     case STATE_CLIP:
03921     case STATE_TINT:
03922     case STATE_IMAGE:
03923     case STATE_GTK_ARROW:
03924     case STATE_GTK_BOX:
03925     case STATE_GTK_VLINE:
03926     case STATE_ICON:
03927     case STATE_TITLE:
03928     case STATE_INCLUDE:
03929     case STATE_TILE:
03930       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03931                  _("Element <%s> is not allowed inside a draw operation element"),
03932                  element_name);
03933       break;
03934     case STATE_GRADIENT:
03935       parse_gradient_element (context, element_name,
03936                               attribute_names, attribute_values,
03937                               info, error);
03938       break;
03939     case STATE_COLOR:
03940       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03941                  _("Element <%s> is not allowed inside a <%s> element"),
03942                  element_name, "color");
03943       break;
03944     case STATE_FRAME_STYLE:
03945       parse_style_element (context, element_name,
03946                            attribute_names, attribute_values,
03947                            info, error);
03948       break;
03949     case STATE_PIECE:
03950       parse_piece_element (context, element_name,
03951                            attribute_names, attribute_values,
03952                            info, error);
03953       break;
03954     case STATE_BUTTON:
03955       parse_button_element (context, element_name,
03956                             attribute_names, attribute_values,
03957                             info, error);
03958       break;
03959     case STATE_MENU_ICON:
03960       parse_menu_icon_element (context, element_name,
03961                                attribute_names, attribute_values,
03962                                info, error);
03963       break;
03964     case STATE_FRAME_STYLE_SET:
03965       parse_style_set_element (context, element_name,
03966                                attribute_names, attribute_values,
03967                                info, error);
03968       break;
03969     case STATE_FRAME:
03970       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03971                  _("Element <%s> is not allowed inside a <%s> element"),
03972                  element_name, "frame");
03973       break;
03974     case STATE_WINDOW:
03975       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03976                  _("Element <%s> is not allowed inside a <%s> element"),
03977                  element_name, "window");
03978       break;
03979     case STATE_FALLBACK:
03980       set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
03981                  _("Element <%s> is not allowed inside a <%s> element"),
03982                  element_name, "fallback");
03983       break;
03984     }
03985 }
03986 
03987 static void
03988 end_element_handler (GMarkupParseContext *context,
03989                      const gchar         *element_name,
03990                      gpointer             user_data,
03991                      GError             **error)
03992 {
03993   ParseInfo *info = user_data;
03994 
03995   switch (peek_state (info))
03996     {
03997     case STATE_START:
03998       break;
03999     case STATE_THEME:
04000       g_assert (info->theme);
04001 
04002       if (!meta_theme_validate (info->theme, error))
04003         {
04004           add_context_to_error (error, context);
04005           meta_theme_free (info->theme);
04006           info->theme = NULL;
04007         }
04008       
04009       pop_state (info);
04010       g_assert (peek_state (info) == STATE_START);
04011       break;
04012     case STATE_INFO:
04013       pop_state (info);
04014       g_assert (peek_state (info) == STATE_THEME);
04015       break;
04016     case STATE_NAME:
04017       pop_state (info);
04018       g_assert (peek_state (info) == STATE_INFO);
04019       break;
04020     case STATE_AUTHOR:
04021       pop_state (info);
04022       g_assert (peek_state (info) == STATE_INFO);
04023       break;
04024     case STATE_COPYRIGHT:
04025       pop_state (info);
04026       g_assert (peek_state (info) == STATE_INFO);
04027       break;
04028     case STATE_DATE:
04029       pop_state (info);
04030       g_assert (peek_state (info) == STATE_INFO);
04031       break;
04032     case STATE_DESCRIPTION:
04033       pop_state (info);
04034       g_assert (peek_state (info) == STATE_INFO);
04035       break;
04036     case STATE_CONSTANT:
04037       pop_state (info);
04038       g_assert (peek_state (info) == STATE_THEME);
04039       break;
04040     case STATE_FRAME_GEOMETRY:
04041       g_assert (info->layout);
04042 
04043       if (!meta_frame_layout_validate (info->layout,
04044                                        error))
04045         {
04046           add_context_to_error (error, context);
04047         }
04048 
04049       /* layout will already be stored in the theme under
04050        * its name
04051        */
04052       meta_frame_layout_unref (info->layout);
04053       info->layout = NULL;
04054       pop_state (info);
04055       g_assert (peek_state (info) == STATE_THEME);
04056       break;
04057     case STATE_DISTANCE:
04058       pop_state (info);
04059       g_assert (peek_state (info) == STATE_FRAME_GEOMETRY);
04060       break;
04061     case STATE_BORDER:
04062       pop_state (info);
04063       g_assert (peek_state (info) == STATE_FRAME_GEOMETRY);
04064       break;
04065     case STATE_ASPECT_RATIO:
04066       pop_state (info);
04067       g_assert (peek_state (info) == STATE_FRAME_GEOMETRY);
04068       break;
04069     case STATE_DRAW_OPS:
04070       {
04071         g_assert (info->op_list);
04072         
04073         if (!meta_draw_op_list_validate (info->op_list,
04074                                          error))
04075           {
04076             add_context_to_error (error, context);
04077             meta_draw_op_list_unref (info->op_list);
04078             info->op_list = NULL;
04079           }
04080 
04081         pop_state (info);
04082 
04083         switch (peek_state (info))
04084           {
04085           case STATE_BUTTON:
04086           case STATE_PIECE:
04087           case STATE_MENU_ICON:
04088             /* Leave info->op_list to be picked up
04089              * when these elements are closed
04090              */
04091             g_assert (info->op_list);
04092             break;
04093           case STATE_THEME:
04094             g_assert (info->op_list);
04095             meta_draw_op_list_unref (info->op_list);
04096             info->op_list = NULL;
04097             break;
04098           default:
04099             /* Op list can't occur in other contexts */
04100             g_assert_not_reached ();
04101             break;
04102           }
04103       }
04104       break;
04105     case STATE_LINE:
04106       pop_state (info);
04107       g_assert (peek_state (info) == STATE_DRAW_OPS);
04108       break;
04109     case STATE_RECTANGLE:
04110       pop_state (info);
04111       g_assert (peek_state (info) == STATE_DRAW_OPS);
04112       break;
04113     case STATE_ARC:
04114       pop_state (info);
04115       g_assert (peek_state (info) == STATE_DRAW_OPS);
04116       break;
04117     case STATE_CLIP:
04118       pop_state (info);
04119       g_assert (peek_state (info) == STATE_DRAW_OPS);
04120       break;
04121     case STATE_TINT:
04122       pop_state (info);
04123       g_assert (peek_state (info) == STATE_DRAW_OPS);
04124       break;
04125     case STATE_GRADIENT:
04126       g_assert (info->op);
04127       g_assert (info->op->type == META_DRAW_GRADIENT);
04128       if (!meta_gradient_spec_validate (info->op->data.gradient.gradient_spec,
04129                                         error))
04130         {
04131           add_context_to_error (error, context);
04132           meta_draw_op_free (info->op);
04133           info->op = NULL;
04134         }
04135       else
04136         {
04137           g_assert (info->op_list);
04138           meta_draw_op_list_append (info->op_list, info->op);
04139           info->op = NULL;
04140         }
04141       pop_state (info);
04142       g_assert (peek_state (info) == STATE_DRAW_OPS);
04143       break;
04144     case STATE_IMAGE:
04145       pop_state (info);
04146       g_assert (peek_state (info) == STATE_DRAW_OPS);
04147       break;
04148     case STATE_GTK_ARROW:
04149       pop_state (info);
04150       g_assert (peek_state (info) == STATE_DRAW_OPS);
04151       break;
04152     case STATE_GTK_BOX:
04153       pop_state (info);
04154       g_assert (peek_state (info) == STATE_DRAW_OPS);
04155       break;
04156     case STATE_GTK_VLINE:
04157       pop_state (info);
04158       g_assert (peek_state (info) == STATE_DRAW_OPS);
04159       break;
04160     case STATE_ICON:
04161       pop_state (info);
04162       g_assert (peek_state (info) == STATE_DRAW_OPS);
04163       break;
04164     case STATE_TITLE:
04165       pop_state (info);
04166       g_assert (peek_state (info) == STATE_DRAW_OPS);
04167       break;
04168     case STATE_INCLUDE:
04169       pop_state (info);
04170       g_assert (peek_state (info) == STATE_DRAW_OPS);
04171       break;
04172     case STATE_TILE:
04173       pop_state (info);
04174       g_assert (peek_state (info) == STATE_DRAW_OPS);
04175       break;
04176     case STATE_COLOR:
04177       pop_state (info);
04178       g_assert (peek_state (info) == STATE_GRADIENT);
04179       break;
04180     case STATE_FRAME_STYLE:
04181       g_assert (info->style);
04182 
04183       if (!meta_frame_style_validate (info->style,
04184                                       info->theme->format_version,
04185                                       error))
04186         {
04187           add_context_to_error (error, context);
04188         }
04189 
04190       /* Frame style is in the theme hash table and a ref
04191        * is held there
04192        */
04193       meta_frame_style_unref (info->style);
04194       info->style = NULL;
04195       pop_state (info);
04196       g_assert (peek_state (info) == STATE_THEME);
04197       break;
04198     case STATE_PIECE:
04199       g_assert (info->style);
04200       if (info->op_list == NULL)
04201         {
04202           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
04203                      _("No draw_ops provided for frame piece"));
04204         }
04205       else
04206         {
04207           info->style->pieces[info->piece] = info->op_list;
04208           info->op_list = NULL;
04209         }
04210       pop_state (info);
04211       g_assert (peek_state (info) == STATE_FRAME_STYLE);
04212       break;
04213     case STATE_BUTTON:
04214       g_assert (info->style);
04215       if (info->op_list == NULL)
04216         {
04217           set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
04218                      _("No draw_ops provided for button"));
04219         }
04220       else
04221         {
04222           info->style->buttons[info->button_type][info->button_state] =
04223             info->op_list;
04224           info->op_list = NULL;
04225         }
04226       pop_state (info);
04227       break;
04228     case STATE_MENU_ICON:
04229       g_assert (info->theme);
04230       if (info->op_list != NULL)
04231         {
04232           meta_draw_op_list_unref (info->op_list);
04233           info->op_list = NULL;
04234         }
04235       pop_state (info);
04236       g_assert (peek_state (info) == STATE_THEME);
04237       break;
04238     case STATE_FRAME_STYLE_SET:
04239       g_assert (info->style_set);
04240 
04241       if (!meta_frame_style_set_validate (info->style_set,
04242                                           error))
04243         {
04244           add_context_to_error (error, context);
04245         }
04246 
04247       /* Style set is in the theme hash table and a reference
04248        * is held there.
04249        */
04250       meta_frame_style_set_unref (info->style_set);
04251       info->style_set = NULL;
04252       pop_state (info);
04253       g_assert (peek_state (info) == STATE_THEME);
04254       break;
04255     case STATE_FRAME:
04256       pop_state (info);
04257       g_assert (peek_state (info) == STATE_FRAME_STYLE_SET);
04258       break;
04259     case STATE_WINDOW:
04260       pop_state (info);
04261       g_assert (peek_state (info) == STATE_THEME);
04262       break;
04263     case STATE_FALLBACK:
04264       pop_state (info);
04265       g_assert (peek_state (info) == STATE_THEME);
04266       break;
04267     }
04268 }
04269 
04270 #define NO_TEXT(element_name) set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, _("No text is allowed inside element <%s>"), element_name)
04271 
04272 static gboolean
04273 all_whitespace (const char *text,
04274                 int         text_len)
04275 {
04276   const char *p;
04277   const char *end;
04278   
04279   p = text;
04280   end = text + text_len;
04281   
04282   while (p != end)
04283     {
04284       if (!g_ascii_isspace (*p))
04285         return FALSE;
04286 
04287       p = g_utf8_next_char (p);
04288     }
04289 
04290   return TRUE;
04291 }
04292 
04293 static void
04294 text_handler (GMarkupParseContext *context,
04295               const gchar         *text,
04296               gsize                text_len,
04297               gpointer             user_data,
04298               GError             **error)
04299 {
04300   ParseInfo *info = user_data;
04301 
04302   if (all_whitespace (text, text_len))
04303     return;
04304   
04305   /* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=70448 would
04306    * allow a nice cleanup here.
04307    */
04308 
04309   switch (peek_state (info))
04310     {
04311     case STATE_START:
04312       g_assert_not_reached (); /* gmarkup shouldn't do this */
04313       break;
04314     case STATE_THEME:
04315       NO_TEXT ("metacity_theme");
04316       break;
04317     case STATE_INFO:
04318       NO_TEXT ("info");
04319       break;
04320     case STATE_NAME:
04321       if (info->theme->readable_name != NULL)
04322         {
04323           set_error (error, context, G_MARKUP_ERROR,
04324                      G_MARKUP_ERROR_PARSE,
04325                      _("<name> specified twice for this theme"));
04326           return;
04327         }
04328 
04329       info->theme->readable_name = g_strndup (text, text_len);
04330       break;
04331     case STATE_AUTHOR:
04332       if (info->theme->author != NULL)
04333         {
04334           set_error (error, context, G_MARKUP_ERROR,
04335                      G_MARKUP_ERROR_PARSE,
04336                      _("<author> specified twice for this theme"));
04337           return;
04338         }
04339 
04340       info->theme->author = g_strndup (text, text_len);
04341       break;
04342     case STATE_COPYRIGHT:
04343       if (info->theme->copyright != NULL)
04344         {
04345           set_error (error, context, G_MARKUP_ERROR,
04346                      G_MARKUP_ERROR_PARSE,
04347                      _("<copyright> specified twice for this theme"));
04348           return;
04349         }
04350 
04351       info->theme->copyright = g_strndup (text, text_len);
04352       break;
04353     case STATE_DATE:
04354       if (info->theme->date != NULL)
04355         {
04356           set_error (error, context, G_MARKUP_ERROR,
04357                      G_MARKUP_ERROR_PARSE,
04358                      _("<date> specified twice for this theme"));
04359           return;
04360         }
04361 
04362       info->theme->date = g_strndup (text, text_len);
04363       break;
04364     case STATE_DESCRIPTION:
04365       if (info->theme->description != NULL)
04366         {
04367           set_error (error, context, G_MARKUP_ERROR,
04368                      G_MARKUP_ERROR_PARSE,
04369                      _("<description> specified twice for this theme"));
04370           return;
04371         }
04372 
04373       info->theme->description = g_strndup (text, text_len);
04374       break;
04375     case STATE_CONSTANT:
04376       NO_TEXT ("constant");
04377       break;
04378     case STATE_FRAME_GEOMETRY:
04379       NO_TEXT ("frame_geometry");
04380       break;
04381     case STATE_DISTANCE:
04382       NO_TEXT ("distance");
04383       break;
04384     case STATE_BORDER:
04385       NO_TEXT ("border");
04386       break;
04387     case STATE_ASPECT_RATIO:
04388       NO_TEXT ("aspect_ratio");
04389       break;
04390     case STATE_DRAW_OPS:
04391       NO_TEXT ("draw_ops");
04392       break;
04393     case STATE_LINE:
04394       NO_TEXT ("line");
04395       break;
04396     case STATE_RECTANGLE:
04397       NO_TEXT ("rectangle");
04398       break;
04399     case STATE_ARC:
04400       NO_TEXT ("arc");
04401       break;
04402     case STATE_CLIP:
04403       NO_TEXT ("clip");
04404       break;
04405     case STATE_TINT:
04406       NO_TEXT ("tint");
04407       break;
04408     case STATE_GRADIENT:
04409       NO_TEXT ("gradient");
04410       break;
04411     case STATE_IMAGE:
04412       NO_TEXT ("image");
04413       break;
04414     case STATE_GTK_ARROW:
04415       NO_TEXT ("gtk_arrow");
04416       break;
04417     case STATE_GTK_BOX:
04418       NO_TEXT ("gtk_box");
04419       break;
04420     case STATE_GTK_VLINE:
04421       NO_TEXT ("gtk_vline");
04422       break;
04423     case STATE_ICON:
04424       NO_TEXT ("icon");
04425       break;
04426     case STATE_TITLE:
04427       NO_TEXT ("title");
04428       break;
04429     case STATE_INCLUDE:
04430       NO_TEXT ("include");
04431       break;
04432     case STATE_TILE:
04433       NO_TEXT ("tile");
04434       break;
04435     case STATE_COLOR:
04436       NO_TEXT ("color");
04437       break;
04438     case STATE_FRAME_STYLE:
04439       NO_TEXT ("frame_style");
04440       break;
04441     case STATE_PIECE:
04442       NO_TEXT ("piece");
04443       break;
04444     case STATE_BUTTON:
04445       NO_TEXT ("button");
04446       break;
04447     case STATE_MENU_ICON:
04448       NO_TEXT ("menu_icon");
04449       break;
04450     case STATE_FRAME_STYLE_SET:
04451       NO_TEXT ("frame_style_set");
04452       break;
04453     case STATE_FRAME:
04454       NO_TEXT ("frame");
04455       break;
04456     case STATE_WINDOW:
04457       NO_TEXT ("window");
04458       break;
04459     case STATE_FALLBACK:
04460       NO_TEXT ("fallback");
04461       break;
04462     }
04463 }
04464 
04465 /* We were intending to put the version number
04466  * in the subdirectory name, but we ended up
04467  * using the filename instead.  The "-1" survives
04468  * as a fossil.
04469  */
04470 #define THEME_SUBDIR "metacity-1"
04471 
04472 /* Highest version of the theme format to
04473  * look out for.
04474  */
04475 #define THEME_VERSION 2
04476 
04477 #define METACITY_THEME_FILENAME_FORMAT "metacity-theme-%d.xml"
04478 
04479 MetaTheme*
04480 meta_theme_load (const char *theme_name,
04481                  GError    **err)
04482 {
04483   GMarkupParseContext *context;
04484   GError *error;
04485   ParseInfo info;
04486   char *text;
04487   gsize length;
04488   char *theme_file;
04489   char *theme_dir;
04490   MetaTheme *retval;
04491   guint version;
04492   const gchar* const* xdg_data_dirs;
04493   int i;
04494 
04495   text = NULL;
04496   length = 0;
04497   retval = NULL;
04498   context = NULL;
04499   
04500   theme_dir = NULL;
04501   theme_file = NULL;
04502   
04503   if (meta_is_debugging ())
04504     {
04505       gchar *theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT,
04506                                                THEME_VERSION);
04507 
04508       /* Try in themes in our source tree */
04509       theme_dir = g_build_filename ("./themes", theme_name, NULL);
04510       
04511       theme_file = g_build_filename (theme_dir,
04512                                      theme_filename,
04513                                      NULL);
04514       
04515       error = NULL;
04516       if (!g_file_get_contents (theme_file,
04517                                 &text,
04518                                 &length,
04519                                 &error))
04520         {
04521           meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
04522                       theme_file, error->message);
04523           g_error_free (error);
04524           g_free (theme_dir);
04525           g_free (theme_file);
04526           theme_file = NULL;
04527         }
04528       version = THEME_VERSION;
04529 
04530       g_free (theme_filename);
04531     }
04532   
04533   /* We try all supported versions from current to oldest */
04534   for (version = THEME_VERSION; (version > 0) && (text == NULL); version--)
04535     {
04536       gchar *theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT,
04537                                                version);
04538       
04539       /* We try first in home dir, XDG_DATA_DIRS, then system dir for themes */
04540 
04541       /* Try home dir for themes */
04542       theme_dir = g_build_filename (g_get_home_dir (),
04543                                     ".themes",
04544                                     theme_name,
04545                                     THEME_SUBDIR,
04546                                     NULL);
04547       
04548       theme_file = g_build_filename (theme_dir,
04549                                      theme_filename,
04550                                      NULL);
04551 
04552       error = NULL;
04553       if (!g_file_get_contents (theme_file,
04554                                 &text,
04555                                 &length,
04556                                 &error))
04557         {
04558           meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
04559                       theme_file, error->message);
04560           g_error_free (error);
04561           g_free (theme_dir);
04562           g_free (theme_file);
04563           theme_file = NULL;
04564         }
04565 
04566       /* Try each XDG_DATA_DIRS for theme */
04567       xdg_data_dirs = g_get_system_data_dirs();
04568       for(i = 0; xdg_data_dirs[i] != NULL; i++)
04569         {
04570           if (text == NULL)
04571             {
04572               theme_dir = g_build_filename (xdg_data_dirs[i],
04573                                             "themes",
04574                                             theme_name,
04575                                             THEME_SUBDIR,
04576                                             NULL);
04577 
04578               theme_file = g_build_filename (theme_dir,
04579                                              theme_filename,
04580                                              NULL);
04581 
04582               error = NULL;
04583               if (!g_file_get_contents (theme_file,
04584                                         &text,
04585                                         &length,
04586                                         &error))
04587                 {
04588                   meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
04589                               theme_file, error->message);
04590                   g_error_free (error);
04591                   g_free (theme_dir);
04592                   g_free (theme_file);
04593                   theme_file = NULL;
04594                 }
04595               else
04596                 {
04597                   break;
04598                 }
04599             }
04600         }
04601 
04602       /* Look for themes in METACITY_DATADIR */
04603       if (text == NULL)
04604         {
04605           theme_dir = g_build_filename (METACITY_DATADIR,
04606                                         "themes",
04607                                         theme_name,
04608                                         THEME_SUBDIR,
04609                                         NULL);
04610       
04611           theme_file = g_build_filename (theme_dir,
04612                                          theme_filename,
04613                                          NULL);
04614 
04615           error = NULL;
04616           if (!g_file_get_contents (theme_file,
04617                                     &text,
04618                                     &length,
04619                                     &error))
04620             {
04621               meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
04622                             theme_file, error->message);
04623               g_error_free (error);
04624               g_free (theme_dir);
04625               g_free (theme_file);
04626               theme_file = NULL;
04627             }
04628         }
04629 
04630       g_free (theme_filename);
04631     }
04632 
04633   if (text == NULL)
04634     {
04635       g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_FAILED,
04636           _("Failed to find a valid file for theme %s\n"),
04637           theme_name);
04638 
04639       return NULL; /* all fallbacks failed */
04640     }
04641 
04642   meta_topic (META_DEBUG_THEMES, "Parsing theme file %s\n", theme_file);
04643 
04644 
04645   parse_info_init (&info);
04646   info.theme_name = theme_name;
04647   
04648   /* pass ownership to info so we free it with the info */
04649   info.theme_file = theme_file;
04650   info.theme_dir = theme_dir;
04651 
04652   info.format_version = version + 1;
04653   
04654   context = g_markup_parse_context_new (&metacity_theme_parser,
04655                                         0, &info, NULL);
04656 
04657   error = NULL;
04658   if (!g_markup_parse_context_parse (context,
04659                                      text,
04660                                      length,
04661                                      &error))
04662     goto out;
04663 
04664   error = NULL;
04665   if (!g_markup_parse_context_end_parse (context, &error))
04666     goto out;
04667 
04668   goto out;
04669 
04670  out:
04671 
04672   if (context)
04673     g_markup_parse_context_free (context);
04674   g_free (text);
04675 
04676   if (info.theme)
04677     info.theme->format_version = info.format_version;
04678 
04679   if (error)
04680     {
04681       g_propagate_error (err, error);
04682     }
04683   else if (info.theme)
04684     {
04685       /* Steal theme from info */
04686       retval = info.theme;
04687       info.theme = NULL;
04688     }
04689   else
04690     {
04691       g_set_error (err, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
04692                    _("Theme file %s did not contain a root <metacity_theme> element"),
04693                    info.theme_file);
04694     }
04695 
04696   parse_info_free (&info);
04697 
04698   return retval;
04699 }

Generated on Sat Aug 23 22:04:18 2008 for metacity by  doxygen 1.5.5