Advertisement
Guest User

theme.c

a guest
Oct 10th, 2011
58
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 208.17 KB | None | 0 0
  1. /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
  2.  
  3. /* Metacity Theme Rendering */
  4.  
  5. /*
  6.  * Copyright (C) 2001 Havoc Pennington
  7.  *
  8.  * This program is free software; you can redistribute it and/or
  9.  * modify it under the terms of the GNU General Public License as
  10.  * published by the Free Software Foundation; either version 2 of the
  11.  * License, or (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful, but
  14.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16.  * General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program; if not, write to the Free Software
  20.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  21.  * 02111-1307, USA.
  22.  */
  23.  
  24. /**
  25.  * \file theme.c    Making Metacity look pretty
  26.  *
  27.  * The window decorations drawn by Metacity are described by files on disk
  28.  * known internally as "themes" (externally as "window border themes" on
  29.  * http://art.gnome.org/themes/metacity/ or "Metacity themes"). This file
  30.  * contains most of the code necessary to support themes; it does not
  31.  * contain the XML parser, which is in theme-parser.c.
  32.  *
  33.  * \bug This is a big file with lots of different subsystems, which might
  34.  * be better split out into separate files.
  35.  */
  36.  
  37. /**
  38.  * \defgroup tokenizer   The theme expression tokenizer
  39.  *
  40.  * Themes can use a simple expression language to represent the values of
  41.  * things. This is the tokeniser used for that language.
  42.  *
  43.  * \bug We could remove almost all this code by using GScanner instead,
  44.  * but we would also have to find every expression in every existing theme
  45.  * we could and make sure the parse trees were the same.
  46.  */
  47.  
  48. /**
  49.  * \defgroup parser  The theme expression parser
  50.  *
  51.  * Themes can use a simple expression language to represent the values of
  52.  * things. This is the parser used for that language.
  53.  */
  54.  
  55. #include <config.h>
  56. #include "theme-private.h"
  57. #include <meta/util.h>
  58. #include <meta/gradient.h>
  59. #include <meta/prefs.h>
  60. #include <gtk/gtk.h>
  61. #include <string.h>
  62. #include <stdlib.h>
  63. #include <math.h>
  64.  
  65. #define GDK_COLOR_RGBA(color)                                           \
  66.                          ((guint32) (0xff                         |     \
  67.                                      ((int)((color).red * 255) << 24)   |    \
  68.                                      ((int)((color).green * 255) << 16) |    \
  69.                                      ((int)((color).blue * 255) << 8)))
  70.  
  71. #define GDK_COLOR_RGB(color)                                            \
  72.                          ((guint32) (((int)((color).red * 255) << 16)   |    \
  73.                                      ((int)((color).green * 255) << 8)  |    \
  74.                                      ((int)((color).blue * 255))))
  75.  
  76. #define ALPHA_TO_UCHAR(d) ((unsigned char) ((d) * 255))
  77.  
  78. #define DEBUG_FILL_STRUCT(s) memset ((s), 0xef, sizeof (*(s)))
  79. #define CLAMP_UCHAR(v) ((guchar) (CLAMP (((int)v), (int)0, (int)255)))
  80. #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
  81.  
  82. static void gtk_style_shade     (GdkRGBA     *a,
  83.                      GdkRGBA     *b,
  84.                      gdouble      k);
  85. static void rgb_to_hls          (gdouble     *r,
  86.                      gdouble     *g,
  87.                      gdouble     *b);
  88. static void hls_to_rgb          (gdouble     *h,
  89.                      gdouble     *l,
  90.                      gdouble     *s);
  91.  
  92. /**
  93.  * The current theme. (Themes are singleton.)
  94.  */
  95. static MetaTheme *meta_current_theme = NULL;
  96.  
  97. static GdkPixbuf *
  98. colorize_pixbuf (GdkPixbuf *orig,
  99.                  GdkRGBA   *new_color)
  100. {
  101.   GdkPixbuf *pixbuf;
  102.   double intensity;
  103.   int x, y;
  104.   const guchar *src;
  105.   guchar *dest;
  106.   int orig_rowstride;
  107.   int dest_rowstride;
  108.   int width, height;
  109.   gboolean has_alpha;
  110.   const guchar *src_pixels;
  111.   guchar *dest_pixels;
  112.  
  113.   pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (orig), gdk_pixbuf_get_has_alpha (orig),
  114.                gdk_pixbuf_get_bits_per_sample (orig),
  115.                gdk_pixbuf_get_width (orig), gdk_pixbuf_get_height (orig));
  116.  
  117.   if (pixbuf == NULL)
  118.     return NULL;
  119.  
  120.   orig_rowstride = gdk_pixbuf_get_rowstride (orig);
  121.   dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
  122.   width = gdk_pixbuf_get_width (pixbuf);
  123.   height = gdk_pixbuf_get_height (pixbuf);
  124.   has_alpha = gdk_pixbuf_get_has_alpha (orig);
  125.   src_pixels = gdk_pixbuf_get_pixels (orig);
  126.   dest_pixels = gdk_pixbuf_get_pixels (pixbuf);
  127.  
  128.   for (y = 0; y < height; y++)
  129.     {
  130.       src = src_pixels + y * orig_rowstride;
  131.       dest = dest_pixels + y * dest_rowstride;
  132.  
  133.       for (x = 0; x < width; x++)
  134.         {
  135.           double dr, dg, db;
  136.          
  137.           intensity = INTENSITY (src[0], src[1], src[2]) / 255.0;
  138.  
  139.           if (intensity <= 0.5)
  140.             {
  141.               /* Go from black at intensity = 0.0 to new_color at intensity = 0.5 */
  142.               dr = new_color->red * intensity * 2.0;
  143.               dg = new_color->green * intensity * 2.0;
  144.               db = new_color->blue * intensity * 2.0;
  145.             }
  146.           else
  147.             {
  148.               /* Go from new_color at intensity = 0.5 to white at intensity = 1.0 */
  149.               dr = new_color->red + (1.0 - new_color->red) * (intensity - 0.5) * 2.0;
  150.               dg = new_color->green + (1.0 - new_color->green) * (intensity - 0.5) * 2.0;
  151.               db = new_color->blue + (1.0 - new_color->blue) * (intensity - 0.5) * 2.0;
  152.             }
  153.          
  154.           dest[0] = CLAMP_UCHAR (255 * dr);
  155.           dest[1] = CLAMP_UCHAR (255 * dg);
  156.           dest[2] = CLAMP_UCHAR (255 * db);
  157.          
  158.           if (has_alpha)
  159.             {
  160.               dest[3] = src[3];
  161.               src += 4;
  162.               dest += 4;
  163.             }
  164.           else
  165.             {
  166.               src += 3;
  167.               dest += 3;
  168.             }
  169.         }
  170.     }
  171.  
  172.   return pixbuf;
  173. }
  174.  
  175. static void
  176. color_composite (const GdkRGBA *bg,
  177.                  const GdkRGBA *fg,
  178.                  double         alpha,
  179.                  GdkRGBA       *color)
  180. {
  181.   *color = *bg;
  182.   color->red = color->red + (fg->red - color->red) * alpha;
  183.   color->green = color->green + (fg->green - color->green) * alpha;
  184.   color->blue = color->blue + (fg->blue - color->blue) * alpha;
  185. }
  186.  
  187. /**
  188.  * Sets all the fields of a border to dummy values.
  189.  *
  190.  * \param border The border whose fields should be reset.
  191.  */
  192. static void
  193. init_border (GtkBorder *border)
  194. {
  195.   border->top = -1;
  196.   border->bottom = -1;
  197.   border->left = -1;
  198.   border->right = -1;
  199. }
  200.  
  201. /**
  202.  * meta_frame_layout_new: (skip)
  203.  *
  204.  * Creates a new, empty MetaFrameLayout. The fields will be set to dummy
  205.  * values.
  206.  *
  207.  * Returns: The newly created MetaFrameLayout.
  208.  */
  209. MetaFrameLayout*
  210. meta_frame_layout_new  (void)
  211. {
  212.   MetaFrameLayout *layout;
  213.  
  214.   layout = g_new0 (MetaFrameLayout, 1);
  215.  
  216.   layout->refcount = 1;
  217.  
  218.   /* Fill with -1 values to detect invalid themes */
  219.   layout->left_width = -1;
  220.   layout->right_width = -1;
  221.   layout->bottom_height = -1;
  222.  
  223.   init_border (&layout->title_border);
  224.  
  225.   layout->title_vertical_pad = -1;
  226.  
  227.   layout->right_titlebar_edge = -1;
  228.   layout->left_titlebar_edge = -1;
  229.  
  230.   layout->button_sizing = META_BUTTON_SIZING_LAST;
  231.   layout->button_aspect = 1.0;
  232.   layout->button_width = -1;
  233.   layout->button_height = -1;
  234.  
  235.   layout->has_title = TRUE;
  236.   layout->title_scale = 1.0;
  237.  
  238.   init_border (&layout->button_border);
  239.  
  240.   return layout;
  241. }
  242.  
  243. /**
  244.  *
  245.  */
  246. static gboolean
  247. validate_border (const GtkBorder *border,
  248.                  const char     **bad)
  249. {
  250.   *bad = NULL;
  251.  
  252.   if (border->top < 0)
  253.     *bad = _("top");
  254.   else if (border->bottom < 0)
  255.     *bad = _("bottom");
  256.   else if (border->left < 0)
  257.     *bad = _("left");
  258.   else if (border->right < 0)
  259.     *bad = _("right");
  260.  
  261.   return *bad == NULL;
  262. }
  263.  
  264. /**
  265.  * Ensures that the theme supplied a particular dimension. When a
  266.  * MetaFrameLayout is created, all its integer fields are set to -1
  267.  * by meta_frame_layout_new(). After an instance of this type
  268.  * should have been initialised, this function checks that
  269.  * a given field is not still at -1. It is never called directly, but
  270.  * rather via the CHECK_GEOMETRY_VALUE and CHECK_GEOMETRY_BORDER
  271.  * macros.
  272.  *
  273.  * \param      val    The value to check
  274.  * \param      name   The name to use in the error message
  275.  * \param[out] error  Set to an error if val was not initialised
  276.  */
  277. static gboolean
  278. validate_geometry_value (int         val,
  279.                          const char *name,
  280.                          GError    **error)
  281. {
  282.   if (val < 0)
  283.     {
  284.       g_set_error (error, META_THEME_ERROR,
  285.                    META_THEME_ERROR_FRAME_GEOMETRY,
  286.                    _("frame geometry does not specify \"%s\" dimension"),
  287.                    name);
  288.       return FALSE;
  289.     }
  290.   else
  291.     return TRUE;
  292. }
  293.  
  294. static gboolean
  295. validate_geometry_border (const GtkBorder *border,
  296.                           const char      *name,
  297.                           GError         **error)
  298. {
  299.   const char *bad;
  300.  
  301.   if (!validate_border (border, &bad))
  302.     {
  303.       g_set_error (error, META_THEME_ERROR,
  304.                    META_THEME_ERROR_FRAME_GEOMETRY,
  305.                    _("frame geometry does not specify dimension \"%s\" for border \"%s\""),
  306.                    bad, name);
  307.       return FALSE;
  308.     }
  309.   else
  310.     return TRUE;
  311. }
  312.  
  313. gboolean
  314. meta_frame_layout_validate (const MetaFrameLayout *layout,
  315.                             GError               **error)
  316. {
  317.   g_return_val_if_fail (layout != NULL, FALSE);
  318.  
  319. #define CHECK_GEOMETRY_VALUE(vname) if (!validate_geometry_value (layout->vname, #vname, error)) return FALSE
  320.  
  321. #define CHECK_GEOMETRY_BORDER(bname) if (!validate_geometry_border (&layout->bname, #bname, error)) return FALSE
  322.  
  323.   CHECK_GEOMETRY_VALUE (left_width);
  324.   CHECK_GEOMETRY_VALUE (right_width);
  325.   CHECK_GEOMETRY_VALUE (bottom_height);
  326.  
  327.   CHECK_GEOMETRY_BORDER (title_border);
  328.  
  329.   CHECK_GEOMETRY_VALUE (title_vertical_pad);
  330.  
  331.   CHECK_GEOMETRY_VALUE (right_titlebar_edge);
  332.   CHECK_GEOMETRY_VALUE (left_titlebar_edge);
  333.  
  334.   switch (layout->button_sizing)
  335.     {
  336.     case META_BUTTON_SIZING_ASPECT:
  337.       if (layout->button_aspect < (0.1) ||
  338.           layout->button_aspect > (15.0))
  339.         {
  340.           g_set_error (error, META_THEME_ERROR,
  341.                        META_THEME_ERROR_FRAME_GEOMETRY,
  342.                        _("Button aspect ratio %g is not reasonable"),
  343.                        layout->button_aspect);
  344.           return FALSE;
  345.         }
  346.       break;
  347.     case META_BUTTON_SIZING_FIXED:
  348.       CHECK_GEOMETRY_VALUE (button_width);
  349.       CHECK_GEOMETRY_VALUE (button_height);
  350.       break;
  351.     case META_BUTTON_SIZING_LAST:
  352.       g_set_error (error, META_THEME_ERROR,
  353.                    META_THEME_ERROR_FRAME_GEOMETRY,
  354.                    _("Frame geometry does not specify size of buttons"));
  355.       return FALSE;
  356.     }
  357.  
  358.   CHECK_GEOMETRY_BORDER (button_border);
  359.  
  360.   return TRUE;
  361. }
  362.  
  363. MetaFrameLayout*
  364. meta_frame_layout_copy (const MetaFrameLayout *src)
  365. {
  366.   MetaFrameLayout *layout;
  367.  
  368.   layout = g_new0 (MetaFrameLayout, 1);
  369.  
  370.   *layout = *src;
  371.  
  372.   layout->refcount = 1;
  373.  
  374.   return layout;
  375. }
  376.  
  377. void
  378. meta_frame_layout_ref (MetaFrameLayout *layout)
  379. {
  380.   g_return_if_fail (layout != NULL);
  381.  
  382.   layout->refcount += 1;
  383. }
  384.  
  385. void
  386. meta_frame_layout_unref (MetaFrameLayout *layout)
  387. {
  388.   g_return_if_fail (layout != NULL);
  389.   g_return_if_fail (layout->refcount > 0);
  390.  
  391.   layout->refcount -= 1;
  392.  
  393.   if (layout->refcount == 0)
  394.     {
  395.       DEBUG_FILL_STRUCT (layout);
  396.       g_free (layout);
  397.     }
  398. }
  399.  
  400. void
  401. meta_frame_layout_get_borders (const MetaFrameLayout *layout,
  402.                                int                    text_height,
  403.                                MetaFrameFlags         flags,
  404.                                MetaFrameType          type,
  405.                                MetaFrameBorders      *borders)
  406. {
  407.   int buttons_height, title_height, draggable_borders;
  408.  
  409.   meta_frame_borders_clear (borders);
  410.  
  411.   /* For a full-screen window, we don't have any borders, visible or not. */
  412.   if (flags & META_FRAME_FULLSCREEN)
  413.     return;
  414.  
  415.   g_return_if_fail (layout != NULL);
  416.  
  417.   if (!layout->has_title)
  418.     text_height = 0;
  419.  
  420.   buttons_height = layout->button_height +
  421.     layout->button_border.top + layout->button_border.bottom;
  422.   title_height = text_height +
  423.     layout->title_vertical_pad +
  424.     layout->title_border.top + layout->title_border.bottom;
  425.  
  426.   borders->visible.top    = MAX (buttons_height, title_height);
  427.   borders->visible.left   = layout->left_width;
  428.   borders->visible.right  = layout->right_width;
  429.   borders->visible.bottom = layout->bottom_height;
  430.  
  431.   draggable_borders = meta_prefs_get_draggable_border_width ();
  432.  
  433.   if (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE)
  434.     {
  435.       borders->invisible.left   = MAX (0, draggable_borders - borders->visible.left);
  436.       borders->invisible.right  = MAX (0, draggable_borders - borders->visible.right);
  437.     }
  438.  
  439.   if (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE)
  440.     {
  441.       borders->invisible.bottom = MAX (0, draggable_borders - borders->visible.bottom);
  442.  
  443.       /* borders.visible.top is the height of the *title bar*. We can't do the same
  444.        * algorithm here, titlebars are expectedly much bigger. Just subtract a couple
  445.        * pixels to get a proper feel. */
  446.       if (type != META_FRAME_TYPE_ATTACHED)
  447.         borders->invisible.top    = MAX (0, draggable_borders - 2);
  448.     }
  449.  
  450.   borders->total.left   = borders->invisible.left   + borders->visible.left;
  451.   borders->total.right  = borders->invisible.right  + borders->visible.right;
  452.   borders->total.bottom = borders->invisible.bottom + borders->visible.bottom;
  453.   borders->total.top    = borders->invisible.top    + borders->visible.top;
  454. }
  455.  
  456. static MetaButtonType
  457. map_button_function_to_type (MetaButtonFunction  function)
  458. {
  459.   switch (function)
  460.     {
  461.     case META_BUTTON_FUNCTION_SHADE:
  462.       return META_BUTTON_TYPE_SHADE;
  463.     case META_BUTTON_FUNCTION_ABOVE:
  464.       return META_BUTTON_TYPE_ABOVE;
  465.     case META_BUTTON_FUNCTION_STICK:
  466.       return META_BUTTON_TYPE_STICK;
  467.     case META_BUTTON_FUNCTION_UNSHADE:
  468.       return META_BUTTON_TYPE_UNSHADE;
  469.     case META_BUTTON_FUNCTION_UNABOVE:
  470.       return META_BUTTON_TYPE_UNABOVE;
  471.     case META_BUTTON_FUNCTION_UNSTICK:
  472.       return META_BUTTON_TYPE_UNSTICK;
  473.     case META_BUTTON_FUNCTION_MENU:
  474.       return META_BUTTON_TYPE_MENU;
  475.     case META_BUTTON_FUNCTION_MINIMIZE:
  476.       return META_BUTTON_TYPE_MINIMIZE;
  477.     case META_BUTTON_FUNCTION_MAXIMIZE:
  478.       return META_BUTTON_TYPE_MAXIMIZE;
  479.     case META_BUTTON_FUNCTION_CLOSE:
  480.       return META_BUTTON_TYPE_CLOSE;
  481.     case META_BUTTON_FUNCTION_LAST:
  482.       return META_BUTTON_TYPE_LAST;
  483.     }
  484.  
  485.   return META_BUTTON_TYPE_LAST;
  486. }
  487.  
  488. static MetaButtonSpace*
  489. rect_for_function (MetaFrameGeometry *fgeom,
  490.                    MetaFrameFlags     flags,
  491.                    MetaButtonFunction function,
  492.                    MetaTheme         *theme)
  493. {
  494.  
  495.   /* Firstly, check version-specific things. */
  496.  
  497.   if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS))
  498.     {
  499.       switch (function)
  500.         {
  501.         case META_BUTTON_FUNCTION_SHADE:
  502.           if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED))
  503.             return &fgeom->shade_rect;
  504.           else
  505.             return NULL;
  506.         case META_BUTTON_FUNCTION_ABOVE:
  507.           if (!(flags & META_FRAME_ABOVE))
  508.             return &fgeom->above_rect;
  509.           else
  510.             return NULL;
  511.         case META_BUTTON_FUNCTION_STICK:
  512.           if (!(flags & META_FRAME_STUCK))
  513.             return &fgeom->stick_rect;
  514.           else
  515.             return NULL;
  516.         case META_BUTTON_FUNCTION_UNSHADE:
  517.           if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED))
  518.             return &fgeom->unshade_rect;
  519.           else
  520.             return NULL;
  521.         case META_BUTTON_FUNCTION_UNABOVE:
  522.           if (flags & META_FRAME_ABOVE)
  523.             return &fgeom->unabove_rect;
  524.           else
  525.             return NULL;
  526.         case META_BUTTON_FUNCTION_UNSTICK:
  527.           if (flags & META_FRAME_STUCK)
  528.             return &fgeom->unstick_rect;
  529.         default:
  530.           /* just go on to the next switch block */;
  531.         }
  532.     }
  533.  
  534.   /* now consider the buttons which exist in all versions */
  535.  
  536.   switch (function)
  537.     {
  538.     case META_BUTTON_FUNCTION_MENU:
  539.       if (flags & META_FRAME_ALLOWS_MENU)
  540.         return &fgeom->menu_rect;
  541.       else
  542.         return NULL;
  543.     case META_BUTTON_FUNCTION_MINIMIZE:
  544.       if (flags & META_FRAME_ALLOWS_MINIMIZE)
  545.         return &fgeom->min_rect;
  546.       else
  547.         return NULL;
  548.     case META_BUTTON_FUNCTION_MAXIMIZE:
  549.       if (flags & META_FRAME_ALLOWS_MAXIMIZE)
  550.         return &fgeom->max_rect;
  551.       else
  552.         return NULL;
  553.     case META_BUTTON_FUNCTION_CLOSE:
  554.       if (flags & META_FRAME_ALLOWS_DELETE)
  555.         return &fgeom->close_rect;
  556.       else
  557.         return NULL;
  558.     case META_BUTTON_FUNCTION_STICK:
  559.     case META_BUTTON_FUNCTION_SHADE:
  560.     case META_BUTTON_FUNCTION_ABOVE:
  561.     case META_BUTTON_FUNCTION_UNSTICK:
  562.     case META_BUTTON_FUNCTION_UNSHADE:
  563.     case META_BUTTON_FUNCTION_UNABOVE:
  564.       /* we are being asked for a >v1 button which hasn't been handled yet,
  565.        * so obviously we're not in a theme which supports that version.
  566.        * therefore, we don't show the button. return NULL and all will
  567.        * be well.
  568.        */
  569.       return NULL;
  570.      
  571.     case META_BUTTON_FUNCTION_LAST:
  572.       return NULL;
  573.     }
  574.  
  575.   return NULL;
  576. }
  577.  
  578. static gboolean
  579. strip_button (MetaButtonSpace *func_rects[MAX_BUTTONS_PER_CORNER],
  580.               GdkRectangle    *bg_rects[MAX_BUTTONS_PER_CORNER],
  581.               int             *n_rects,
  582.               MetaButtonSpace *to_strip)
  583. {
  584.   int i;
  585.  
  586.   i = 0;
  587.   while (i < *n_rects)
  588.     {
  589.       if (func_rects[i] == to_strip)
  590.         {
  591.           *n_rects -= 1;
  592.  
  593.           /* shift the other rects back in the array */
  594.           while (i < *n_rects)
  595.             {
  596.               func_rects[i] = func_rects[i+1];
  597.               bg_rects[i] = bg_rects[i+1];
  598.  
  599.               ++i;
  600.             }
  601.  
  602.           func_rects[i] = NULL;
  603.           bg_rects[i] = NULL;
  604.          
  605.           return TRUE;
  606.         }
  607.  
  608.       ++i;
  609.     }
  610.  
  611.   return FALSE; /* did not strip anything */
  612. }
  613.  
  614. void
  615. meta_frame_layout_calc_geometry (const MetaFrameLayout  *layout,
  616.                                  int                     text_height,
  617.                                  MetaFrameFlags          flags,
  618.                                  int                     client_width,
  619.                                  int                     client_height,
  620.                                  const MetaButtonLayout *button_layout,
  621.                                  MetaFrameType           type,
  622.                                  MetaFrameGeometry      *fgeom,
  623.                                  MetaTheme              *theme)
  624. {
  625.   int i, n_left, n_right, n_left_spacers, n_right_spacers;
  626.   int x;
  627.   int button_y;
  628.   int title_right_edge;
  629.   int width, height;
  630.   int button_width, button_height;
  631.   int min_size_for_rounding;
  632.  
  633.   /* the left/right rects in order; the max # of rects
  634.    * is the number of button functions
  635.    */
  636.   MetaButtonSpace *left_func_rects[MAX_BUTTONS_PER_CORNER];
  637.   MetaButtonSpace *right_func_rects[MAX_BUTTONS_PER_CORNER];
  638.   GdkRectangle *left_bg_rects[MAX_BUTTONS_PER_CORNER];
  639.   gboolean left_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
  640.   GdkRectangle *right_bg_rects[MAX_BUTTONS_PER_CORNER];
  641.   gboolean right_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
  642.  
  643.   MetaFrameBorders borders;
  644.  
  645.   meta_frame_layout_get_borders (layout, text_height,
  646.                                  flags, type,
  647.                                  &borders);
  648.  
  649.   fgeom->borders = borders;
  650.  
  651.   width = client_width + borders.total.left + borders.total.right;
  652.  
  653.   height = ((flags & META_FRAME_SHADED) ? 0: client_height) +
  654.     borders.total.top + borders.total.bottom;
  655.  
  656.   fgeom->width = width;
  657.   fgeom->height = height;
  658.  
  659.   fgeom->top_titlebar_edge = layout->title_border.top;
  660.   fgeom->bottom_titlebar_edge = layout->title_border.bottom;
  661.   fgeom->left_titlebar_edge = layout->left_titlebar_edge;
  662.   fgeom->right_titlebar_edge = layout->right_titlebar_edge;
  663.  
  664.   /* gcc warnings */
  665.   button_width = -1;
  666.   button_height = -1;
  667.  
  668.   switch (layout->button_sizing)
  669.     {
  670.     case META_BUTTON_SIZING_ASPECT:
  671.       button_height = borders.visible.top - layout->button_border.top - layout->button_border.bottom;
  672.       button_width = button_height / layout->button_aspect;
  673.       break;
  674.     case META_BUTTON_SIZING_FIXED:
  675.       button_width = layout->button_width;
  676.       button_height = layout->button_height;
  677.       break;
  678.     case META_BUTTON_SIZING_LAST:
  679.       g_assert_not_reached ();
  680.       break;
  681.     }
  682.  
  683.   /* FIXME all this code sort of pretends that duplicate buttons
  684.    * with the same function are allowed, but that breaks the
  685.    * code in frames.c, so isn't really allowed right now.
  686.    * Would need left_close_rect, right_close_rect, etc.
  687.    */
  688.  
  689.   /* Init all button rects to 0, lame hack */
  690.   memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0',
  691.           LENGTH_OF_BUTTON_RECTS);
  692.  
  693.   n_left = 0;
  694.   n_right = 0;
  695.   n_left_spacers = 0;
  696.   n_right_spacers = 0;
  697.  
  698.   if (!layout->hide_buttons)
  699.     {
  700.       /* Try to fill in rects */
  701.       for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
  702.         {
  703.           left_func_rects[n_left] = rect_for_function (fgeom, flags,
  704.                                                        button_layout->left_buttons[i],
  705.                                                        theme);
  706.           if (left_func_rects[n_left] != NULL)
  707.             {
  708.               left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i];
  709.               if (button_layout->left_buttons_has_spacer[i])
  710.                 ++n_left_spacers;
  711.  
  712.               ++n_left;
  713.             }
  714.         }
  715.      
  716.       for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
  717.         {
  718.           right_func_rects[n_right] = rect_for_function (fgeom, flags,
  719.                                                          button_layout->right_buttons[i],
  720.                                                          theme);
  721.           if (right_func_rects[n_right] != NULL)
  722.             {
  723.               right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i];
  724.               if (button_layout->right_buttons_has_spacer[i])
  725.                 ++n_right_spacers;
  726.  
  727.               ++n_right;
  728.             }
  729.         }
  730.     }
  731.  
  732.   for (i = 0; i < MAX_BUTTONS_PER_CORNER; i++)
  733.     {
  734.       left_bg_rects[i] = NULL;
  735.       right_bg_rects[i] = NULL;
  736.     }
  737.  
  738.   for (i = 0; i < n_left; i++)
  739.     {
  740.       if (n_left == 1)
  741.         left_bg_rects[i] = &fgeom->left_single_background;
  742.       else if (i == 0)
  743.         left_bg_rects[i] = &fgeom->left_left_background;
  744.       else if (i == (n_left - 1))
  745.         left_bg_rects[i] = &fgeom->left_right_background;
  746.       else
  747.         left_bg_rects[i] = &fgeom->left_middle_backgrounds[i - 1];
  748.     }
  749.  
  750.   for (i = 0; i < n_right; i++)
  751.     {
  752.       if (n_right == 1)
  753.         right_bg_rects[i] = &fgeom->right_single_background;
  754.       else if (i == (n_right - 1))
  755.         right_bg_rects[i] = &fgeom->right_right_background;
  756.       else if (i == 0)
  757.         right_bg_rects[i] = &fgeom->right_left_background;
  758.       else
  759.         right_bg_rects[i] = &fgeom->right_middle_backgrounds[i - 1];
  760.     }
  761.  
  762.   /* Be sure buttons fit */
  763.   while (n_left > 0 || n_right > 0)
  764.     {
  765.       int space_used_by_buttons;
  766.       int space_available;
  767.  
  768.       space_available = fgeom->width - layout->left_titlebar_edge - layout->right_titlebar_edge;
  769.      
  770.       space_used_by_buttons = 0;
  771.  
  772.       space_used_by_buttons += button_width * n_left;
  773.       space_used_by_buttons += (button_width * 0.75) * n_left_spacers;
  774.       space_used_by_buttons += layout->button_border.left * n_left;
  775.       space_used_by_buttons += layout->button_border.right * n_left;
  776.  
  777.       space_used_by_buttons += button_width * n_right;
  778.       space_used_by_buttons += (button_width * 0.75) * n_right_spacers;
  779.       space_used_by_buttons += layout->button_border.left * n_right;
  780.       space_used_by_buttons += layout->button_border.right * n_right;
  781.  
  782.       if (space_used_by_buttons <= space_available)
  783.         break; /* Everything fits, bail out */
  784.      
  785.       /* First try to remove separators */
  786.       if (n_left_spacers > 0)
  787.         {
  788.           left_buttons_has_spacer[--n_left_spacers] = FALSE;
  789.           continue;
  790.         }
  791.       else if (n_right_spacers > 0)
  792.         {
  793.           right_buttons_has_spacer[--n_right_spacers] = FALSE;
  794.           continue;
  795.         }
  796.  
  797.       /* Otherwise we need to shave out a button. Shave
  798.        * above, stick, shade, min, max, close, then menu (menu is most useful);
  799.        * prefer the default button locations.
  800.        */
  801.       if (strip_button (left_func_rects, left_bg_rects,
  802.                         &n_left, &fgeom->above_rect))
  803.         continue;
  804.       else if (strip_button (right_func_rects, right_bg_rects,
  805.                              &n_right, &fgeom->above_rect))
  806.         continue;
  807.       else if (strip_button (left_func_rects, left_bg_rects,
  808.                         &n_left, &fgeom->stick_rect))
  809.         continue;
  810.       else if (strip_button (right_func_rects, right_bg_rects,
  811.                              &n_right, &fgeom->stick_rect))
  812.         continue;
  813.       else if (strip_button (left_func_rects, left_bg_rects,
  814.                         &n_left, &fgeom->shade_rect))
  815.         continue;
  816.       else if (strip_button (right_func_rects, right_bg_rects,
  817.                              &n_right, &fgeom->shade_rect))
  818.         continue;
  819.       else if (strip_button (left_func_rects, left_bg_rects,
  820.                         &n_left, &fgeom->min_rect))
  821.         continue;
  822.       else if (strip_button (right_func_rects, right_bg_rects,
  823.                              &n_right, &fgeom->min_rect))
  824.         continue;
  825.       else if (strip_button (left_func_rects, left_bg_rects,
  826.                              &n_left, &fgeom->max_rect))
  827.         continue;
  828.       else if (strip_button (right_func_rects, right_bg_rects,
  829.                              &n_right, &fgeom->max_rect))
  830.         continue;
  831.       else if (strip_button (left_func_rects, left_bg_rects,
  832.                              &n_left, &fgeom->close_rect))
  833.         continue;
  834.       else if (strip_button (right_func_rects, right_bg_rects,
  835.                              &n_right, &fgeom->close_rect))
  836.         continue;
  837.       else if (strip_button (right_func_rects, right_bg_rects,
  838.                              &n_right, &fgeom->menu_rect))
  839.         continue;
  840.       else if (strip_button (left_func_rects, left_bg_rects,
  841.                              &n_left, &fgeom->menu_rect))
  842.         continue;
  843.       else
  844.         {
  845.           meta_bug ("Could not find a button to strip. n_left = %d n_right = %d\n",
  846.                     n_left, n_right);
  847.         }
  848.     }
  849.  
  850.   /* Save the button layout */
  851.   fgeom->button_layout = *button_layout;
  852.   fgeom->n_left_buttons = n_left;
  853.   fgeom->n_right_buttons = n_right;
  854.  
  855.   /* center buttons vertically */
  856.   button_y = (borders.visible.top -
  857.               (button_height + layout->button_border.top + layout->button_border.bottom)) / 2 + layout->button_border.top + borders.invisible.top;
  858.  
  859.   /* right edge of farthest-right button */
  860.   x = width - layout->right_titlebar_edge - borders.invisible.right;
  861.  
  862.   i = n_right - 1;
  863.   while (i >= 0)
  864.     {
  865.       MetaButtonSpace *rect;
  866.  
  867.       if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */
  868.         break;
  869.      
  870.       rect = right_func_rects[i];
  871.       rect->visible.x = x - layout->button_border.right - button_width;
  872.       if (right_buttons_has_spacer[i])
  873.         rect->visible.x -= (button_width * 0.75);
  874.  
  875.       rect->visible.y = button_y;
  876.       rect->visible.width = button_width;
  877.       rect->visible.height = button_height;
  878.  
  879.       if (flags & META_FRAME_MAXIMIZED ||
  880.           flags & META_FRAME_TILED_LEFT ||
  881.           flags & META_FRAME_TILED_RIGHT)
  882.         {
  883.           rect->clickable.x = rect->visible.x;
  884.           rect->clickable.y = 0;
  885.           rect->clickable.width = rect->visible.width;
  886.           rect->clickable.height = button_height + button_y;
  887.  
  888.           if (i == n_right - 1)
  889.             rect->clickable.width += layout->right_titlebar_edge + layout->right_width + layout->button_border.right;
  890.  
  891.         }
  892.       else
  893.         g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
  894.  
  895.       *(right_bg_rects[i]) = rect->visible;
  896.      
  897.       x = rect->visible.x - layout->button_border.left;
  898.      
  899.       --i;
  900.     }
  901.  
  902.   /* save right edge of titlebar for later use */
  903.   title_right_edge = x - layout->title_border.right;
  904.  
  905.   /* Now x changes to be position from the left and we go through
  906.    * the left-side buttons
  907.    */
  908.   x = layout->left_titlebar_edge + borders.invisible.left;
  909.   for (i = 0; i < n_left; i++)
  910.     {
  911.       MetaButtonSpace *rect;
  912.  
  913.       rect = left_func_rects[i];
  914.      
  915.       rect->visible.x = x + layout->button_border.left;
  916.       rect->visible.y = button_y;
  917.       rect->visible.width = button_width;
  918.       rect->visible.height = button_height;
  919.  
  920.       if (flags & META_FRAME_MAXIMIZED)
  921.         {
  922.           if (i==0)
  923.             {
  924.               rect->clickable.x = 0;
  925.               rect->clickable.width = button_width + x;
  926.             }
  927.           else
  928.             {
  929.               rect->clickable.x = rect->visible.x;
  930.               rect->clickable.width = button_width;
  931.             }
  932.  
  933.             rect->clickable.y = 0;
  934.             rect->clickable.height = button_height + button_y;
  935.           }
  936.         else
  937.           g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
  938.  
  939.  
  940.       x = rect->visible.x + rect->visible.width + layout->button_border.right;
  941.       if (left_buttons_has_spacer[i])
  942.         x += (button_width * 0.75);
  943.  
  944.       *(left_bg_rects[i]) = rect->visible;
  945.     }
  946.  
  947.   /* We always fill as much vertical space as possible with title rect,
  948.    * rather than centering it like the buttons
  949.    */
  950.   fgeom->title_rect.x = x + layout->title_border.left;
  951.   fgeom->title_rect.y = layout->title_border.top + borders.invisible.top;
  952.   fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x;
  953.   fgeom->title_rect.height = borders.visible.top - layout->title_border.top - layout->title_border.bottom;
  954.  
  955.   /* Nuke title if it won't fit */
  956.   if (fgeom->title_rect.width < 0 ||
  957.       fgeom->title_rect.height < 0)
  958.     {
  959.       fgeom->title_rect.width = 0;
  960.       fgeom->title_rect.height = 0;
  961.     }
  962.  
  963.   if (flags & META_FRAME_SHADED)
  964.     min_size_for_rounding = 0;
  965.   else
  966.     min_size_for_rounding = 5;
  967.  
  968.   fgeom->top_left_corner_rounded_radius = 0;
  969.   fgeom->top_right_corner_rounded_radius = 0;
  970.   fgeom->bottom_left_corner_rounded_radius = 0;
  971.   fgeom->bottom_right_corner_rounded_radius = 0;
  972.  
  973.   if (borders.visible.top + borders.visible.left >= min_size_for_rounding)
  974.     fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius;
  975.   if (borders.visible.top + borders.visible.right >= min_size_for_rounding)
  976.     fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius;
  977.  
  978.   if (borders.visible.bottom + borders.visible.left >= min_size_for_rounding)
  979.     fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius;
  980.   if (borders.visible.bottom + borders.visible.right >= min_size_for_rounding)
  981.     fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius;
  982. }
  983.  
  984. /**
  985.  * meta_gradient_spec_new: (skip)
  986.  *
  987.  */
  988. MetaGradientSpec*
  989. meta_gradient_spec_new (MetaGradientType type)
  990. {
  991.   MetaGradientSpec *spec;
  992.  
  993.   spec = g_new (MetaGradientSpec, 1);
  994.  
  995.   spec->type = type;
  996.   spec->color_specs = NULL;
  997.  
  998.   return spec;
  999. }
  1000.  
  1001. static void
  1002. free_color_spec (gpointer spec, gpointer user_data)
  1003. {
  1004.   meta_color_spec_free (spec);
  1005. }
  1006.  
  1007. void
  1008. meta_gradient_spec_free (MetaGradientSpec *spec)
  1009. {
  1010.   g_return_if_fail (spec != NULL);
  1011.  
  1012.   g_slist_foreach (spec->color_specs, free_color_spec, NULL);
  1013.   g_slist_free (spec->color_specs);
  1014.  
  1015.   DEBUG_FILL_STRUCT (spec);
  1016.   g_free (spec);
  1017. }
  1018.  
  1019. GdkPixbuf*
  1020. meta_gradient_spec_render (const MetaGradientSpec *spec,
  1021.                            GtkStyleContext        *style,
  1022.                            int                     width,
  1023.                            int                     height)
  1024. {
  1025.   int n_colors;
  1026.   GdkRGBA *colors;
  1027.   GSList *tmp;
  1028.   int i;
  1029.   GdkPixbuf *pixbuf;
  1030.  
  1031.   n_colors = g_slist_length (spec->color_specs);
  1032.  
  1033.   if (n_colors == 0)
  1034.     return NULL;
  1035.  
  1036.   colors = g_new (GdkRGBA, n_colors);
  1037.  
  1038.   i = 0;
  1039.   tmp = spec->color_specs;
  1040.   while (tmp != NULL)
  1041.     {
  1042.       meta_color_spec_render (tmp->data, style, &colors[i]);
  1043.  
  1044.       tmp = tmp->next;
  1045.       ++i;
  1046.     }
  1047.  
  1048.   pixbuf = meta_gradient_create_multi (width, height,
  1049.                                        colors, n_colors,
  1050.                                        spec->type);
  1051.  
  1052.   g_free (colors);
  1053.  
  1054.   return pixbuf;
  1055. }
  1056.  
  1057. gboolean
  1058. meta_gradient_spec_validate (MetaGradientSpec *spec,
  1059.                              GError          **error)
  1060. {
  1061.   g_return_val_if_fail (spec != NULL, FALSE);
  1062.  
  1063.   if (g_slist_length (spec->color_specs) < 2)
  1064.     {
  1065.       g_set_error (error, META_THEME_ERROR,
  1066.                    META_THEME_ERROR_FAILED,
  1067.                    _("Gradients should have at least two colors"));
  1068.       return FALSE;
  1069.     }
  1070.  
  1071.   return TRUE;
  1072. }
  1073.  
  1074. /**
  1075.  * meta_alpha_gradient_spec_new: (skip)
  1076.  *
  1077.  */
  1078. MetaAlphaGradientSpec*
  1079. meta_alpha_gradient_spec_new (MetaGradientType       type,
  1080.                               int                    n_alphas)
  1081. {
  1082.   MetaAlphaGradientSpec *spec;
  1083.  
  1084.   g_return_val_if_fail (n_alphas > 0, NULL);
  1085.  
  1086.   spec = g_new0 (MetaAlphaGradientSpec, 1);
  1087.  
  1088.   spec->type = type;
  1089.   spec->alphas = g_new0 (unsigned char, n_alphas);
  1090.   spec->n_alphas = n_alphas;
  1091.  
  1092.   return spec;
  1093. }
  1094.  
  1095. void
  1096. meta_alpha_gradient_spec_free (MetaAlphaGradientSpec *spec)
  1097. {
  1098.   g_return_if_fail (spec != NULL);
  1099.  
  1100.   g_free (spec->alphas);
  1101.   g_free (spec);
  1102. }
  1103.  
  1104. /**
  1105.  * meta_color_spec_new: (skip)
  1106.  *
  1107.  */
  1108. MetaColorSpec*
  1109. meta_color_spec_new (MetaColorSpecType type)
  1110. {
  1111.   MetaColorSpec *spec;
  1112.   MetaColorSpec dummy;
  1113.   int size;
  1114.  
  1115.   size = G_STRUCT_OFFSET (MetaColorSpec, data);
  1116.  
  1117.   switch (type)
  1118.     {
  1119.     case META_COLOR_SPEC_BASIC:
  1120.       size += sizeof (dummy.data.basic);
  1121.       break;
  1122.  
  1123.     case META_COLOR_SPEC_GTK:
  1124.       size += sizeof (dummy.data.gtk);
  1125.       break;
  1126.  
  1127.     case META_COLOR_SPEC_GTK_CUSTOM:
  1128.       size += sizeof (dummy.data.gtkcustom);
  1129.       break;
  1130.  
  1131.     case META_COLOR_SPEC_BLEND:
  1132.       size += sizeof (dummy.data.blend);
  1133.       break;
  1134.  
  1135.     case META_COLOR_SPEC_SHADE:
  1136.       size += sizeof (dummy.data.shade);
  1137.       break;
  1138.     }
  1139.  
  1140.   spec = g_malloc0 (size);
  1141.  
  1142.   spec->type = type;
  1143.  
  1144.   return spec;
  1145. }
  1146.  
  1147. void
  1148. meta_color_spec_free (MetaColorSpec *spec)
  1149. {
  1150.   g_return_if_fail (spec != NULL);
  1151.  
  1152.   switch (spec->type)
  1153.     {
  1154.     case META_COLOR_SPEC_BASIC:
  1155.       DEBUG_FILL_STRUCT (&spec->data.basic);
  1156.       break;
  1157.  
  1158.     case META_COLOR_SPEC_GTK:
  1159.       DEBUG_FILL_STRUCT (&spec->data.gtk);
  1160.       break;
  1161.  
  1162.     case META_COLOR_SPEC_GTK_CUSTOM:
  1163.       if (spec->data.gtkcustom.color_name)
  1164.         g_free (spec->data.gtkcustom.color_name);
  1165.       if (spec->data.gtkcustom.fallback)
  1166.         meta_color_spec_free (spec->data.gtkcustom.fallback);
  1167.       DEBUG_FILL_STRUCT (&spec->data.gtkcustom);
  1168.       break;
  1169.  
  1170.     case META_COLOR_SPEC_BLEND:
  1171.       if (spec->data.blend.foreground)
  1172.         meta_color_spec_free (spec->data.blend.foreground);
  1173.       if (spec->data.blend.background)
  1174.         meta_color_spec_free (spec->data.blend.background);
  1175.       DEBUG_FILL_STRUCT (&spec->data.blend);
  1176.       break;
  1177.  
  1178.     case META_COLOR_SPEC_SHADE:
  1179.       if (spec->data.shade.base)
  1180.         meta_color_spec_free (spec->data.shade.base);
  1181.       DEBUG_FILL_STRUCT (&spec->data.shade);
  1182.       break;
  1183.     }
  1184.  
  1185.   g_free (spec);
  1186. }
  1187.  
  1188. /**
  1189.  * meta_color_spec_new_from_string: (skip)
  1190.  *
  1191.  */
  1192. MetaColorSpec*
  1193. meta_color_spec_new_from_string (const char *str,
  1194.                                  GError    **err)
  1195. {
  1196.   MetaColorSpec *spec;
  1197.  
  1198.   spec = NULL;
  1199.  
  1200.   if (str[0] == 'g' && str[1] == 't' && str[2] == 'k' && str[3] == ':' &&
  1201.       str[4] == 'c' && str[5] == 'u' && str[6] == 's' && str[7] == 't' &&
  1202.       str[8] == 'o' && str[9] == 'm')
  1203.     {
  1204.       const char *color_name_start, *fallback_str_start, *end;
  1205.       char *color_name;
  1206.       MetaColorSpec *fallback = NULL;
  1207.       static gboolean debug, debug_set = FALSE;
  1208.  
  1209.       if (!debug_set)
  1210.         {
  1211.           debug = g_getenv ("MUTTER_DISABLE_FALLBACK_COLOR") != NULL;
  1212.           debug_set = TRUE;
  1213.         }
  1214.  
  1215.       if (str[10] != '(')
  1216.         {
  1217.           g_set_error (err, META_THEME_ERROR,
  1218.                        META_THEME_ERROR_FAILED,
  1219.                        _("GTK custom color specification must have color name and fallback in parentheses, e.g. gtk:custom(foo,bar); could not parse \"%s\""),
  1220.                        str);
  1221.           return NULL;
  1222.         }
  1223.  
  1224.       color_name_start = str + 11;
  1225.  
  1226.       fallback_str_start = color_name_start;
  1227.       while (*fallback_str_start && *fallback_str_start != ',')
  1228.         {
  1229.           if (!(g_ascii_isalnum (*fallback_str_start)
  1230.                 || *fallback_str_start == '-'
  1231.                 || *fallback_str_start == '_'))
  1232.             {
  1233.               g_set_error (err, META_THEME_ERROR,
  1234.                            META_THEME_ERROR_FAILED,
  1235.                            _("Invalid character '%c' in color_name parameter of gtk:custom, only A-Za-z0-9-_ are valid"),
  1236.                            *fallback_str_start);
  1237.               return NULL;
  1238.             }
  1239.           fallback_str_start++;
  1240.         }
  1241.       fallback_str_start++;
  1242.  
  1243.       end = strrchr (str, ')');
  1244.  
  1245.       if (color_name_start == NULL || fallback_str_start == NULL || end == NULL)
  1246.         {
  1247.           g_set_error (err, META_THEME_ERROR,
  1248.                        META_THEME_ERROR_FAILED,
  1249.                        _("Gtk:custom format is \"gtk:custom(color_name,fallback)\", \"%s\" does not fit the format"),
  1250.                        str);
  1251.           return NULL;
  1252.         }
  1253.  
  1254.       if (!debug)
  1255.         {
  1256.           char *fallback_str;
  1257.           fallback_str = g_strndup (fallback_str_start,
  1258.                                     end - fallback_str_start);
  1259.           fallback = meta_color_spec_new_from_string (fallback_str, err);
  1260.           g_free (fallback_str);
  1261.         }
  1262.       else
  1263.         {
  1264.           fallback = meta_color_spec_new_from_string ("pink", err);
  1265.         }
  1266.  
  1267.       if (fallback == NULL)
  1268.         return NULL;
  1269.  
  1270.       color_name = g_strndup (color_name_start,
  1271.                               fallback_str_start - color_name_start - 1);
  1272.  
  1273.       spec = meta_color_spec_new (META_COLOR_SPEC_GTK_CUSTOM);
  1274.       spec->data.gtkcustom.color_name = color_name;
  1275.       spec->data.gtkcustom.fallback = fallback;
  1276.     }
  1277.   else if (str[0] == 'g' && str[1] == 't' && str[2] == 'k' && str[3] == ':')
  1278.     {
  1279.       /* GTK color */
  1280.       const char *bracket;
  1281.       const char *end_bracket;
  1282.       char *tmp;
  1283.       GtkStateFlags state;
  1284.       MetaGtkColorComponent component;
  1285.      
  1286.       bracket = str;
  1287.       while (*bracket && *bracket != '[')
  1288.         ++bracket;
  1289.  
  1290.       if (*bracket == '\0')
  1291.         {
  1292.           g_set_error (err, META_THEME_ERROR,
  1293.                        META_THEME_ERROR_FAILED,
  1294.                        _("GTK color specification must have the state in brackets, e.g. gtk:fg[NORMAL] where NORMAL is the state; could not parse \"%s\""),
  1295.                        str);
  1296.           return NULL;
  1297.         }
  1298.  
  1299.       end_bracket = bracket;
  1300.       ++end_bracket;
  1301.       while (*end_bracket && *end_bracket != ']')
  1302.         ++end_bracket;
  1303.      
  1304.       if (*end_bracket == '\0')
  1305.         {
  1306.           g_set_error (err, META_THEME_ERROR,
  1307.                        META_THEME_ERROR_FAILED,
  1308.                        _("GTK color specification must have a close bracket after the state, e.g. gtk:fg[NORMAL] where NORMAL is the state; could not parse \"%s\""),
  1309.                        str);
  1310.           return NULL;
  1311.         }
  1312.  
  1313.       tmp = g_strndup (bracket + 1, end_bracket - bracket - 1);
  1314.       state = meta_gtk_state_from_string (tmp);
  1315.       if (((int) state) == -1)
  1316.         {
  1317.           g_set_error (err, META_THEME_ERROR,
  1318.                        META_THEME_ERROR_FAILED,
  1319.                        _("Did not understand state \"%s\" in color specification"),
  1320.                        tmp);
  1321.           g_free (tmp);
  1322.           return NULL;
  1323.         }
  1324.       g_free (tmp);
  1325.      
  1326.       tmp = g_strndup (str + 4, bracket - str - 4);
  1327.       component = meta_color_component_from_string (tmp);
  1328.       if (component == META_GTK_COLOR_LAST)
  1329.         {
  1330.           g_set_error (err, META_THEME_ERROR,
  1331.                        META_THEME_ERROR_FAILED,
  1332.                        _("Did not understand color component \"%s\" in color specification"),
  1333.                        tmp);
  1334.           g_free (tmp);
  1335.           return NULL;
  1336.         }
  1337.       g_free (tmp);
  1338.  
  1339.       spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
  1340.       spec->data.gtk.state = state;
  1341.       spec->data.gtk.component = component;
  1342.       g_assert (spec->data.gtk.component < META_GTK_COLOR_LAST);
  1343.     }
  1344.   else if (str[0] == 'b' && str[1] == 'l' && str[2] == 'e' && str[3] == 'n' &&
  1345.            str[4] == 'd' && str[5] == '/')
  1346.     {
  1347.       /* blend */
  1348.       char **split;
  1349.       double alpha;
  1350.       char *end;
  1351.       MetaColorSpec *fg;
  1352.       MetaColorSpec *bg;
  1353.      
  1354.       split = g_strsplit (str, "/", 4);
  1355.      
  1356.       if (split[0] == NULL || split[1] == NULL ||
  1357.           split[2] == NULL || split[3] == NULL)
  1358.         {
  1359.           g_set_error (err, META_THEME_ERROR,
  1360.                        META_THEME_ERROR_FAILED,
  1361.                        _("Blend format is \"blend/bg_color/fg_color/alpha\", \"%s\" does not fit the format"),
  1362.                        str);
  1363.           g_strfreev (split);
  1364.           return NULL;
  1365.         }
  1366.  
  1367.       alpha = g_ascii_strtod (split[3], &end);
  1368.       if (end == split[3])
  1369.         {
  1370.           g_set_error (err, META_THEME_ERROR,
  1371.                        META_THEME_ERROR_FAILED,
  1372.                        _("Could not parse alpha value \"%s\" in blended color"),
  1373.                        split[3]);
  1374.           g_strfreev (split);
  1375.           return NULL;
  1376.         }
  1377.  
  1378.       if (alpha < (0.0 - 1e6) || alpha > (1.0 + 1e6))
  1379.         {
  1380.           g_set_error (err, META_THEME_ERROR,
  1381.                        META_THEME_ERROR_FAILED,
  1382.                        _("Alpha value \"%s\" in blended color is not between 0.0 and 1.0"),
  1383.                        split[3]);
  1384.           g_strfreev (split);
  1385.           return NULL;
  1386.         }
  1387.      
  1388.       fg = NULL;
  1389.       bg = NULL;
  1390.  
  1391.       bg = meta_color_spec_new_from_string (split[1], err);
  1392.       if (bg == NULL)
  1393.         {
  1394.           g_strfreev (split);
  1395.           return NULL;
  1396.         }
  1397.  
  1398.       fg = meta_color_spec_new_from_string (split[2], err);
  1399.       if (fg == NULL)
  1400.         {
  1401.           meta_color_spec_free (bg);
  1402.           g_strfreev (split);
  1403.           return NULL;
  1404.         }
  1405.  
  1406.       g_strfreev (split);
  1407.      
  1408.       spec = meta_color_spec_new (META_COLOR_SPEC_BLEND);
  1409.       spec->data.blend.alpha = alpha;
  1410.       spec->data.blend.background = bg;
  1411.       spec->data.blend.foreground = fg;
  1412.     }
  1413.   else if (str[0] == 's' && str[1] == 'h' && str[2] == 'a' && str[3] == 'd' &&
  1414.            str[4] == 'e' && str[5] == '/')
  1415.     {
  1416.       /* shade */
  1417.       char **split;
  1418.       double factor;
  1419.       char *end;
  1420.       MetaColorSpec *base;
  1421.      
  1422.       split = g_strsplit (str, "/", 3);
  1423.      
  1424.       if (split[0] == NULL || split[1] == NULL ||
  1425.           split[2] == NULL)
  1426.         {
  1427.           g_set_error (err, META_THEME_ERROR,
  1428.                        META_THEME_ERROR_FAILED,
  1429.                        _("Shade format is \"shade/base_color/factor\", \"%s\" does not fit the format"),
  1430.                        str);
  1431.           g_strfreev (split);
  1432.           return NULL;
  1433.         }
  1434.  
  1435.       factor = g_ascii_strtod (split[2], &end);
  1436.       if (end == split[2])
  1437.         {
  1438.           g_set_error (err, META_THEME_ERROR,
  1439.                        META_THEME_ERROR_FAILED,
  1440.                        _("Could not parse shade factor \"%s\" in shaded color"),
  1441.                        split[2]);
  1442.           g_strfreev (split);
  1443.           return NULL;
  1444.         }
  1445.  
  1446.       if (factor < (0.0 - 1e6))
  1447.         {
  1448.           g_set_error (err, META_THEME_ERROR,
  1449.                        META_THEME_ERROR_FAILED,
  1450.                        _("Shade factor \"%s\" in shaded color is negative"),
  1451.                        split[2]);
  1452.           g_strfreev (split);
  1453.           return NULL;
  1454.         }
  1455.      
  1456.       base = NULL;
  1457.  
  1458.       base = meta_color_spec_new_from_string (split[1], err);
  1459.       if (base == NULL)
  1460.         {
  1461.           g_strfreev (split);
  1462.           return NULL;
  1463.         }
  1464.  
  1465.       g_strfreev (split);
  1466.      
  1467.       spec = meta_color_spec_new (META_COLOR_SPEC_SHADE);
  1468.       spec->data.shade.factor = factor;
  1469.       spec->data.shade.base = base;
  1470.     }
  1471.   else
  1472.     {
  1473.       spec = meta_color_spec_new (META_COLOR_SPEC_BASIC);
  1474.      
  1475.       if (!gdk_rgba_parse (&spec->data.basic.color, str))
  1476.         {
  1477.           g_set_error (err, META_THEME_ERROR,
  1478.                        META_THEME_ERROR_FAILED,
  1479.                        _("Could not parse color \"%s\""),
  1480.                        str);
  1481.           meta_color_spec_free (spec);
  1482.           return NULL;
  1483.         }
  1484.     }
  1485.  
  1486.   g_assert (spec);
  1487.  
  1488.   return spec;
  1489. }
  1490.  
  1491. /**
  1492.  * meta_color_spec_new_gtk: (skip)
  1493.  *
  1494.  */
  1495. MetaColorSpec*
  1496. meta_color_spec_new_gtk (MetaGtkColorComponent component,
  1497.                          GtkStateFlags         state)
  1498. {
  1499.   MetaColorSpec *spec;
  1500.  
  1501.   spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
  1502.  
  1503.   spec->data.gtk.component = component;
  1504.   spec->data.gtk.state = state;
  1505.  
  1506.   return spec;
  1507. }
  1508.  
  1509. /* Based on set_color() in gtkstyle.c */
  1510. #define LIGHTNESS_MULT 1.3
  1511. #define DARKNESS_MULT  0.7
  1512. void
  1513. meta_gtk_style_get_light_color (GtkStyleContext *style,
  1514.                                 GtkStateFlags    state,
  1515.                                 GdkRGBA         *color)
  1516. {
  1517.   gtk_style_context_get_background_color (style, state, color);
  1518.   gtk_style_shade (color, color, LIGHTNESS_MULT);
  1519. }
  1520.  
  1521. void
  1522. meta_gtk_style_get_dark_color (GtkStyleContext *style,
  1523.                                GtkStateFlags    state,
  1524.                                GdkRGBA         *color)
  1525. {
  1526.   gtk_style_context_get_background_color (style, state, color);
  1527.   gtk_style_shade (color, color, DARKNESS_MULT);
  1528. }
  1529.  
  1530. static void
  1531. meta_set_color_from_style (GdkRGBA               *color,
  1532.                            GtkStyleContext       *context,
  1533.                            GtkStateFlags          state,
  1534.                            MetaGtkColorComponent  component)
  1535. {
  1536.   GdkRGBA other;
  1537.  
  1538.   switch (component)
  1539.     {
  1540.     case META_GTK_COLOR_BG:
  1541.     case META_GTK_COLOR_BASE:
  1542.       gtk_style_context_get_background_color (context, state, color);
  1543.       break;
  1544.     case META_GTK_COLOR_FG:
  1545.     case META_GTK_COLOR_TEXT:
  1546.       gtk_style_context_get_color (context, state, color);
  1547.       break;
  1548.     case META_GTK_COLOR_TEXT_AA:
  1549.       gtk_style_context_get_color (context, state, color);
  1550.       meta_set_color_from_style (&other, context, state, META_GTK_COLOR_BASE);
  1551.  
  1552.       color->red = (color->red + other.red) / 2;
  1553.       color->green = (color->green + other.green) / 2;
  1554.       color->blue = (color->blue + other.blue) / 2;
  1555.       break;
  1556.     case META_GTK_COLOR_MID:
  1557.       meta_gtk_style_get_light_color (context, state, color);
  1558.       meta_gtk_style_get_dark_color (context, state, &other);
  1559.  
  1560.       color->red = (color->red + other.red) / 2;
  1561.       color->green = (color->green + other.green) / 2;
  1562.       color->blue = (color->blue + other.blue) / 2;
  1563.       break;
  1564.     case META_GTK_COLOR_LIGHT:
  1565.       meta_gtk_style_get_light_color (context, state, color);
  1566.       break;
  1567.     case META_GTK_COLOR_DARK:
  1568.       meta_gtk_style_get_dark_color (context, state, color);
  1569.       break;
  1570.     case META_GTK_COLOR_LAST:
  1571.       g_assert_not_reached ();
  1572.       break;
  1573.     }
  1574. }
  1575.  
  1576. static void
  1577. meta_set_custom_color_from_style (GdkRGBA         *color,
  1578.                                   GtkStyleContext *context,
  1579.                                   char            *color_name,
  1580.                                   MetaColorSpec   *fallback)
  1581. {
  1582.   if (!gtk_style_context_lookup_color (context, color_name, color))
  1583.     meta_color_spec_render (fallback, context, color);
  1584. }
  1585.  
  1586. void
  1587. meta_color_spec_render (MetaColorSpec   *spec,
  1588.                         GtkStyleContext *context,
  1589.                         GdkRGBA         *color)
  1590. {
  1591.   g_return_if_fail (spec != NULL);
  1592.   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
  1593.  
  1594.   switch (spec->type)
  1595.     {
  1596.     case META_COLOR_SPEC_BASIC:
  1597.       *color = spec->data.basic.color;
  1598.       break;
  1599.  
  1600.     case META_COLOR_SPEC_GTK:
  1601.       meta_set_color_from_style (color,
  1602.                                  context,
  1603.                                  spec->data.gtk.state,
  1604.                                  spec->data.gtk.component);
  1605.       break;
  1606.  
  1607.     case META_COLOR_SPEC_GTK_CUSTOM:
  1608.       meta_set_custom_color_from_style (color,
  1609.                                         context,
  1610.                                         spec->data.gtkcustom.color_name,
  1611.                                         spec->data.gtkcustom.fallback);
  1612.       break;
  1613.  
  1614.     case META_COLOR_SPEC_BLEND:
  1615.       {
  1616.         GdkRGBA bg, fg;
  1617.  
  1618.         meta_color_spec_render (spec->data.blend.background, context, &bg);
  1619.         meta_color_spec_render (spec->data.blend.foreground, context, &fg);
  1620.  
  1621.         color_composite (&bg, &fg, spec->data.blend.alpha,
  1622.                          &spec->data.blend.color);
  1623.  
  1624.         *color = spec->data.blend.color;
  1625.       }
  1626.       break;
  1627.  
  1628.     case META_COLOR_SPEC_SHADE:
  1629.       {
  1630.         meta_color_spec_render (spec->data.shade.base, context,
  1631.                                 &spec->data.shade.color);
  1632.            
  1633.         gtk_style_shade (&spec->data.shade.color,
  1634.                          &spec->data.shade.color, spec->data.shade.factor);
  1635.  
  1636.         *color = spec->data.shade.color;
  1637.       }
  1638.       break;
  1639.     }
  1640. }
  1641.  
  1642. /**
  1643.  * Represents an operation as a string.
  1644.  *
  1645.  * \param type  an operation, such as addition
  1646.  * \return  a string, such as "+"
  1647.  */
  1648. static const char*
  1649. op_name (PosOperatorType type)
  1650. {
  1651.   switch (type)
  1652.     {
  1653.     case POS_OP_ADD:
  1654.       return "+";
  1655.     case POS_OP_SUBTRACT:
  1656.       return "-";
  1657.     case POS_OP_MULTIPLY:
  1658.       return "*";
  1659.     case POS_OP_DIVIDE:
  1660.       return "/";
  1661.     case POS_OP_MOD:
  1662.       return "%";
  1663.     case POS_OP_MAX:
  1664.       return "`max`";
  1665.     case POS_OP_MIN:
  1666.       return "`min`";
  1667.     case POS_OP_NONE:
  1668.       break;
  1669.     }
  1670.  
  1671.   return "<unknown>";
  1672. }
  1673.  
  1674. /**
  1675.  * Parses a string and returns an operation.
  1676.  *
  1677.  * \param p  a pointer into a string representing an operation; part of an
  1678.  *           expression somewhere, so not null-terminated
  1679.  * \param len  set to the length of the string found. Set to 0 if none is.
  1680.  * \return  the operation found. If none was, returns POS_OP_NONE.
  1681.  */
  1682. static PosOperatorType
  1683. op_from_string (const char *p,
  1684.                 int        *len)
  1685. {
  1686.   *len = 0;
  1687.  
  1688.   switch (*p)
  1689.     {
  1690.     case '+':
  1691.       *len = 1;
  1692.       return POS_OP_ADD;
  1693.     case '-':
  1694.       *len = 1;
  1695.       return POS_OP_SUBTRACT;
  1696.     case '*':
  1697.       *len = 1;
  1698.       return POS_OP_MULTIPLY;
  1699.     case '/':
  1700.       *len = 1;
  1701.       return POS_OP_DIVIDE;
  1702.     case '%':
  1703.       *len = 1;
  1704.       return POS_OP_MOD;
  1705.  
  1706.     case '`':
  1707.       if (p[0] == '`' &&
  1708.           p[1] == 'm' &&
  1709.           p[2] == 'a' &&
  1710.           p[3] == 'x' &&
  1711.           p[4] == '`')
  1712.         {
  1713.           *len = 5;
  1714.           return POS_OP_MAX;
  1715.         }
  1716.       else if (p[0] == '`' &&
  1717.                p[1] == 'm' &&
  1718.                p[2] == 'i' &&
  1719.                p[3] == 'n' &&
  1720.                p[4] == '`')
  1721.         {
  1722.           *len = 5;
  1723.           return POS_OP_MIN;
  1724.         }
  1725.     }
  1726.  
  1727.   return POS_OP_NONE;
  1728. }
  1729.  
  1730. /**
  1731.  * Frees an array of tokens. All the tokens and their associated memory
  1732.  * will be freed.
  1733.  *
  1734.  * \param tokens  an array of tokens to be freed
  1735.  * \param n_tokens  how many tokens are in the array.
  1736.  */
  1737. static void
  1738. free_tokens (PosToken *tokens,
  1739.              int       n_tokens)
  1740. {
  1741.   int i;
  1742.  
  1743.   /* n_tokens can be 0 since tokens may have been allocated more than
  1744.    * it was initialized
  1745.    */
  1746.  
  1747.   for (i = 0; i < n_tokens; i++)
  1748.     if (tokens[i].type == POS_TOKEN_VARIABLE)
  1749.       g_free (tokens[i].d.v.name);
  1750.  
  1751.   g_free (tokens);
  1752. }
  1753.  
  1754. /**
  1755.  * Tokenises a number in an expression.
  1756.  *
  1757.  * \param p  a pointer into a string representing an operation; part of an
  1758.  *           expression somewhere, so not null-terminated
  1759.  * \param end_return  set to a pointer to the end of the number found; but
  1760.  *                    not updated if no number was found at all
  1761.  * \param next  set to either an integer or a float token
  1762.  * \param[out] err  set to the problem if there was a problem
  1763.  * \return TRUE if a valid number was found, FALSE otherwise (and "err" will
  1764.  *         have been set)
  1765.  *
  1766.  * \bug The "while (*start)..." part: what's wrong with strchr-ish things?
  1767.  * \bug The name is wrong: it doesn't parse anything.
  1768.  * \ingroup tokenizer
  1769.  */
  1770. static gboolean
  1771. parse_number (const char  *p,
  1772.               const char **end_return,
  1773.               PosToken    *next,
  1774.               GError     **err)
  1775. {
  1776.   const char *start = p;
  1777.   char *end;
  1778.   gboolean is_float;
  1779.   char *num_str;
  1780.  
  1781.   while (*p && (*p == '.' || g_ascii_isdigit (*p)))
  1782.     ++p;
  1783.  
  1784.   if (p == start)
  1785.     {
  1786.       char buf[7] = { '\0' };
  1787.       buf[g_unichar_to_utf8 (g_utf8_get_char (p), buf)] = '\0';
  1788.       g_set_error (err, META_THEME_ERROR,
  1789.                    META_THEME_ERROR_BAD_CHARACTER,
  1790.                    _("Coordinate expression contains character '%s' which is not allowed"),
  1791.                    buf);
  1792.       return FALSE;
  1793.     }
  1794.  
  1795.   *end_return = p;
  1796.  
  1797.   /* we need this to exclude floats like "1e6" */
  1798.   num_str = g_strndup (start, p - start);
  1799.   start = num_str;
  1800.   is_float = FALSE;
  1801.   while (*start)
  1802.     {
  1803.       if (*start == '.')
  1804.         is_float = TRUE;
  1805.       ++start;
  1806.     }
  1807.  
  1808.   if (is_float)
  1809.     {
  1810.       next->type = POS_TOKEN_DOUBLE;
  1811.       next->d.d.val = g_ascii_strtod (num_str, &end);
  1812.  
  1813.       if (end == num_str)
  1814.         {
  1815.           g_set_error (err, META_THEME_ERROR,
  1816.                        META_THEME_ERROR_FAILED,
  1817.                        _("Coordinate expression contains floating point number '%s' which could not be parsed"),
  1818.                        num_str);
  1819.           g_free (num_str);
  1820.           return FALSE;
  1821.         }
  1822.     }
  1823.   else
  1824.     {
  1825.       next->type = POS_TOKEN_INT;
  1826.       next->d.i.val = strtol (num_str, &end, 10);
  1827.       if (end == num_str)
  1828.         {
  1829.           g_set_error (err, META_THEME_ERROR,
  1830.                        META_THEME_ERROR_FAILED,
  1831.                        _("Coordinate expression contains integer '%s' which could not be parsed"),
  1832.                        num_str);
  1833.           g_free (num_str);
  1834.           return FALSE;
  1835.         }
  1836.     }
  1837.  
  1838.   g_free (num_str);
  1839.  
  1840.   g_assert (next->type == POS_TOKEN_INT || next->type == POS_TOKEN_DOUBLE);
  1841.  
  1842.   return TRUE;
  1843. }
  1844.  
  1845. /**
  1846.  * Whether a variable can validly appear as part of the name of a variable.
  1847.  */
  1848. #define IS_VARIABLE_CHAR(c) (g_ascii_isalpha ((c)) || (c) == '_')
  1849.  
  1850. #if 0
  1851. static void
  1852. debug_print_tokens (PosToken *tokens,
  1853.                     int       n_tokens)
  1854. {
  1855.   int i;
  1856.  
  1857.   for (i = 0; i < n_tokens; i++)
  1858.     {
  1859.       PosToken *t = &tokens[i];
  1860.  
  1861.       g_print (" ");
  1862.  
  1863.       switch (t->type)
  1864.         {
  1865.         case POS_TOKEN_INT:
  1866.           g_print ("\"%d\"", t->d.i.val);
  1867.           break;
  1868.         case POS_TOKEN_DOUBLE:
  1869.           g_print ("\"%g\"", t->d.d.val);
  1870.           break;
  1871.         case POS_TOKEN_OPEN_PAREN:
  1872.           g_print ("\"(\"");
  1873.           break;
  1874.         case POS_TOKEN_CLOSE_PAREN:
  1875.           g_print ("\")\"");
  1876.           break;
  1877.         case POS_TOKEN_VARIABLE:
  1878.           g_print ("\"%s\"", t->d.v.name);
  1879.           break;
  1880.         case POS_TOKEN_OPERATOR:
  1881.           g_print ("\"%s\"", op_name (t->d.o.op));
  1882.           break;
  1883.         }
  1884.     }
  1885.  
  1886.   g_print ("\n");
  1887. }
  1888. #endif
  1889.  
  1890. /**
  1891.  * Tokenises an expression.
  1892.  *
  1893.  * \param      expr        The expression
  1894.  * \param[out] tokens_p    The resulting tokens
  1895.  * \param[out] n_tokens_p  The number of resulting tokens
  1896.  * \param[out] err  set to the problem if there was a problem
  1897.  *
  1898.  * \return  True if the expression was successfully tokenised; false otherwise.
  1899.  *
  1900.  * \ingroup tokenizer
  1901.  */
  1902. static gboolean
  1903. pos_tokenize (const char  *expr,
  1904.               PosToken   **tokens_p,
  1905.               int         *n_tokens_p,
  1906.               GError     **err)
  1907. {
  1908.   PosToken *tokens;
  1909.   int n_tokens;
  1910.   int allocated;
  1911.   const char *p;
  1912.  
  1913.   *tokens_p = NULL;
  1914.   *n_tokens_p = 0;
  1915.  
  1916.   allocated = 3;
  1917.   n_tokens = 0;
  1918.   tokens = g_new (PosToken, allocated);
  1919.  
  1920.   p = expr;
  1921.   while (*p)
  1922.     {
  1923.       PosToken *next;
  1924.       int len;
  1925.      
  1926.       if (n_tokens == allocated)
  1927.         {
  1928.           allocated *= 2;
  1929.           tokens = g_renew (PosToken, tokens, allocated);
  1930.         }
  1931.  
  1932.       next = &tokens[n_tokens];
  1933.  
  1934.       switch (*p)
  1935.         {
  1936.         case '*':
  1937.         case '/':
  1938.         case '+':
  1939.         case '-': /* negative numbers aren't allowed so this is easy */
  1940.         case '%':
  1941.         case '`':
  1942.           next->type = POS_TOKEN_OPERATOR;
  1943.           next->d.o.op = op_from_string (p, &len);
  1944.           if (next->d.o.op != POS_OP_NONE)
  1945.             {
  1946.               ++n_tokens;
  1947.               p = p + (len - 1); /* -1 since we ++p later */
  1948.             }
  1949.           else
  1950.             {
  1951.               g_set_error (err, META_THEME_ERROR,
  1952.                            META_THEME_ERROR_FAILED,
  1953.                            _("Coordinate expression contained unknown operator at the start of this text: \"%s\""),
  1954.                            p);
  1955.              
  1956.               goto error;
  1957.             }
  1958.           break;
  1959.  
  1960.         case '(':
  1961.           next->type = POS_TOKEN_OPEN_PAREN;
  1962.           ++n_tokens;
  1963.           break;
  1964.  
  1965.         case ')':
  1966.           next->type = POS_TOKEN_CLOSE_PAREN;
  1967.           ++n_tokens;
  1968.           break;
  1969.  
  1970.         case ' ':
  1971.         case '\t':
  1972.         case '\n':     
  1973.           break;
  1974.  
  1975.         default:
  1976.           if (IS_VARIABLE_CHAR (*p))
  1977.             {
  1978.               /* Assume variable */
  1979.               const char *start = p;
  1980.               while (*p && IS_VARIABLE_CHAR (*p))
  1981.                 ++p;
  1982.               g_assert (p != start);
  1983.               next->type = POS_TOKEN_VARIABLE;
  1984.               next->d.v.name = g_strndup (start, p - start);
  1985.               ++n_tokens;
  1986.               --p; /* since we ++p again at the end of while loop */
  1987.             }
  1988.           else
  1989.             {
  1990.               /* Assume number */
  1991.               const char *end;
  1992.  
  1993.               if (!parse_number (p, &end, next, err))
  1994.                 goto error;
  1995.  
  1996.               ++n_tokens;
  1997.               p = end - 1; /* -1 since we ++p again at the end of while loop */
  1998.             }
  1999.  
  2000.           break;
  2001.         }
  2002.  
  2003.       ++p;
  2004.     }
  2005.  
  2006.   if (n_tokens == 0)
  2007.     {
  2008.       g_set_error (err, META_THEME_ERROR,
  2009.                    META_THEME_ERROR_FAILED,
  2010.                    _("Coordinate expression was empty or not understood"));
  2011.  
  2012.       goto error;
  2013.     }
  2014.  
  2015.   *tokens_p = tokens;
  2016.   *n_tokens_p = n_tokens;
  2017.  
  2018.   return TRUE;
  2019.  
  2020.  error:
  2021.   g_assert (err == NULL || *err != NULL);
  2022.  
  2023.   free_tokens (tokens, n_tokens);
  2024.   return FALSE;
  2025. }
  2026.  
  2027. /**
  2028.  * The type of a PosExpr: either integer, double, or an operation.
  2029.  * \ingroup parser
  2030.  */
  2031. typedef enum
  2032. {
  2033.   POS_EXPR_INT,
  2034.   POS_EXPR_DOUBLE,
  2035.   POS_EXPR_OPERATOR
  2036. } PosExprType;
  2037.  
  2038. /**
  2039.  * Type and value of an expression in a parsed sequence. We don't
  2040.  * keep expressions in a tree; if this is of type POS_EXPR_OPERATOR,
  2041.  * the arguments of the operator will be in the array positions
  2042.  * immediately preceding and following this operator; they cannot
  2043.  * themselves be operators.
  2044.  *
  2045.  * \bug operator is char; it should really be of PosOperatorType.
  2046.  * \ingroup parser
  2047.  */
  2048. typedef struct
  2049. {
  2050.   PosExprType type;
  2051.   union
  2052.   {
  2053.     double double_val;
  2054.     int int_val;
  2055.     char operator;
  2056.   } d;
  2057. } PosExpr;
  2058.  
  2059. #if 0
  2060. static void
  2061. debug_print_exprs (PosExpr *exprs,
  2062.                    int      n_exprs)
  2063. {
  2064.   int i;
  2065.  
  2066.   for (i = 0; i < n_exprs; i++)
  2067.     {
  2068.       switch (exprs[i].type)
  2069.         {
  2070.         case POS_EXPR_INT:
  2071.           g_print (" %d", exprs[i].d.int_val);
  2072.           break;
  2073.         case POS_EXPR_DOUBLE:
  2074.           g_print (" %g", exprs[i].d.double_val);
  2075.           break;
  2076.         case POS_EXPR_OPERATOR:
  2077.           g_print (" %s", op_name (exprs[i].d.operator));
  2078.           break;
  2079.         }
  2080.     }
  2081.   g_print ("\n");
  2082. }
  2083. #endif
  2084.  
  2085. static gboolean
  2086. do_operation (PosExpr *a,
  2087.               PosExpr *b,
  2088.               PosOperatorType op,
  2089.               GError **err)
  2090. {
  2091.   /* Promote types to double if required */
  2092.   if (a->type == POS_EXPR_DOUBLE ||
  2093.       b->type == POS_EXPR_DOUBLE)
  2094.     {
  2095.       if (a->type != POS_EXPR_DOUBLE)
  2096.         {
  2097.           a->type = POS_EXPR_DOUBLE;
  2098.           a->d.double_val = a->d.int_val;
  2099.         }
  2100.       if (b->type != POS_EXPR_DOUBLE)
  2101.         {
  2102.           b->type = POS_EXPR_DOUBLE;
  2103.           b->d.double_val = b->d.int_val;
  2104.         }
  2105.     }
  2106.  
  2107.   g_assert (a->type == b->type);
  2108.  
  2109.   if (a->type == POS_EXPR_INT)
  2110.     {
  2111.       switch (op)
  2112.         {
  2113.         case POS_OP_MULTIPLY:
  2114.           a->d.int_val = a->d.int_val * b->d.int_val;
  2115.           break;
  2116.         case POS_OP_DIVIDE:
  2117.           if (b->d.int_val == 0)
  2118.             {
  2119.               g_set_error (err, META_THEME_ERROR,
  2120.                            META_THEME_ERROR_DIVIDE_BY_ZERO,
  2121.                            _("Coordinate expression results in division by zero"));
  2122.               return FALSE;
  2123.             }
  2124.           a->d.int_val = a->d.int_val / b->d.int_val;
  2125.           break;
  2126.         case POS_OP_MOD:
  2127.           if (b->d.int_val == 0)
  2128.             {
  2129.               g_set_error (err, META_THEME_ERROR,
  2130.                            META_THEME_ERROR_DIVIDE_BY_ZERO,
  2131.                            _("Coordinate expression results in division by zero"));
  2132.               return FALSE;
  2133.             }
  2134.           a->d.int_val = a->d.int_val % b->d.int_val;
  2135.           break;
  2136.         case POS_OP_ADD:
  2137.           a->d.int_val = a->d.int_val + b->d.int_val;
  2138.           break;
  2139.         case POS_OP_SUBTRACT:
  2140.           a->d.int_val = a->d.int_val - b->d.int_val;
  2141.           break;
  2142.         case POS_OP_MAX:
  2143.           a->d.int_val = MAX (a->d.int_val, b->d.int_val);
  2144.           break;
  2145.         case POS_OP_MIN:
  2146.           a->d.int_val = MIN (a->d.int_val, b->d.int_val);
  2147.           break;
  2148.         case POS_OP_NONE:
  2149.           g_assert_not_reached ();
  2150.           break;
  2151.         }
  2152.     }
  2153.   else if (a->type == POS_EXPR_DOUBLE)
  2154.     {
  2155.       switch (op)
  2156.         {
  2157.         case POS_OP_MULTIPLY:
  2158.           a->d.double_val = a->d.double_val * b->d.double_val;
  2159.           break;
  2160.         case POS_OP_DIVIDE:
  2161.           if (b->d.double_val == 0.0)
  2162.             {
  2163.               g_set_error (err, META_THEME_ERROR,
  2164.                            META_THEME_ERROR_DIVIDE_BY_ZERO,
  2165.                            _("Coordinate expression results in division by zero"));
  2166.               return FALSE;
  2167.             }
  2168.           a->d.double_val = a->d.double_val / b->d.double_val;
  2169.           break;
  2170.         case POS_OP_MOD:
  2171.           g_set_error (err, META_THEME_ERROR,
  2172.                        META_THEME_ERROR_MOD_ON_FLOAT,
  2173.                        _("Coordinate expression tries to use mod operator on a floating-point number"));
  2174.           return FALSE;
  2175.         case POS_OP_ADD:
  2176.           a->d.double_val = a->d.double_val + b->d.double_val;
  2177.           break;
  2178.         case POS_OP_SUBTRACT:
  2179.           a->d.double_val = a->d.double_val - b->d.double_val;
  2180.           break;
  2181.         case POS_OP_MAX:
  2182.           a->d.double_val = MAX (a->d.double_val, b->d.double_val);
  2183.           break;
  2184.         case POS_OP_MIN:
  2185.           a->d.double_val = MIN (a->d.double_val, b->d.double_val);
  2186.           break;
  2187.         case POS_OP_NONE:
  2188.           g_assert_not_reached ();
  2189.           break;
  2190.         }
  2191.     }
  2192.   else
  2193.     g_assert_not_reached ();
  2194.  
  2195.   return TRUE;
  2196. }
  2197.  
  2198. static gboolean
  2199. do_operations (PosExpr *exprs,
  2200.                int     *n_exprs,
  2201.                int      precedence,
  2202.                GError **err)
  2203. {
  2204.   int i;
  2205.  
  2206. #if 0
  2207.   g_print ("Doing prec %d ops on %d exprs\n", precedence, *n_exprs);
  2208.   debug_print_exprs (exprs, *n_exprs);
  2209. #endif
  2210.  
  2211.   i = 1;
  2212.   while (i < *n_exprs)
  2213.     {
  2214.       gboolean compress;
  2215.  
  2216.       /* exprs[i-1] first operand
  2217.        * exprs[i]   operator
  2218.        * exprs[i+1] second operand
  2219.        *
  2220.        * we replace first operand with result of mul/div/mod,
  2221.        * or skip over operator and second operand if we have
  2222.        * an add/subtract
  2223.        */
  2224.  
  2225.       if (exprs[i-1].type == POS_EXPR_OPERATOR)
  2226.         {
  2227.           g_set_error (err, META_THEME_ERROR,
  2228.                        META_THEME_ERROR_FAILED,
  2229.                        _("Coordinate expression has an operator \"%s\" where an operand was expected"),
  2230.                        op_name (exprs[i-1].d.operator));
  2231.           return FALSE;
  2232.         }
  2233.  
  2234.       if (exprs[i].type != POS_EXPR_OPERATOR)
  2235.         {
  2236.           g_set_error (err, META_THEME_ERROR,
  2237.                        META_THEME_ERROR_FAILED,
  2238.                        _("Coordinate expression had an operand where an operator was expected"));
  2239.           return FALSE;
  2240.         }
  2241.  
  2242.       if (i == (*n_exprs - 1))
  2243.         {
  2244.           g_set_error (err, META_THEME_ERROR,
  2245.                        META_THEME_ERROR_FAILED,
  2246.                        _("Coordinate expression ended with an operator instead of an operand"));
  2247.           return FALSE;
  2248.         }
  2249.  
  2250.       g_assert ((i+1) < *n_exprs);
  2251.  
  2252.       if (exprs[i+1].type == POS_EXPR_OPERATOR)
  2253.         {
  2254.           g_set_error (err, META_THEME_ERROR,
  2255.                        META_THEME_ERROR_FAILED,
  2256.                        _("Coordinate expression has operator \"%c\" following operator \"%c\" with no operand in between"),
  2257.                        exprs[i+1].d.operator,
  2258.                        exprs[i].d.operator);
  2259.           return FALSE;
  2260.         }
  2261.  
  2262.       compress = FALSE;
  2263.  
  2264.       switch (precedence)
  2265.         {
  2266.         case 2:
  2267.           switch (exprs[i].d.operator)
  2268.             {
  2269.             case POS_OP_DIVIDE:
  2270.             case POS_OP_MOD:
  2271.             case POS_OP_MULTIPLY:
  2272.               compress = TRUE;
  2273.               if (!do_operation (&exprs[i-1], &exprs[i+1],
  2274.                                  exprs[i].d.operator,
  2275.                                  err))
  2276.                 return FALSE;
  2277.               break;
  2278.             }
  2279.           break;
  2280.         case 1:
  2281.           switch (exprs[i].d.operator)
  2282.             {
  2283.             case POS_OP_ADD:
  2284.             case POS_OP_SUBTRACT:
  2285.               compress = TRUE;
  2286.               if (!do_operation (&exprs[i-1], &exprs[i+1],
  2287.                                  exprs[i].d.operator,
  2288.                                  err))
  2289.                 return FALSE;
  2290.               break;
  2291.             }
  2292.           break;
  2293.           /* I have no rationale at all for making these low-precedence */
  2294.         case 0:
  2295.           switch (exprs[i].d.operator)
  2296.             {
  2297.             case POS_OP_MAX:
  2298.             case POS_OP_MIN:
  2299.               compress = TRUE;
  2300.               if (!do_operation (&exprs[i-1], &exprs[i+1],
  2301.                                  exprs[i].d.operator,
  2302.                                  err))
  2303.                 return FALSE;
  2304.               break;
  2305.             }
  2306.           break;
  2307.         }
  2308.  
  2309.       if (compress)
  2310.         {
  2311.           /* exprs[i-1] first operand (now result)
  2312.            * exprs[i]   operator
  2313.            * exprs[i+1] second operand
  2314.            * exprs[i+2] new operator
  2315.            *
  2316.            * we move new operator just after first operand
  2317.            */
  2318.           if ((i+2) < *n_exprs)
  2319.             {
  2320.               g_memmove (&exprs[i], &exprs[i+2],
  2321.                          sizeof (PosExpr) * (*n_exprs - i - 2));
  2322.             }
  2323.  
  2324.           *n_exprs -= 2;
  2325.         }
  2326.       else
  2327.         {
  2328.           /* Skip operator and next operand */
  2329.           i += 2;
  2330.         }
  2331.     }
  2332.  
  2333.   return TRUE;
  2334. }
  2335.  
  2336. /**
  2337.  * There is a predefined set of variables which can appear in an expression.
  2338.  * Here we take a token representing a variable, and return the current value
  2339.  * of that variable in a particular environment.
  2340.  * (The value is always an integer.)
  2341.  *
  2342.  * There are supposedly some circumstances in which this function can be
  2343.  * called from outside Metacity, in which case env->theme will be NULL, and
  2344.  * therefore we can't use it to find out quark values, so we do the comparison
  2345.  * using strcmp, which is slower.
  2346.  *
  2347.  * \param t  The token representing a variable
  2348.  * \param[out] result  The value of that variable; not set if the token did
  2349.  *                     not represent a known variable
  2350.  * \param env  The environment within which t should be evaluated
  2351.  * \param[out] err  set to the problem if there was a problem
  2352.  *
  2353.  * \return true if we found the variable asked for, false if we didn't
  2354.  *
  2355.  * \bug shouldn't t be const?
  2356.  * \bug we should perhaps consider some sort of lookup arrangement into an
  2357.  *      array; also, the duplication of code is unlovely; perhaps using glib
  2358.  *      string hashes instead of quarks would fix both problems?
  2359.  * \ingroup parser
  2360.  */
  2361. static gboolean
  2362. pos_eval_get_variable (PosToken                  *t,
  2363.                        int                       *result,
  2364.                        const MetaPositionExprEnv *env,
  2365.                        GError                   **err)
  2366. {
  2367.   if (env->theme)
  2368.     {
  2369.       if (t->d.v.name_quark == env->theme->quark_width)
  2370.         *result = env->rect.width;
  2371.       else if (t->d.v.name_quark == env->theme->quark_height)
  2372.         *result = env->rect.height;
  2373.       else if (env->object_width >= 0 &&
  2374.                t->d.v.name_quark == env->theme->quark_object_width)
  2375.         *result = env->object_width;
  2376.       else if (env->object_height >= 0 &&
  2377.                t->d.v.name_quark == env->theme->quark_object_height)
  2378.         *result = env->object_height;
  2379.       else if (t->d.v.name_quark == env->theme->quark_left_width)
  2380.         *result = env->left_width;
  2381.       else if (t->d.v.name_quark == env->theme->quark_right_width)
  2382.         *result = env->right_width;
  2383.       else if (t->d.v.name_quark == env->theme->quark_top_height)
  2384.         *result = env->top_height;
  2385.       else if (t->d.v.name_quark == env->theme->quark_bottom_height)
  2386.         *result = env->bottom_height;
  2387.       else if (t->d.v.name_quark == env->theme->quark_mini_icon_width)
  2388.         *result = env->mini_icon_width;
  2389.       else if (t->d.v.name_quark == env->theme->quark_mini_icon_height)
  2390.         *result = env->mini_icon_height;
  2391.       else if (t->d.v.name_quark == env->theme->quark_icon_width)
  2392.         *result = env->icon_width;
  2393.       else if (t->d.v.name_quark == env->theme->quark_icon_height)
  2394.         *result = env->icon_height;
  2395.       else if (t->d.v.name_quark == env->theme->quark_title_width)
  2396.         *result = env->title_width;
  2397.       else if (t->d.v.name_quark == env->theme->quark_title_height)
  2398.         *result = env->title_height;
  2399.       else if (t->d.v.name_quark == env->theme->quark_frame_x_center)
  2400.         *result = env->frame_x_center;
  2401.       else if (t->d.v.name_quark == env->theme->quark_frame_y_center)
  2402.         *result = env->frame_y_center;
  2403.       else
  2404.         {
  2405.           g_set_error (err, META_THEME_ERROR,
  2406.                        META_THEME_ERROR_UNKNOWN_VARIABLE,
  2407.                        _("Coordinate expression had unknown variable or constant \"%s\""),
  2408.                        t->d.v.name);
  2409.           return FALSE;
  2410.         }
  2411.     }
  2412.   else
  2413.     {
  2414.       if (strcmp (t->d.v.name, "width") == 0)
  2415.         *result = env->rect.width;
  2416.       else if (strcmp (t->d.v.name, "height") == 0)
  2417.         *result = env->rect.height;
  2418.       else if (env->object_width >= 0 &&
  2419.                strcmp (t->d.v.name, "object_width") == 0)
  2420.         *result = env->object_width;
  2421.       else if (env->object_height >= 0 &&
  2422.                strcmp (t->d.v.name, "object_height") == 0)
  2423.         *result = env->object_height;
  2424.       else if (strcmp (t->d.v.name, "left_width") == 0)
  2425.         *result = env->left_width;
  2426.       else if (strcmp (t->d.v.name, "right_width") == 0)
  2427.         *result = env->right_width;
  2428.       else if (strcmp (t->d.v.name, "top_height") == 0)
  2429.         *result = env->top_height;
  2430.       else if (strcmp (t->d.v.name, "bottom_height") == 0)
  2431.         *result = env->bottom_height;
  2432.       else if (strcmp (t->d.v.name, "mini_icon_width") == 0)
  2433.         *result = env->mini_icon_width;
  2434.       else if (strcmp (t->d.v.name, "mini_icon_height") == 0)
  2435.         *result = env->mini_icon_height;
  2436.       else if (strcmp (t->d.v.name, "icon_width") == 0)
  2437.         *result = env->icon_width;
  2438.       else if (strcmp (t->d.v.name, "icon_height") == 0)
  2439.         *result = env->icon_height;
  2440.       else if (strcmp (t->d.v.name, "title_width") == 0)
  2441.         *result = env->title_width;
  2442.       else if (strcmp (t->d.v.name, "title_height") == 0)
  2443.         *result = env->title_height;
  2444.       else if (strcmp (t->d.v.name, "frame_x_center") == 0)
  2445.         *result = env->frame_x_center;
  2446.       else if (strcmp (t->d.v.name, "frame_y_center") == 0)
  2447.         *result = env->frame_y_center;
  2448.       else
  2449.         {
  2450.           g_set_error (err, META_THEME_ERROR,
  2451.                        META_THEME_ERROR_UNKNOWN_VARIABLE,
  2452.                        _("Coordinate expression had unknown variable or constant \"%s\""),
  2453.                        t->d.v.name);
  2454.           return FALSE;
  2455.         }
  2456.     }
  2457.  
  2458.   return TRUE;
  2459. }
  2460.  
  2461. /**
  2462.  * Evaluates a sequence of tokens within a particular environment context,
  2463.  * and returns the current value. May recur if parantheses are found.
  2464.  *
  2465.  * \param tokens  A list of tokens to evaluate.
  2466.  * \param n_tokens  How many tokens are in the list.
  2467.  * \param env  The environment context in which to evaluate the expression.
  2468.  * \param[out] result  The current value of the expression
  2469.  *
  2470.  * \bug Yes, we really do reparse the expression every time it's evaluated.
  2471.  *      We should keep the parse tree around all the time and just
  2472.  *      run the new values through it.
  2473.  * \ingroup parser
  2474.  */
  2475. static gboolean
  2476. pos_eval_helper (PosToken                   *tokens,
  2477.                  int                         n_tokens,
  2478.                  const MetaPositionExprEnv  *env,
  2479.                  PosExpr                    *result,
  2480.                  GError                    **err)
  2481. {
  2482.   /* Lazy-ass hardcoded limit on number of terms in expression */
  2483. #define MAX_EXPRS 32
  2484.   int paren_level;
  2485.   int first_paren;
  2486.   int i;
  2487.   PosExpr exprs[MAX_EXPRS];
  2488.   int n_exprs;
  2489.   int precedence;
  2490.  
  2491.   /* Our first goal is to get a list of PosExpr, essentially
  2492.    * substituting variables and handling parentheses.
  2493.    */
  2494.  
  2495.   first_paren = 0;
  2496.   paren_level = 0;
  2497.   n_exprs = 0;
  2498.   for (i = 0; i < n_tokens; i++)
  2499.     {
  2500.       PosToken *t = &tokens[i];
  2501.  
  2502.       if (n_exprs >= MAX_EXPRS)
  2503.         {
  2504.           g_set_error (err, META_THEME_ERROR,
  2505.                        META_THEME_ERROR_FAILED,
  2506.                        _("Coordinate expression parser overflowed its buffer."));
  2507.           return FALSE;
  2508.         }
  2509.  
  2510.       if (paren_level == 0)
  2511.         {
  2512.           switch (t->type)
  2513.             {
  2514.             case POS_TOKEN_INT:
  2515.               exprs[n_exprs].type = POS_EXPR_INT;
  2516.               exprs[n_exprs].d.int_val = t->d.i.val;
  2517.               ++n_exprs;
  2518.               break;
  2519.  
  2520.             case POS_TOKEN_DOUBLE:
  2521.               exprs[n_exprs].type = POS_EXPR_DOUBLE;
  2522.               exprs[n_exprs].d.double_val = t->d.d.val;
  2523.               ++n_exprs;
  2524.               break;
  2525.  
  2526.             case POS_TOKEN_OPEN_PAREN:
  2527.               ++paren_level;
  2528.               if (paren_level == 1)
  2529.                 first_paren = i;
  2530.               break;
  2531.  
  2532.             case POS_TOKEN_CLOSE_PAREN:
  2533.               g_set_error (err, META_THEME_ERROR,
  2534.                            META_THEME_ERROR_BAD_PARENS,
  2535.                            _("Coordinate expression had a close parenthesis with no open parenthesis"));
  2536.               return FALSE;
  2537.  
  2538.             case POS_TOKEN_VARIABLE:
  2539.               exprs[n_exprs].type = POS_EXPR_INT;
  2540.  
  2541.               /* FIXME we should just dump all this crap
  2542.                * in a hash, maybe keep width/height out
  2543.                * for optimization purposes
  2544.                */
  2545.               if (!pos_eval_get_variable (t, &exprs[n_exprs].d.int_val, env, err))
  2546.                 return FALSE;
  2547.                  
  2548.               ++n_exprs;
  2549.               break;
  2550.  
  2551.             case POS_TOKEN_OPERATOR:
  2552.               exprs[n_exprs].type = POS_EXPR_OPERATOR;
  2553.               exprs[n_exprs].d.operator = t->d.o.op;
  2554.               ++n_exprs;
  2555.               break;
  2556.             }
  2557.         }
  2558.       else
  2559.         {
  2560.           g_assert (paren_level > 0);
  2561.  
  2562.           switch (t->type)
  2563.             {
  2564.             case POS_TOKEN_INT:
  2565.             case POS_TOKEN_DOUBLE:
  2566.             case POS_TOKEN_VARIABLE:
  2567.             case POS_TOKEN_OPERATOR:
  2568.               break;
  2569.  
  2570.             case POS_TOKEN_OPEN_PAREN:
  2571.               ++paren_level;
  2572.               break;
  2573.  
  2574.             case POS_TOKEN_CLOSE_PAREN:
  2575.               if (paren_level == 1)
  2576.                 {
  2577.                   /* We closed a toplevel paren group, so recurse */
  2578.                   if (!pos_eval_helper (&tokens[first_paren+1],
  2579.                                         i - first_paren - 1,
  2580.                                         env,
  2581.                                         &exprs[n_exprs],
  2582.                                         err))
  2583.                     return FALSE;
  2584.  
  2585.                   ++n_exprs;
  2586.                 }
  2587.  
  2588.               --paren_level;
  2589.               break;
  2590.  
  2591.             }
  2592.         }
  2593.     }
  2594.  
  2595.   if (paren_level > 0)
  2596.     {
  2597.       g_set_error (err, META_THEME_ERROR,
  2598.                    META_THEME_ERROR_BAD_PARENS,
  2599.                    _("Coordinate expression had an open parenthesis with no close parenthesis"));
  2600.       return FALSE;
  2601.     }
  2602.  
  2603.   /* Now we have no parens and no vars; so we just do all the multiplies
  2604.    * and divides, then all the add and subtract.
  2605.    */
  2606.   if (n_exprs == 0)
  2607.     {
  2608.       g_set_error (err, META_THEME_ERROR,
  2609.                    META_THEME_ERROR_FAILED,
  2610.                    _("Coordinate expression doesn't seem to have any operators or operands"));
  2611.       return FALSE;
  2612.     }
  2613.  
  2614.   /* precedence 1 ops */
  2615.   precedence = 2;
  2616.   while (precedence >= 0)
  2617.     {
  2618.       if (!do_operations (exprs, &n_exprs, precedence, err))
  2619.         return FALSE;
  2620.       --precedence;
  2621.     }
  2622.  
  2623.   g_assert (n_exprs == 1);
  2624.  
  2625.   *result = *exprs;
  2626.  
  2627.   return TRUE;
  2628. }
  2629.  
  2630. /*
  2631.  *   expr = int | double | expr * expr | expr / expr |
  2632.  *          expr + expr | expr - expr | (expr)
  2633.  *
  2634.  *   so very not worth fooling with bison, yet so very painful by hand.
  2635.  */
  2636. /**
  2637.  * Evaluates an expression.
  2638.  *
  2639.  * \param spec  The expression to evaluate.
  2640.  * \param env   The environment context to evaluate the expression in.
  2641.  * \param[out] val_p  The integer value of the expression; if the expression
  2642.  *                    is of type float, this will be rounded. If we return
  2643.  *                    FALSE because the expression is invalid, this will be
  2644.  *                    zero.
  2645.  * \param[out] err    The error, if anything went wrong.
  2646.  *
  2647.  * \return  True if we evaluated the expression successfully; false otherwise.
  2648.  *
  2649.  * \bug Shouldn't spec be const?
  2650.  * \ingroup parser
  2651.  */
  2652. static gboolean
  2653. pos_eval (MetaDrawSpec              *spec,
  2654.           const MetaPositionExprEnv *env,
  2655.           int                       *val_p,
  2656.           GError                   **err)
  2657. {
  2658.   PosExpr expr;
  2659.  
  2660.   *val_p = 0;
  2661.  
  2662.   if (pos_eval_helper (spec->tokens, spec->n_tokens, env, &expr, err))
  2663.     {
  2664.       switch (expr.type)
  2665.         {
  2666.         case POS_EXPR_INT:
  2667.           *val_p = expr.d.int_val;
  2668.           break;
  2669.         case POS_EXPR_DOUBLE:
  2670.           *val_p = expr.d.double_val;
  2671.           break;
  2672.         case POS_EXPR_OPERATOR:
  2673.           g_assert_not_reached ();
  2674.           break;
  2675.         }
  2676.       return TRUE;
  2677.     }
  2678.   else
  2679.     {
  2680.       return FALSE;
  2681.     }
  2682. }
  2683.  
  2684. /* We always return both X and Y, but only one will be meaningful in
  2685.  * most contexts.
  2686.  */
  2687.  
  2688. /**
  2689.  * meta_parse_position_expression: (skip)
  2690.  *
  2691.  */
  2692. gboolean
  2693. meta_parse_position_expression (MetaDrawSpec              *spec,
  2694.                                 const MetaPositionExprEnv *env,
  2695.                                 int                       *x_return,
  2696.                                 int                       *y_return,
  2697.                                 GError                   **err)
  2698. {
  2699.   /* All positions are in a coordinate system with x, y at the origin.
  2700.    * The expression can have -, +, *, / as operators, floating point
  2701.    * or integer constants, and the variables "width" and "height" and
  2702.    * optionally "object_width" and object_height". Negative numbers
  2703.    * aren't allowed.
  2704.    */
  2705.   int val;
  2706.  
  2707.   if (spec->constant)
  2708.     val = spec->value;
  2709.   else
  2710.     {
  2711.       if (pos_eval (spec, env, &spec->value, err) == FALSE)
  2712.         {
  2713.           g_assert (err == NULL || *err != NULL);
  2714.           return FALSE;
  2715.         }
  2716.  
  2717.       val = spec->value;
  2718.     }
  2719.  
  2720.   if (x_return)
  2721.     *x_return = env->rect.x + val;
  2722.   if (y_return)
  2723.     *y_return = env->rect.y + val;
  2724.  
  2725.   return TRUE;
  2726. }
  2727.  
  2728.  
  2729. /**
  2730.  * meta_parse_size_expression: (skip)
  2731.  *
  2732.  */
  2733. gboolean
  2734. meta_parse_size_expression (MetaDrawSpec              *spec,
  2735.                             const MetaPositionExprEnv *env,
  2736.                             int                       *val_return,
  2737.                             GError                   **err)
  2738. {
  2739.   int val;
  2740.  
  2741.   if (spec->constant)
  2742.     val = spec->value;
  2743.   else
  2744.     {
  2745.       if (pos_eval (spec, env, &spec->value, err) == FALSE)
  2746.         {
  2747.           g_assert (err == NULL || *err != NULL);
  2748.           return FALSE;
  2749.         }
  2750.  
  2751.       val = spec->value;
  2752.     }
  2753.  
  2754.   if (val_return)
  2755.     *val_return = MAX (val, 1); /* require that sizes be at least 1x1 */
  2756.  
  2757.   return TRUE;
  2758. }
  2759.  
  2760. /* To do this we tokenize, replace variable tokens
  2761.  * that are constants, then reassemble. The purpose
  2762.  * here is to optimize expressions so we don't do hash
  2763.  * lookups to eval them. Obviously it's a tradeoff that
  2764.  * slows down theme load times.
  2765.  */
  2766. gboolean
  2767. meta_theme_replace_constants (MetaTheme   *theme,
  2768.                               PosToken    *tokens,
  2769.                               int          n_tokens,
  2770.                               GError     **err)
  2771. {
  2772.   int i;
  2773.   double dval;
  2774.   int ival;
  2775.   gboolean is_constant = TRUE;
  2776.  
  2777.   /* Loop through tokenized string looking for variables to replace */
  2778.   for (i = 0; i < n_tokens; i++)
  2779.     {
  2780.       PosToken *t = &tokens[i];      
  2781.  
  2782.       if (t->type == POS_TOKEN_VARIABLE)
  2783.         {
  2784.           if (meta_theme_lookup_int_constant (theme, t->d.v.name, &ival))
  2785.             {
  2786.               g_free (t->d.v.name);
  2787.               t->type = POS_TOKEN_INT;
  2788.               t->d.i.val = ival;
  2789.             }
  2790.           else if (meta_theme_lookup_float_constant (theme, t->d.v.name, &dval))
  2791.             {
  2792.               g_free (t->d.v.name);
  2793.               t->type = POS_TOKEN_DOUBLE;
  2794.               t->d.d.val = dval;
  2795.             }
  2796.           else
  2797.             {
  2798.               /* If we've found a variable that cannot be replaced then the
  2799.                  expression is not a constant expression and we want to
  2800.                  replace it with a GQuark */
  2801.  
  2802.               t->d.v.name_quark = g_quark_from_string (t->d.v.name);
  2803.               is_constant = FALSE;
  2804.             }
  2805.         }
  2806.     }  
  2807.  
  2808.   return is_constant;
  2809. }
  2810.  
  2811. static int
  2812. parse_x_position_unchecked (MetaDrawSpec              *spec,
  2813.                             const MetaPositionExprEnv *env)
  2814. {
  2815.   int retval;
  2816.   GError *error;
  2817.  
  2818.   retval = 0;
  2819.   error = NULL;
  2820.   if (!meta_parse_position_expression (spec, env, &retval, NULL, &error))
  2821.     {
  2822.       meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
  2823.                     error->message);
  2824.      
  2825.       g_error_free (error);
  2826.     }
  2827.  
  2828.   return retval;
  2829. }
  2830.  
  2831. static int
  2832. parse_y_position_unchecked (MetaDrawSpec              *spec,
  2833.                             const MetaPositionExprEnv *env)
  2834. {
  2835.   int retval;
  2836.   GError *error;
  2837.  
  2838.   retval = 0;
  2839.   error = NULL;
  2840.   if (!meta_parse_position_expression (spec, env, NULL, &retval, &error))
  2841.     {
  2842.       meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
  2843.                     error->message);
  2844.  
  2845.       g_error_free (error);
  2846.     }
  2847.  
  2848.   return retval;
  2849. }
  2850.  
  2851. static int
  2852. parse_size_unchecked (MetaDrawSpec        *spec,
  2853.                       MetaPositionExprEnv *env)
  2854. {
  2855.   int retval;
  2856.   GError *error;
  2857.  
  2858.   retval = 0;
  2859.   error = NULL;
  2860.   if (!meta_parse_size_expression (spec, env, &retval, &error))
  2861.     {
  2862.       meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
  2863.                     error->message);
  2864.  
  2865.       g_error_free (error);
  2866.     }
  2867.  
  2868.   return retval;
  2869. }
  2870.  
  2871. void
  2872. meta_draw_spec_free (MetaDrawSpec *spec)
  2873. {
  2874.   if (!spec) return;
  2875.   free_tokens (spec->tokens, spec->n_tokens);
  2876.   g_slice_free (MetaDrawSpec, spec);
  2877. }
  2878.  
  2879. /**
  2880.  * meta_draw_spec_new: (skip)
  2881.  *
  2882.  */
  2883. MetaDrawSpec *
  2884. meta_draw_spec_new (MetaTheme  *theme,
  2885.                     const char *expr,
  2886.                     GError    **error)
  2887. {
  2888.   MetaDrawSpec *spec;
  2889.  
  2890.   spec = g_slice_new0 (MetaDrawSpec);
  2891.  
  2892.   pos_tokenize (expr, &spec->tokens, &spec->n_tokens, NULL);
  2893.  
  2894.   spec->constant = meta_theme_replace_constants (theme, spec->tokens,
  2895.                                                  spec->n_tokens, NULL);
  2896.   if (spec->constant)
  2897.     {
  2898.       gboolean result;
  2899.  
  2900.       result = pos_eval (spec, NULL, &spec->value, error);
  2901.       if (result == FALSE)
  2902.         {
  2903.           meta_draw_spec_free (spec);
  2904.           return NULL;
  2905.         }
  2906.     }
  2907.    
  2908.   return spec;
  2909. }
  2910.  
  2911. /**
  2912.  * meta_draw_op_new: (skip)
  2913.  *
  2914.  */
  2915. MetaDrawOp*
  2916. meta_draw_op_new (MetaDrawType type)
  2917. {
  2918.   MetaDrawOp *op;
  2919.   MetaDrawOp dummy;
  2920.   int size;
  2921.  
  2922.   size = G_STRUCT_OFFSET (MetaDrawOp, data);
  2923.  
  2924.   switch (type)
  2925.     {
  2926.     case META_DRAW_LINE:
  2927.       size += sizeof (dummy.data.line);
  2928.       break;
  2929.  
  2930.     case META_DRAW_RECTANGLE:
  2931.       size += sizeof (dummy.data.rectangle);
  2932.       break;
  2933.  
  2934.     case META_DRAW_ARC:
  2935.       size += sizeof (dummy.data.arc);
  2936.       break;
  2937.  
  2938.     case META_DRAW_CLIP:
  2939.       size += sizeof (dummy.data.clip);
  2940.       break;
  2941.      
  2942.     case META_DRAW_TINT:
  2943.       size += sizeof (dummy.data.tint);
  2944.       break;
  2945.  
  2946.     case META_DRAW_GRADIENT:
  2947.       size += sizeof (dummy.data.gradient);
  2948.       break;
  2949.  
  2950.     case META_DRAW_IMAGE:
  2951.       size += sizeof (dummy.data.image);
  2952.       break;
  2953.  
  2954.     case META_DRAW_GTK_ARROW:
  2955.       size += sizeof (dummy.data.gtk_arrow);
  2956.       break;
  2957.  
  2958.     case META_DRAW_GTK_BOX:
  2959.       size += sizeof (dummy.data.gtk_box);
  2960.       break;
  2961.  
  2962.     case META_DRAW_GTK_VLINE:
  2963.       size += sizeof (dummy.data.gtk_vline);
  2964.       break;
  2965.  
  2966.     case META_DRAW_ICON:
  2967.       size += sizeof (dummy.data.icon);
  2968.       break;
  2969.  
  2970.     case META_DRAW_TITLE:
  2971.       size += sizeof (dummy.data.title);
  2972.       break;
  2973.     case META_DRAW_OP_LIST:
  2974.       size += sizeof (dummy.data.op_list);
  2975.       break;
  2976.     case META_DRAW_TILE:
  2977.       size += sizeof (dummy.data.tile);
  2978.       break;
  2979.     }
  2980.  
  2981.   op = g_malloc0 (size);
  2982.  
  2983.   op->type = type;
  2984.  
  2985.   return op;
  2986. }
  2987.  
  2988. void
  2989. meta_draw_op_free (MetaDrawOp *op)
  2990. {
  2991.   g_return_if_fail (op != NULL);
  2992.  
  2993.   switch (op->type)
  2994.     {
  2995.     case META_DRAW_LINE:
  2996.       if (op->data.line.color_spec)
  2997.         meta_color_spec_free (op->data.line.color_spec);
  2998.  
  2999.       meta_draw_spec_free (op->data.line.x1);
  3000.       meta_draw_spec_free (op->data.line.y1);
  3001.       meta_draw_spec_free (op->data.line.x2);
  3002.       meta_draw_spec_free (op->data.line.y2);
  3003.       break;
  3004.  
  3005.     case META_DRAW_RECTANGLE:
  3006.       if (op->data.rectangle.color_spec)
  3007.         g_free (op->data.rectangle.color_spec);
  3008.  
  3009.       meta_draw_spec_free (op->data.rectangle.x);
  3010.       meta_draw_spec_free (op->data.rectangle.y);
  3011.       meta_draw_spec_free (op->data.rectangle.width);
  3012.       meta_draw_spec_free (op->data.rectangle.height);
  3013.       break;
  3014.  
  3015.     case META_DRAW_ARC:
  3016.       if (op->data.arc.color_spec)
  3017.         g_free (op->data.arc.color_spec);
  3018.  
  3019.       meta_draw_spec_free (op->data.arc.x);
  3020.       meta_draw_spec_free (op->data.arc.y);
  3021.       meta_draw_spec_free (op->data.arc.width);
  3022.       meta_draw_spec_free (op->data.arc.height);
  3023.       break;
  3024.  
  3025.     case META_DRAW_CLIP:
  3026.       meta_draw_spec_free (op->data.clip.x);
  3027.       meta_draw_spec_free (op->data.clip.y);
  3028.       meta_draw_spec_free (op->data.clip.width);
  3029.       meta_draw_spec_free (op->data.clip.height);
  3030.       break;
  3031.      
  3032.     case META_DRAW_TINT:
  3033.       if (op->data.tint.color_spec)
  3034.         meta_color_spec_free (op->data.tint.color_spec);
  3035.  
  3036.       if (op->data.tint.alpha_spec)
  3037.         meta_alpha_gradient_spec_free (op->data.tint.alpha_spec);
  3038.  
  3039.       meta_draw_spec_free (op->data.tint.x);
  3040.       meta_draw_spec_free (op->data.tint.y);
  3041.       meta_draw_spec_free (op->data.tint.width);
  3042.       meta_draw_spec_free (op->data.tint.height);
  3043.       break;
  3044.  
  3045.     case META_DRAW_GRADIENT:
  3046.       if (op->data.gradient.gradient_spec)
  3047.         meta_gradient_spec_free (op->data.gradient.gradient_spec);
  3048.  
  3049.       if (op->data.gradient.alpha_spec)
  3050.         meta_alpha_gradient_spec_free (op->data.gradient.alpha_spec);
  3051.  
  3052.       meta_draw_spec_free (op->data.gradient.x);
  3053.       meta_draw_spec_free (op->data.gradient.y);
  3054.       meta_draw_spec_free (op->data.gradient.width);
  3055.       meta_draw_spec_free (op->data.gradient.height);
  3056.       break;
  3057.  
  3058.     case META_DRAW_IMAGE:
  3059.       if (op->data.image.alpha_spec)
  3060.         meta_alpha_gradient_spec_free (op->data.image.alpha_spec);
  3061.  
  3062.       if (op->data.image.pixbuf)
  3063.         g_object_unref (G_OBJECT (op->data.image.pixbuf));
  3064.  
  3065.       if (op->data.image.colorize_spec)
  3066.     meta_color_spec_free (op->data.image.colorize_spec);
  3067.  
  3068.       if (op->data.image.colorize_cache_pixbuf)
  3069.         g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
  3070.  
  3071.       meta_draw_spec_free (op->data.image.x);
  3072.       meta_draw_spec_free (op->data.image.y);
  3073.       meta_draw_spec_free (op->data.image.width);
  3074.       meta_draw_spec_free (op->data.image.height);
  3075.       break;
  3076.  
  3077.     case META_DRAW_GTK_ARROW:
  3078.       meta_draw_spec_free (op->data.gtk_arrow.x);
  3079.       meta_draw_spec_free (op->data.gtk_arrow.y);
  3080.       meta_draw_spec_free (op->data.gtk_arrow.width);
  3081.       meta_draw_spec_free (op->data.gtk_arrow.height);
  3082.       break;
  3083.  
  3084.     case META_DRAW_GTK_BOX:
  3085.       meta_draw_spec_free (op->data.gtk_box.x);
  3086.       meta_draw_spec_free (op->data.gtk_box.y);
  3087.       meta_draw_spec_free (op->data.gtk_box.width);
  3088.       meta_draw_spec_free (op->data.gtk_box.height);
  3089.       break;
  3090.  
  3091.     case META_DRAW_GTK_VLINE:
  3092.       meta_draw_spec_free (op->data.gtk_vline.x);
  3093.       meta_draw_spec_free (op->data.gtk_vline.y1);
  3094.       meta_draw_spec_free (op->data.gtk_vline.y2);
  3095.       break;
  3096.  
  3097.     case META_DRAW_ICON:
  3098.       if (op->data.icon.alpha_spec)
  3099.         meta_alpha_gradient_spec_free (op->data.icon.alpha_spec);
  3100.  
  3101.       meta_draw_spec_free (op->data.icon.x);
  3102.       meta_draw_spec_free (op->data.icon.y);
  3103.       meta_draw_spec_free (op->data.icon.width);
  3104.       meta_draw_spec_free (op->data.icon.height);
  3105.       break;
  3106.  
  3107.     case META_DRAW_TITLE:
  3108.       if (op->data.title.color_spec)
  3109.         meta_color_spec_free (op->data.title.color_spec);
  3110.  
  3111.       meta_draw_spec_free (op->data.title.x);
  3112.       meta_draw_spec_free (op->data.title.y);
  3113.       if (op->data.title.ellipsize_width)
  3114.         meta_draw_spec_free (op->data.title.ellipsize_width);
  3115.       break;
  3116.  
  3117.     case META_DRAW_OP_LIST:
  3118.       if (op->data.op_list.op_list)
  3119.         meta_draw_op_list_unref (op->data.op_list.op_list);
  3120.  
  3121.       meta_draw_spec_free (op->data.op_list.x);
  3122.       meta_draw_spec_free (op->data.op_list.y);
  3123.       meta_draw_spec_free (op->data.op_list.width);
  3124.       meta_draw_spec_free (op->data.op_list.height);
  3125.       break;
  3126.  
  3127.     case META_DRAW_TILE:
  3128.       if (op->data.tile.op_list)
  3129.         meta_draw_op_list_unref (op->data.tile.op_list);
  3130.  
  3131.       meta_draw_spec_free (op->data.tile.x);
  3132.       meta_draw_spec_free (op->data.tile.y);
  3133.       meta_draw_spec_free (op->data.tile.width);
  3134.       meta_draw_spec_free (op->data.tile.height);
  3135.       meta_draw_spec_free (op->data.tile.tile_xoffset);
  3136.       meta_draw_spec_free (op->data.tile.tile_yoffset);
  3137.       meta_draw_spec_free (op->data.tile.tile_width);
  3138.       meta_draw_spec_free (op->data.tile.tile_height);
  3139.       break;
  3140.     }
  3141.  
  3142.   g_free (op);
  3143. }
  3144.  
  3145. static GdkPixbuf*
  3146. apply_alpha (GdkPixbuf             *pixbuf,
  3147.              MetaAlphaGradientSpec *spec,
  3148.              gboolean               force_copy)
  3149. {
  3150.   GdkPixbuf *new_pixbuf;
  3151.   gboolean needs_alpha;
  3152.  
  3153.   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
  3154.  
  3155.   needs_alpha = spec && (spec->n_alphas > 1 ||
  3156.                          spec->alphas[0] != 0xff);
  3157.  
  3158.   if (!needs_alpha)
  3159.     return pixbuf;
  3160.  
  3161.   if (!gdk_pixbuf_get_has_alpha (pixbuf))
  3162.     {
  3163.       new_pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
  3164.       g_object_unref (G_OBJECT (pixbuf));
  3165.       pixbuf = new_pixbuf;
  3166.     }
  3167.   else if (force_copy)
  3168.     {
  3169.       new_pixbuf = gdk_pixbuf_copy (pixbuf);
  3170.       g_object_unref (G_OBJECT (pixbuf));
  3171.       pixbuf = new_pixbuf;
  3172.     }
  3173.  
  3174.   g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
  3175.  
  3176.   meta_gradient_add_alpha (pixbuf, spec->alphas, spec->n_alphas, spec->type);
  3177.  
  3178.   return pixbuf;
  3179. }
  3180.  
  3181. static GdkPixbuf*
  3182. pixbuf_tile (GdkPixbuf *tile,
  3183.              int        width,
  3184.              int        height)
  3185. {
  3186.   GdkPixbuf *pixbuf;
  3187.   int tile_width;
  3188.   int tile_height;
  3189.   int i, j;
  3190.  
  3191.   tile_width = gdk_pixbuf_get_width (tile);
  3192.   tile_height = gdk_pixbuf_get_height (tile);
  3193.  
  3194.   pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
  3195.                            gdk_pixbuf_get_has_alpha (tile),
  3196.                            8, width, height);
  3197.  
  3198.   i = 0;
  3199.   while (i < width)
  3200.     {
  3201.       j = 0;
  3202.       while (j < height)
  3203.         {
  3204.           int w, h;
  3205.  
  3206.           w = MIN (tile_width, width - i);
  3207.           h = MIN (tile_height, height - j);
  3208.          
  3209.           gdk_pixbuf_copy_area (tile,
  3210.                                 0, 0,
  3211.                                 w, h,
  3212.                                 pixbuf,
  3213.                                 i, j);
  3214.  
  3215.           j += tile_height;
  3216.         }
  3217.      
  3218.       i += tile_width;
  3219.     }
  3220.  
  3221.   return pixbuf;
  3222. }
  3223.  
  3224. static GdkPixbuf *
  3225. replicate_rows (GdkPixbuf  *src,
  3226.                 int         src_x,
  3227.                 int         src_y,
  3228.                 int         width,
  3229.                 int         height)
  3230. {
  3231.   unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
  3232.   unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
  3233.   unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
  3234.                            * n_channels);
  3235.   unsigned char *dest_pixels;
  3236.   GdkPixbuf *result;
  3237.   unsigned int dest_rowstride;
  3238.   int i;
  3239.  
  3240.   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
  3241.                            width, height);
  3242.   dest_rowstride = gdk_pixbuf_get_rowstride (result);
  3243.   dest_pixels = gdk_pixbuf_get_pixels (result);
  3244.  
  3245.   for (i = 0; i < height; i++)
  3246.     memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
  3247.  
  3248.   return result;
  3249. }
  3250.  
  3251. static GdkPixbuf *
  3252. replicate_cols (GdkPixbuf  *src,
  3253.                 int         src_x,
  3254.                 int         src_y,
  3255.                 int         width,
  3256.                 int         height)
  3257. {
  3258.   unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
  3259.   unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
  3260.   unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
  3261.                            * n_channels);
  3262.   unsigned char *dest_pixels;
  3263.   GdkPixbuf *result;
  3264.   unsigned int dest_rowstride;
  3265.   int i, j;
  3266.  
  3267.   result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
  3268.                            width, height);
  3269.   dest_rowstride = gdk_pixbuf_get_rowstride (result);
  3270.   dest_pixels = gdk_pixbuf_get_pixels (result);
  3271.  
  3272.   for (i = 0; i < height; i++)
  3273.     {
  3274.       unsigned char *p = dest_pixels + dest_rowstride * i;
  3275.       unsigned char *q = pixels + src_rowstride * i;
  3276.  
  3277.       unsigned char r = *(q++);
  3278.       unsigned char g = *(q++);
  3279.       unsigned char b = *(q++);
  3280.      
  3281.       if (n_channels == 4)
  3282.         {
  3283.           unsigned char a;
  3284.          
  3285.           a = *(q++);
  3286.          
  3287.           for (j = 0; j < width; j++)
  3288.             {
  3289.               *(p++) = r;
  3290.               *(p++) = g;
  3291.               *(p++) = b;                    
  3292.               *(p++) = a;
  3293.             }
  3294.         }
  3295.       else
  3296.         {
  3297.           for (j = 0; j < width; j++)
  3298.             {
  3299.               *(p++) = r;
  3300.               *(p++) = g;
  3301.               *(p++) = b;
  3302.             }
  3303.         }
  3304.     }
  3305.  
  3306.   return result;
  3307. }
  3308.  
  3309. static GdkPixbuf*
  3310. scale_and_alpha_pixbuf (GdkPixbuf             *src,
  3311.                         MetaAlphaGradientSpec *alpha_spec,
  3312.                         MetaImageFillType      fill_type,
  3313.                         int                    width,
  3314.                         int                    height,
  3315.                         gboolean               vertical_stripes,
  3316.                         gboolean               horizontal_stripes)
  3317. {
  3318.   GdkPixbuf *pixbuf;
  3319.   GdkPixbuf *temp_pixbuf;
  3320.  
  3321.   pixbuf = NULL;
  3322.  
  3323.   pixbuf = src;
  3324.  
  3325.   if (gdk_pixbuf_get_width (pixbuf) == width &&
  3326.       gdk_pixbuf_get_height (pixbuf) == height)
  3327.     {
  3328.       g_object_ref (G_OBJECT (pixbuf));
  3329.     }
  3330.   else
  3331.     {
  3332.       if (fill_type == META_IMAGE_FILL_TILE)
  3333.         {
  3334.           pixbuf = pixbuf_tile (pixbuf, width, height);
  3335.         }
  3336.       else
  3337.         {
  3338.           int src_h, src_w, dest_h, dest_w;
  3339.           src_h = gdk_pixbuf_get_height (src);
  3340.           src_w = gdk_pixbuf_get_width (src);
  3341.  
  3342.           /* prefer to replicate_cols if possible, as that
  3343.            * is faster (no memory reads)
  3344.            */
  3345.           if (horizontal_stripes)
  3346.             {
  3347.               dest_w = gdk_pixbuf_get_width (src);
  3348.               dest_h = height;
  3349.             }
  3350.           else if (vertical_stripes)
  3351.             {
  3352.               dest_w = width;
  3353.               dest_h = gdk_pixbuf_get_height (src);
  3354.             }
  3355.  
  3356.           else
  3357.             {
  3358.               dest_w = width;
  3359.               dest_h = height;
  3360.             }
  3361.  
  3362.           if (dest_w == src_w && dest_h == src_h)
  3363.             {
  3364.               temp_pixbuf = src;
  3365.               g_object_ref (G_OBJECT (temp_pixbuf));
  3366.             }
  3367.           else
  3368.             {
  3369.               temp_pixbuf = gdk_pixbuf_scale_simple (src,
  3370.                                                      dest_w, dest_h,
  3371.                                                      GDK_INTERP_BILINEAR);
  3372.             }
  3373.  
  3374.           /* prefer to replicate_cols if possible, as that
  3375.            * is faster (no memory reads)
  3376.            */
  3377.           if (horizontal_stripes)
  3378.             {
  3379.               pixbuf = replicate_cols (temp_pixbuf, 0, 0, width, height);
  3380.               g_object_unref (G_OBJECT (temp_pixbuf));
  3381.             }
  3382.           else if (vertical_stripes)
  3383.             {
  3384.               pixbuf = replicate_rows (temp_pixbuf, 0, 0, width, height);
  3385.               g_object_unref (G_OBJECT (temp_pixbuf));
  3386.             }
  3387.           else
  3388.             {
  3389.               pixbuf = temp_pixbuf;
  3390.             }
  3391.         }
  3392.     }
  3393.  
  3394.   if (pixbuf)
  3395.     pixbuf = apply_alpha (pixbuf, alpha_spec, pixbuf == src);
  3396.  
  3397.   return pixbuf;
  3398. }
  3399.  
  3400. static GdkPixbuf*
  3401. draw_op_as_pixbuf (const MetaDrawOp    *op,
  3402.                    GtkStyleContext     *context,
  3403.                    const MetaDrawInfo  *info,
  3404.                    int                  width,
  3405.                    int                  height)
  3406. {
  3407.   /* Try to get the op as a pixbuf, assuming w/h in the op
  3408.    * matches the width/height passed in. return NULL
  3409.    * if the op can't be converted to an equivalent pixbuf.
  3410.    */
  3411.   GdkPixbuf *pixbuf;
  3412.  
  3413.   pixbuf = NULL;
  3414.  
  3415.   switch (op->type)
  3416.     {
  3417.     case META_DRAW_LINE:
  3418.       break;
  3419.  
  3420.     case META_DRAW_RECTANGLE:
  3421.       if (op->data.rectangle.filled)
  3422.         {
  3423.           GdkRGBA color;
  3424.  
  3425.           meta_color_spec_render (op->data.rectangle.color_spec,
  3426.                                   context,
  3427.                                   &color);
  3428.  
  3429.           pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
  3430.                                    FALSE,
  3431.                                    8, width, height);
  3432.  
  3433.           gdk_pixbuf_fill (pixbuf, GDK_COLOR_RGBA (color));
  3434.         }
  3435.       break;
  3436.  
  3437.     case META_DRAW_ARC:
  3438.       break;
  3439.  
  3440.     case META_DRAW_CLIP:
  3441.       break;
  3442.      
  3443.     case META_DRAW_TINT:
  3444.       {
  3445.         GdkRGBA color;
  3446.         guint32 rgba;
  3447.         gboolean has_alpha;
  3448.  
  3449.         meta_color_spec_render (op->data.rectangle.color_spec,
  3450.                                 context,
  3451.                                 &color);
  3452.  
  3453.         has_alpha =
  3454.           op->data.tint.alpha_spec &&
  3455.           (op->data.tint.alpha_spec->n_alphas > 1 ||
  3456.            op->data.tint.alpha_spec->alphas[0] != 0xff);
  3457.        
  3458.         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
  3459.                                  has_alpha,
  3460.                                  8, width, height);
  3461.  
  3462.         if (!has_alpha)
  3463.           {
  3464.             rgba = GDK_COLOR_RGBA (color);
  3465.            
  3466.             gdk_pixbuf_fill (pixbuf, rgba);
  3467.           }
  3468.         else if (op->data.tint.alpha_spec->n_alphas == 1)
  3469.           {
  3470.             rgba = GDK_COLOR_RGBA (color);
  3471.             rgba &= ~0xff;
  3472.             rgba |= op->data.tint.alpha_spec->alphas[0];
  3473.            
  3474.             gdk_pixbuf_fill (pixbuf, rgba);
  3475.           }
  3476.         else
  3477.           {
  3478.             rgba = GDK_COLOR_RGBA (color);
  3479.            
  3480.             gdk_pixbuf_fill (pixbuf, rgba);
  3481.  
  3482.             meta_gradient_add_alpha (pixbuf,
  3483.                                      op->data.tint.alpha_spec->alphas,
  3484.                                      op->data.tint.alpha_spec->n_alphas,
  3485.                                      op->data.tint.alpha_spec->type);
  3486.           }
  3487.       }
  3488.       break;
  3489.  
  3490.     case META_DRAW_GRADIENT:
  3491.       {
  3492.         pixbuf = meta_gradient_spec_render (op->data.gradient.gradient_spec,
  3493.                                             context, width, height);
  3494.  
  3495.         pixbuf = apply_alpha (pixbuf,
  3496.                               op->data.gradient.alpha_spec,
  3497.                               FALSE);
  3498.       }
  3499.       break;
  3500.  
  3501.      
  3502.     case META_DRAW_IMAGE:
  3503.       {
  3504.     if (op->data.image.colorize_spec)
  3505.       {
  3506.         GdkRGBA color;
  3507.  
  3508.             meta_color_spec_render (op->data.image.colorize_spec,
  3509.                                     context, &color);
  3510.            
  3511.             if (op->data.image.colorize_cache_pixbuf == NULL ||
  3512.                 op->data.image.colorize_cache_pixel != GDK_COLOR_RGB (color))
  3513.               {
  3514.                 if (op->data.image.colorize_cache_pixbuf)
  3515.                   g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
  3516.                
  3517.                 /* const cast here */
  3518.                 ((MetaDrawOp*)op)->data.image.colorize_cache_pixbuf =
  3519.                   colorize_pixbuf (op->data.image.pixbuf,
  3520.                                    &color);
  3521.                 ((MetaDrawOp*)op)->data.image.colorize_cache_pixel =
  3522.                   GDK_COLOR_RGB (color);
  3523.               }
  3524.            
  3525.             if (op->data.image.colorize_cache_pixbuf)
  3526.               {
  3527.                 pixbuf = scale_and_alpha_pixbuf (op->data.image.colorize_cache_pixbuf,
  3528.                                                  op->data.image.alpha_spec,
  3529.                                                  op->data.image.fill_type,
  3530.                                                  width, height,
  3531.                                                  op->data.image.vertical_stripes,
  3532.                                                  op->data.image.horizontal_stripes);
  3533.               }
  3534.       }
  3535.     else
  3536.       {
  3537.         pixbuf = scale_and_alpha_pixbuf (op->data.image.pixbuf,
  3538.                                              op->data.image.alpha_spec,
  3539.                                              op->data.image.fill_type,
  3540.                                              width, height,
  3541.                                              op->data.image.vertical_stripes,
  3542.                                              op->data.image.horizontal_stripes);
  3543.       }
  3544.         break;
  3545.       }
  3546.      
  3547.     case META_DRAW_GTK_ARROW:
  3548.     case META_DRAW_GTK_BOX:
  3549.     case META_DRAW_GTK_VLINE:
  3550.       break;
  3551.  
  3552.     case META_DRAW_ICON:
  3553.       if (info->mini_icon &&
  3554.           width <= gdk_pixbuf_get_width (info->mini_icon) &&
  3555.           height <= gdk_pixbuf_get_height (info->mini_icon))
  3556.         pixbuf = scale_and_alpha_pixbuf (info->mini_icon,
  3557.                                          op->data.icon.alpha_spec,
  3558.                                          op->data.icon.fill_type,
  3559.                                          width, height,
  3560.                                          FALSE, FALSE);
  3561.       else if (info->icon)
  3562.         pixbuf = scale_and_alpha_pixbuf (info->icon,
  3563.                                          op->data.icon.alpha_spec,
  3564.                                          op->data.icon.fill_type,
  3565.                                          width, height,
  3566.                                          FALSE, FALSE);
  3567.       break;
  3568.  
  3569.     case META_DRAW_TITLE:
  3570.       break;
  3571.  
  3572.     case META_DRAW_OP_LIST:
  3573.       break;
  3574.  
  3575.     case META_DRAW_TILE:
  3576.       break;
  3577.     }
  3578.  
  3579.   return pixbuf;
  3580. }
  3581.  
  3582. static void
  3583. fill_env (MetaPositionExprEnv *env,
  3584.           const MetaDrawInfo  *info,
  3585.           MetaRectangle        logical_region)
  3586. {
  3587.   /* FIXME this stuff could be raised into draw_op_list_draw() probably
  3588.    */
  3589.   env->rect = logical_region;
  3590.   env->object_width = -1;
  3591.   env->object_height = -1;
  3592.   if (info->fgeom)
  3593.     {
  3594.       env->left_width = info->fgeom->borders.visible.left;
  3595.       env->right_width = info->fgeom->borders.visible.right;
  3596.       env->top_height = info->fgeom->borders.visible.top;
  3597.       env->bottom_height = info->fgeom->borders.visible.bottom;
  3598.       env->frame_x_center = info->fgeom->width / 2 - logical_region.x;
  3599.       env->frame_y_center = info->fgeom->height / 2 - logical_region.y;
  3600.     }
  3601.   else
  3602.     {
  3603.       env->left_width = 0;
  3604.       env->right_width = 0;
  3605.       env->top_height = 0;
  3606.       env->bottom_height = 0;
  3607.       env->frame_x_center = 0;
  3608.       env->frame_y_center = 0;
  3609.     }
  3610.  
  3611.   env->mini_icon_width = info->mini_icon ? gdk_pixbuf_get_width (info->mini_icon) : 0;
  3612.   env->mini_icon_height = info->mini_icon ? gdk_pixbuf_get_height (info->mini_icon) : 0;
  3613.   env->icon_width = info->icon ? gdk_pixbuf_get_width (info->icon) : 0;
  3614.   env->icon_height = info->icon ? gdk_pixbuf_get_height (info->icon) : 0;
  3615.  
  3616.   env->title_width = info->title_layout_width;
  3617.   env->title_height = info->title_layout_height;
  3618.   env->theme = meta_current_theme;
  3619. }
  3620.  
  3621.  
  3622. /* This code was originally rendering anti-aliased using X primitives, and
  3623.  * now has been switched to draw anti-aliased using cairo. In general, the
  3624.  * closest correspondence between X rendering and cairo rendering is given
  3625.  * by offsetting the geometry by 0.5 pixels in both directions before rendering
  3626.  * with cairo. This is because X samples at the upper left corner of the
  3627.  * pixel while cairo averages over the entire pixel. However, in the cases
  3628.  * where the X rendering was an exact rectangle with no "jaggies"
  3629.  * we need to be a bit careful about applying the offset. We want to produce
  3630.  * the exact same pixel-aligned rectangle, rather than a rectangle with
  3631.  * fuzz around the edges.
  3632.  */
  3633. static void
  3634. meta_draw_op_draw_with_env (const MetaDrawOp    *op,
  3635.                             GtkStyleContext     *style_gtk,
  3636.                             GtkWidget           *widget,
  3637.                             cairo_t             *cr,
  3638.                             const MetaDrawInfo  *info,
  3639.                             MetaRectangle        rect,
  3640.                             MetaPositionExprEnv *env)
  3641. {
  3642.   GdkRGBA color;
  3643.  
  3644.   cairo_save (cr);
  3645.   gtk_style_context_save (style_gtk);
  3646.  
  3647.   cairo_set_line_width (cr, 1.0);
  3648.  
  3649.   switch (op->type)
  3650.     {
  3651.     case META_DRAW_LINE:
  3652.       {
  3653.         int x1, x2, y1, y2;
  3654.  
  3655.         meta_color_spec_render (op->data.line.color_spec, style_gtk, &color);
  3656.         gdk_cairo_set_source_rgba (cr, &color);
  3657.  
  3658.         if (op->data.line.width > 0)
  3659.           cairo_set_line_width (cr, op->data.line.width);
  3660.  
  3661.         if (op->data.line.dash_on_length > 0 &&
  3662.             op->data.line.dash_off_length > 0)
  3663.           {
  3664.             double dash_list[2];
  3665.             dash_list[0] = op->data.line.dash_on_length;
  3666.             dash_list[1] = op->data.line.dash_off_length;
  3667.             cairo_set_dash (cr, dash_list, 2, 0);
  3668.           }
  3669.  
  3670.         x1 = parse_x_position_unchecked (op->data.line.x1, env);
  3671.         y1 = parse_y_position_unchecked (op->data.line.y1, env);
  3672.  
  3673.         if (!op->data.line.x2 &&
  3674.             !op->data.line.y2 &&
  3675.             op->data.line.width==0)
  3676.           {
  3677.             cairo_rectangle (cr, x1, y1, 1, 1);
  3678.             cairo_fill (cr);
  3679.           }
  3680.         else
  3681.           {
  3682.             if (op->data.line.x2)
  3683.               x2 = parse_x_position_unchecked (op->data.line.x2, env);
  3684.             else
  3685.               x2 = x1;
  3686.  
  3687.             if (op->data.line.y2)
  3688.               y2 = parse_y_position_unchecked (op->data.line.y2, env);
  3689.             else
  3690.               y2 = y1;
  3691.  
  3692.             /* This is one of the cases where we are matching the exact
  3693.              * pixel aligned rectangle produced by X; for zero-width lines
  3694.              * the generic algorithm produces the right result so we don't
  3695.              * need to handle them here.
  3696.              */
  3697.             if ((y1 == y2 || x1 == x2) && op->data.line.width != 0)
  3698.               {
  3699.                 double offset = op->data.line.width % 2 ? .5 : 0;
  3700.  
  3701.                 if (y1 == y2)
  3702.                   {
  3703.                     cairo_move_to (cr, x1, y1 + offset);
  3704.                     cairo_line_to (cr, x2, y2 + offset);
  3705.                   }
  3706.                 else
  3707.                   {
  3708.                     cairo_move_to (cr, x1 + offset, y1);
  3709.                     cairo_line_to (cr, x2 + offset, y2);
  3710.                   }
  3711.               }
  3712.             else
  3713.               {
  3714.                 /* zero-width lines include both end-points in X, unlike wide lines */
  3715.                 if (op->data.line.width == 0)
  3716.                   cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
  3717.  
  3718.                 cairo_move_to (cr, x1 + .5, y1 + .5);
  3719.                 cairo_line_to (cr, x2 + .5, y2 + .5);
  3720.               }
  3721.             cairo_stroke (cr);
  3722.           }
  3723.       }
  3724.       break;
  3725.  
  3726.     case META_DRAW_RECTANGLE:
  3727.       {
  3728.         int rx, ry, rwidth, rheight;
  3729.  
  3730.         meta_color_spec_render (op->data.rectangle.color_spec,
  3731.                                 style_gtk, &color);
  3732.         gdk_cairo_set_source_rgba (cr, &color);
  3733.  
  3734.         rx = parse_x_position_unchecked (op->data.rectangle.x, env);
  3735.         ry = parse_y_position_unchecked (op->data.rectangle.y, env);
  3736.         rwidth = parse_size_unchecked (op->data.rectangle.width, env);
  3737.         rheight = parse_size_unchecked (op->data.rectangle.height, env);
  3738.  
  3739.         /* Filled and stroked rectangles are the other cases
  3740.          * we pixel-align to X rasterization
  3741.          */
  3742.         if (op->data.rectangle.filled)
  3743.           {
  3744.             cairo_rectangle (cr, rx, ry, rwidth, rheight);
  3745.             cairo_fill (cr);
  3746.           }
  3747.         else
  3748.           {
  3749.             cairo_rectangle (cr, rx + .5, ry + .5, rwidth, rheight);
  3750.             cairo_stroke (cr);
  3751.           }
  3752.       }
  3753.       break;
  3754.  
  3755.     case META_DRAW_ARC:
  3756.       {
  3757.         int rx, ry, rwidth, rheight;
  3758.         double start_angle, end_angle;
  3759.         double center_x, center_y;
  3760.  
  3761.         meta_color_spec_render (op->data.arc.color_spec, style_gtk, &color);
  3762.         gdk_cairo_set_source_rgba (cr, &color);
  3763.  
  3764.         rx = parse_x_position_unchecked (op->data.arc.x, env);
  3765.         ry = parse_y_position_unchecked (op->data.arc.y, env);
  3766.         rwidth = parse_size_unchecked (op->data.arc.width, env);
  3767.         rheight = parse_size_unchecked (op->data.arc.height, env);
  3768.  
  3769.         start_angle = op->data.arc.start_angle * (M_PI / 180.)
  3770.                       - (.5 * M_PI); /* start at 12 instead of 3 oclock */
  3771.         end_angle = start_angle + op->data.arc.extent_angle * (M_PI / 180.);
  3772.         center_x = rx + (double)rwidth / 2. + .5;
  3773.         center_y = ry + (double)rheight / 2. + .5;
  3774.  
  3775.         cairo_save (cr);
  3776.  
  3777.         cairo_translate (cr, center_x, center_y);
  3778.         cairo_scale (cr, (double)rwidth / 2., (double)rheight / 2.);
  3779.  
  3780.         if (op->data.arc.extent_angle >= 0)
  3781.           cairo_arc (cr, 0, 0, 1, start_angle, end_angle);
  3782.         else
  3783.           cairo_arc_negative (cr, 0, 0, 1, start_angle, end_angle);
  3784.  
  3785.         cairo_restore (cr);
  3786.  
  3787.         if (op->data.arc.filled)
  3788.           {
  3789.             cairo_line_to (cr, center_x, center_y);
  3790.             cairo_fill (cr);
  3791.           }
  3792.         else
  3793.           cairo_stroke (cr);
  3794.       }
  3795.       break;
  3796.  
  3797.     case META_DRAW_CLIP:
  3798.       break;
  3799.      
  3800.     case META_DRAW_TINT:
  3801.       {
  3802.         int rx, ry, rwidth, rheight;
  3803.         gboolean needs_alpha;
  3804.        
  3805.         needs_alpha = op->data.tint.alpha_spec &&
  3806.           (op->data.tint.alpha_spec->n_alphas > 1 ||
  3807.            op->data.tint.alpha_spec->alphas[0] != 0xff);
  3808.        
  3809.         rx = parse_x_position_unchecked (op->data.tint.x, env);
  3810.         ry = parse_y_position_unchecked (op->data.tint.y, env);
  3811.         rwidth = parse_size_unchecked (op->data.tint.width, env);
  3812.         rheight = parse_size_unchecked (op->data.tint.height, env);
  3813.  
  3814.         if (!needs_alpha)
  3815.           {
  3816.             meta_color_spec_render (op->data.tint.color_spec,
  3817.                                     style_gtk, &color);
  3818.             gdk_cairo_set_source_rgba (cr, &color);
  3819.  
  3820.             cairo_rectangle (cr, rx, ry, rwidth, rheight);
  3821.             cairo_fill (cr);
  3822.           }
  3823.         else
  3824.           {
  3825.             GdkPixbuf *pixbuf;
  3826.  
  3827.             pixbuf = draw_op_as_pixbuf (op, style_gtk, info,
  3828.                                         rwidth, rheight);
  3829.  
  3830.             if (pixbuf)
  3831.               {
  3832.                 gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
  3833.                 cairo_paint (cr);
  3834.  
  3835.                 g_object_unref (G_OBJECT (pixbuf));
  3836.               }
  3837.           }
  3838.       }
  3839.       break;
  3840.  
  3841.     case META_DRAW_GRADIENT:
  3842.       {
  3843.         int rx, ry, rwidth, rheight;
  3844.         GdkPixbuf *pixbuf;
  3845.  
  3846.         rx = parse_x_position_unchecked (op->data.gradient.x, env);
  3847.         ry = parse_y_position_unchecked (op->data.gradient.y, env);
  3848.         rwidth = parse_size_unchecked (op->data.gradient.width, env);
  3849.         rheight = parse_size_unchecked (op->data.gradient.height, env);
  3850.  
  3851.         pixbuf = draw_op_as_pixbuf (op, style_gtk, info,
  3852.                                     rwidth, rheight);
  3853.  
  3854.         if (pixbuf)
  3855.           {
  3856.             gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
  3857.             cairo_paint (cr);
  3858.  
  3859.             g_object_unref (G_OBJECT (pixbuf));
  3860.           }
  3861.       }
  3862.       break;
  3863.  
  3864.     case META_DRAW_IMAGE:
  3865.       {
  3866.         int rx, ry, rwidth, rheight;
  3867.         GdkPixbuf *pixbuf;
  3868.  
  3869.         if (op->data.image.pixbuf)
  3870.           {
  3871.             env->object_width = gdk_pixbuf_get_width (op->data.image.pixbuf);
  3872.             env->object_height = gdk_pixbuf_get_height (op->data.image.pixbuf);
  3873.           }
  3874.  
  3875.         rwidth = parse_size_unchecked (op->data.image.width, env);
  3876.         rheight = parse_size_unchecked (op->data.image.height, env);
  3877.        
  3878.         pixbuf = draw_op_as_pixbuf (op, style_gtk, info,
  3879.                                     rwidth, rheight);
  3880.  
  3881.         if (pixbuf)
  3882.           {
  3883.             rx = parse_x_position_unchecked (op->data.image.x, env);
  3884.             ry = parse_y_position_unchecked (op->data.image.y, env);
  3885.  
  3886.             gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
  3887.             cairo_paint (cr);
  3888.  
  3889.             g_object_unref (G_OBJECT (pixbuf));
  3890.           }
  3891.       }
  3892.       break;
  3893.  
  3894.     case META_DRAW_GTK_ARROW:
  3895.       {
  3896.         int rx, ry, rwidth, rheight;
  3897.         double angle = 0, size;
  3898.  
  3899.         rx = parse_x_position_unchecked (op->data.gtk_arrow.x, env);
  3900.         ry = parse_y_position_unchecked (op->data.gtk_arrow.y, env);
  3901.         rwidth = parse_size_unchecked (op->data.gtk_arrow.width, env);
  3902.         rheight = parse_size_unchecked (op->data.gtk_arrow.height, env);
  3903.  
  3904.         size = MAX(rwidth, rheight);
  3905.  
  3906.         switch (op->data.gtk_arrow.arrow)
  3907.           {
  3908.           case GTK_ARROW_UP:
  3909.             angle = 0;
  3910.             break;
  3911.           case GTK_ARROW_RIGHT:
  3912.             angle = M_PI / 2;
  3913.             break;
  3914.           case GTK_ARROW_DOWN:
  3915.             angle = M_PI;
  3916.             break;
  3917.           case GTK_ARROW_LEFT:
  3918.             angle = 3 * M_PI / 2;
  3919.             break;
  3920.           case GTK_ARROW_NONE:
  3921.             return;
  3922.           }
  3923.  
  3924.         gtk_style_context_set_state (style_gtk, op->data.gtk_arrow.state);
  3925.         gtk_render_arrow (style_gtk, cr, angle, rx, ry, size);
  3926.       }
  3927.       break;
  3928.  
  3929.     case META_DRAW_GTK_BOX:
  3930.       {
  3931.         int rx, ry, rwidth, rheight;
  3932.  
  3933.         rx = parse_x_position_unchecked (op->data.gtk_box.x, env);
  3934.         ry = parse_y_position_unchecked (op->data.gtk_box.y, env);
  3935.         rwidth = parse_size_unchecked (op->data.gtk_box.width, env);
  3936.         rheight = parse_size_unchecked (op->data.gtk_box.height, env);
  3937.  
  3938.         gtk_style_context_set_state (style_gtk, op->data.gtk_box.state);
  3939.         gtk_render_background (style_gtk, cr, rx, ry, rwidth, rheight);
  3940.         gtk_render_frame (style_gtk, cr, rx, ry, rwidth, rheight);
  3941.       }
  3942.       break;
  3943.  
  3944.     case META_DRAW_GTK_VLINE:
  3945.       {
  3946.         int rx, ry1, ry2;
  3947.  
  3948.         rx = parse_x_position_unchecked (op->data.gtk_vline.x, env);
  3949.         ry1 = parse_y_position_unchecked (op->data.gtk_vline.y1, env);
  3950.         ry2 = parse_y_position_unchecked (op->data.gtk_vline.y2, env);
  3951.        
  3952.         gtk_style_context_set_state (style_gtk, op->data.gtk_vline.state);
  3953.         gtk_render_line (style_gtk, cr, rx, ry1, rx, ry2);
  3954.       }
  3955.       break;
  3956.  
  3957.     case META_DRAW_ICON:
  3958.       {
  3959.         int rx, ry, rwidth, rheight;
  3960.         GdkPixbuf *pixbuf;
  3961.  
  3962.         rwidth = parse_size_unchecked (op->data.icon.width, env);
  3963.         rheight = parse_size_unchecked (op->data.icon.height, env);
  3964.        
  3965.         pixbuf = draw_op_as_pixbuf (op, style_gtk, info,
  3966.                                     rwidth, rheight);
  3967.  
  3968.         if (pixbuf)
  3969.           {
  3970.             rx = parse_x_position_unchecked (op->data.icon.x, env);
  3971.             ry = parse_y_position_unchecked (op->data.icon.y, env);
  3972.  
  3973.             gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
  3974.             cairo_paint (cr);
  3975.  
  3976.             g_object_unref (G_OBJECT (pixbuf));
  3977.           }
  3978.       }
  3979.       break;
  3980.  
  3981.     case META_DRAW_TITLE:
  3982.       if (info->title_layout)
  3983.         {
  3984.           int rx, ry;
  3985.           PangoRectangle ink_rect, logical_rect;
  3986.  
  3987.           meta_color_spec_render (op->data.title.color_spec,
  3988.                                   style_gtk, &color);
  3989.           gdk_cairo_set_source_rgba (cr, &color);
  3990.  
  3991.           rx = parse_x_position_unchecked (op->data.title.x, env);
  3992.           ry = parse_y_position_unchecked (op->data.title.y, env);
  3993.  
  3994.           if (op->data.title.ellipsize_width)
  3995.             {
  3996.               int ellipsize_width;
  3997.               int right_bearing;
  3998.  
  3999.               ellipsize_width = parse_x_position_unchecked (op->data.title.ellipsize_width, env);
  4000.               /* HACK: parse_x_position_unchecked adds in env->rect.x, subtract out again */
  4001.               ellipsize_width -= env->rect.x;
  4002.  
  4003.               pango_layout_set_width (info->title_layout, -1);
  4004.               pango_layout_get_pixel_extents (info->title_layout,
  4005.                                               &ink_rect, &logical_rect);
  4006.  
  4007.               /* Pango's idea of ellipsization is with respect to the logical rect.
  4008.                * correct for this, by reducing the ellipsization width by the overflow
  4009.                * of the un-ellipsized text on the right... it's always the visual
  4010.                * right we want regardless of bidi, since since the X we pass in to
  4011.                * cairo_move_to() is always the left edge of the line.
  4012.                */
  4013.               right_bearing = (ink_rect.x + ink_rect.width) - (logical_rect.x + logical_rect.width);
  4014.               right_bearing = MAX (right_bearing, 0);
  4015.  
  4016.               ellipsize_width -= right_bearing;
  4017.               ellipsize_width = MAX (ellipsize_width, 0);
  4018.  
  4019.               /* Only ellipsizing when necessary is a performance optimization -
  4020.                * pango_layout_set_width() will force a relayout if it isn't the
  4021.                * same as the current width of -1.
  4022.                */
  4023.               if (ellipsize_width < logical_rect.width)
  4024.                 pango_layout_set_width (info->title_layout, PANGO_SCALE * ellipsize_width);
  4025.             }
  4026.  
  4027.           cairo_move_to (cr, rx, ry);
  4028.           pango_cairo_show_layout (cr, info->title_layout);
  4029.  
  4030.           /* Remove any ellipsization we might have set; will short-circuit
  4031.            * if the width is already -1 */
  4032.           pango_layout_set_width (info->title_layout, -1);
  4033.         }
  4034.       break;
  4035.  
  4036.     case META_DRAW_OP_LIST:
  4037.       {
  4038.         MetaRectangle d_rect;
  4039.  
  4040.         d_rect.x = parse_x_position_unchecked (op->data.op_list.x, env);
  4041.         d_rect.y = parse_y_position_unchecked (op->data.op_list.y, env);
  4042.         d_rect.width = parse_size_unchecked (op->data.op_list.width, env);
  4043.         d_rect.height = parse_size_unchecked (op->data.op_list.height, env);
  4044.  
  4045.         meta_draw_op_list_draw_with_style (op->data.op_list.op_list,
  4046.                                            style_gtk, widget, cr, info,
  4047.                                 d_rect);
  4048.       }
  4049.       break;
  4050.  
  4051.     case META_DRAW_TILE:
  4052.       {
  4053.         int rx, ry, rwidth, rheight;
  4054.         int tile_xoffset, tile_yoffset;
  4055.         MetaRectangle tile;
  4056.        
  4057.         rx = parse_x_position_unchecked (op->data.tile.x, env);
  4058.         ry = parse_y_position_unchecked (op->data.tile.y, env);
  4059.         rwidth = parse_size_unchecked (op->data.tile.width, env);
  4060.         rheight = parse_size_unchecked (op->data.tile.height, env);
  4061.  
  4062.         cairo_save (cr);
  4063.  
  4064.         cairo_rectangle (cr, rx, ry, rwidth, rheight);
  4065.         cairo_clip (cr);
  4066.  
  4067.         tile_xoffset = parse_x_position_unchecked (op->data.tile.tile_xoffset, env);
  4068.         tile_yoffset = parse_y_position_unchecked (op->data.tile.tile_yoffset, env);
  4069.         /* tile offset should not include x/y */
  4070.         tile_xoffset -= rect.x;
  4071.         tile_yoffset -= rect.y;
  4072.        
  4073.         tile.width = parse_size_unchecked (op->data.tile.tile_width, env);
  4074.         tile.height = parse_size_unchecked (op->data.tile.tile_height, env);
  4075.  
  4076.         tile.x = rx - tile_xoffset;
  4077.    
  4078.         while (tile.x < (rx + rwidth))
  4079.           {
  4080.             tile.y = ry - tile_yoffset;
  4081.             while (tile.y < (ry + rheight))
  4082.               {
  4083.                 meta_draw_op_list_draw_with_style (op->data.tile.op_list,
  4084.                                                    style_gtk, widget, cr, info,
  4085.                                         tile);
  4086.  
  4087.                 tile.y += tile.height;
  4088.               }
  4089.  
  4090.             tile.x += tile.width;
  4091.           }
  4092.         cairo_restore (cr);
  4093.  
  4094.       }
  4095.       break;
  4096.     }
  4097.  
  4098.   cairo_restore (cr);
  4099.   gtk_style_context_restore (style_gtk);
  4100. }
  4101.  
  4102. void
  4103. meta_draw_op_draw_with_style (const MetaDrawOp    *op,
  4104.                               GtkStyleContext     *style_gtk,
  4105.                               GtkWidget           *widget,
  4106.                               cairo_t             *cr,
  4107.                               const MetaDrawInfo  *info,
  4108.                               MetaRectangle        logical_region)
  4109. {
  4110.   MetaPositionExprEnv env;
  4111.  
  4112.   fill_env (&env, info, logical_region);
  4113.  
  4114.   meta_draw_op_draw_with_env (op, style_gtk, widget, cr,
  4115.                               info, logical_region,
  4116.                               &env);
  4117.  
  4118. }
  4119.  
  4120. void
  4121. meta_draw_op_draw (const MetaDrawOp    *op,
  4122.                    GtkWidget           *widget,
  4123.                    cairo_t             *cr,
  4124.                    const MetaDrawInfo  *info,
  4125.                    MetaRectangle        logical_region)
  4126. {
  4127.   meta_draw_op_draw_with_style (op, gtk_widget_get_style_context (widget),
  4128.                                 widget, cr, info, logical_region);
  4129. }
  4130.  
  4131. /**
  4132.  * meta_draw_op_list_new: (skip)
  4133.  *
  4134.  */
  4135. MetaDrawOpList*
  4136. meta_draw_op_list_new (int n_preallocs)
  4137. {
  4138.   MetaDrawOpList *op_list;
  4139.  
  4140.   g_return_val_if_fail (n_preallocs >= 0, NULL);
  4141.  
  4142.   op_list = g_new (MetaDrawOpList, 1);
  4143.  
  4144.   op_list->refcount = 1;
  4145.   op_list->n_allocated = n_preallocs;
  4146.   op_list->ops = g_new (MetaDrawOp*, op_list->n_allocated);
  4147.   op_list->n_ops = 0;
  4148.  
  4149.   return op_list;
  4150. }
  4151.  
  4152. void
  4153. meta_draw_op_list_ref (MetaDrawOpList *op_list)
  4154. {
  4155.   g_return_if_fail (op_list != NULL);
  4156.  
  4157.   op_list->refcount += 1;
  4158. }
  4159.  
  4160. void
  4161. meta_draw_op_list_unref (MetaDrawOpList *op_list)
  4162. {
  4163.   g_return_if_fail (op_list != NULL);
  4164.   g_return_if_fail (op_list->refcount > 0);
  4165.  
  4166.   op_list->refcount -= 1;
  4167.  
  4168.   if (op_list->refcount == 0)
  4169.     {
  4170.       int i;
  4171.  
  4172.       for (i = 0; i < op_list->n_ops; i++)
  4173.         meta_draw_op_free (op_list->ops[i]);
  4174.  
  4175.       g_free (op_list->ops);
  4176.  
  4177.       DEBUG_FILL_STRUCT (op_list);
  4178.       g_free (op_list);
  4179.     }
  4180. }
  4181.  
  4182. void
  4183. meta_draw_op_list_draw_with_style  (const MetaDrawOpList *op_list,
  4184.                                     GtkStyleContext      *style_gtk,
  4185.                                     GtkWidget            *widget,
  4186.                                     cairo_t              *cr,
  4187.                                     const MetaDrawInfo   *info,
  4188.                                     MetaRectangle         rect)
  4189. {
  4190.   int i;
  4191.   MetaPositionExprEnv env;
  4192.  
  4193.   if (op_list->n_ops == 0)
  4194.     return;
  4195.  
  4196.   fill_env (&env, info, rect);
  4197.  
  4198.   /* FIXME this can be optimized, potentially a lot, by
  4199.    * compressing multiple ops when possible. For example,
  4200.    * anything convertible to a pixbuf can be composited
  4201.    * client-side, and putting a color tint over a pixbuf
  4202.    * can be done without creating the solid-color pixbuf.
  4203.    *
  4204.    * To implement this my plan is to have the idea of a
  4205.    * compiled draw op (with the string expressions already
  4206.    * evaluated), we make an array of those, and then fold
  4207.    * adjacent items when possible.
  4208.    */
  4209.  
  4210.   cairo_save (cr);
  4211.  
  4212.   for (i = 0; i < op_list->n_ops; i++)
  4213.     {
  4214.       MetaDrawOp *op = op_list->ops[i];
  4215.      
  4216.       if (op->type == META_DRAW_CLIP)
  4217.         {
  4218.           cairo_restore (cr);
  4219.  
  4220.           cairo_rectangle (cr,
  4221.                            parse_x_position_unchecked (op->data.clip.x, &env),
  4222.                            parse_y_position_unchecked (op->data.clip.y, &env),
  4223.                            parse_size_unchecked (op->data.clip.width, &env),
  4224.                            parse_size_unchecked (op->data.clip.height, &env));
  4225.           cairo_clip (cr);
  4226.          
  4227.           cairo_save (cr);
  4228.         }
  4229.       else if (gdk_cairo_get_clip_rectangle (cr, NULL))
  4230.         {
  4231.           meta_draw_op_draw_with_env (op,
  4232.                                       style_gtk, widget, cr, info,
  4233.                                       rect,
  4234.                                       &env);
  4235.         }
  4236.     }
  4237.  
  4238.   cairo_restore (cr);
  4239. }
  4240.  
  4241. void
  4242. meta_draw_op_list_draw  (const MetaDrawOpList *op_list,
  4243.                          GtkWidget            *widget,
  4244.                          cairo_t              *cr,
  4245.                          const MetaDrawInfo   *info,
  4246.                          MetaRectangle         rect)
  4247.  
  4248. {
  4249.   meta_draw_op_list_draw_with_style (op_list, gtk_widget_get_style_context (widget), widget,
  4250.                                      cr, info, rect);
  4251. }
  4252.  
  4253. void
  4254. meta_draw_op_list_append (MetaDrawOpList       *op_list,
  4255.                           MetaDrawOp           *op)
  4256. {
  4257.   if (op_list->n_ops == op_list->n_allocated)
  4258.     {
  4259.       op_list->n_allocated *= 2;
  4260.       op_list->ops = g_renew (MetaDrawOp*, op_list->ops, op_list->n_allocated);
  4261.     }
  4262.  
  4263.   op_list->ops[op_list->n_ops] = op;
  4264.   op_list->n_ops += 1;
  4265. }
  4266.  
  4267. gboolean
  4268. meta_draw_op_list_validate (MetaDrawOpList    *op_list,
  4269.                             GError           **error)
  4270. {
  4271.   g_return_val_if_fail (op_list != NULL, FALSE);
  4272.  
  4273.   /* empty lists are OK, nothing else to check really */
  4274.  
  4275.   return TRUE;
  4276. }
  4277.  
  4278. /* This is not done in validate, since we wouldn't know the name
  4279.  * of the list to report the error. It might be nice to
  4280.  * store names inside the list sometime.
  4281.  */
  4282. gboolean
  4283. meta_draw_op_list_contains (MetaDrawOpList    *op_list,
  4284.                             MetaDrawOpList    *child)
  4285. {
  4286.   int i;
  4287.  
  4288.   /* mmm, huge tree recursion */
  4289.  
  4290.   for (i = 0; i < op_list->n_ops; i++)
  4291.     {
  4292.       if (op_list->ops[i]->type == META_DRAW_OP_LIST)
  4293.         {
  4294.           if (op_list->ops[i]->data.op_list.op_list == child)
  4295.             return TRUE;
  4296.          
  4297.           if (meta_draw_op_list_contains (op_list->ops[i]->data.op_list.op_list,
  4298.                                           child))
  4299.             return TRUE;
  4300.         }
  4301.       else if (op_list->ops[i]->type == META_DRAW_TILE)
  4302.         {
  4303.           if (op_list->ops[i]->data.tile.op_list == child)
  4304.             return TRUE;
  4305.          
  4306.           if (meta_draw_op_list_contains (op_list->ops[i]->data.tile.op_list,
  4307.                                           child))
  4308.             return TRUE;
  4309.         }
  4310.     }
  4311.  
  4312.   return FALSE;
  4313. }
  4314.  
  4315. /**
  4316.  * Constructor for a MetaFrameStyle.
  4317.  *
  4318.  * \param parent  The parent style. Data not filled in here will be
  4319.  *                looked for in the parent style, and in its parent
  4320.  *                style, and so on.
  4321.  *
  4322.  * \return The newly-constructed style.
  4323.  */
  4324. MetaFrameStyle*
  4325. meta_frame_style_new (MetaFrameStyle *parent)
  4326. {
  4327.   MetaFrameStyle *style;
  4328.  
  4329.   style = g_new0 (MetaFrameStyle, 1);
  4330.  
  4331.   style->refcount = 1;
  4332.  
  4333.   /* Default alpha is fully opaque */
  4334.   style->window_background_alpha = 255;
  4335.  
  4336.   style->parent = parent;
  4337.   if (parent)
  4338.     meta_frame_style_ref (parent);
  4339.  
  4340.   return style;
  4341. }
  4342.  
  4343. /**
  4344.  * Increases the reference count of a frame style.
  4345.  * If the style is NULL, this is a no-op.
  4346.  *
  4347.  * \param style  The style.
  4348.  */
  4349. void
  4350. meta_frame_style_ref (MetaFrameStyle *style)
  4351. {
  4352.   g_return_if_fail (style != NULL);
  4353.  
  4354.   style->refcount += 1;
  4355. }
  4356.  
  4357. static void
  4358. free_button_ops (MetaDrawOpList *op_lists[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST])
  4359. {
  4360.   int i, j;
  4361.  
  4362.   for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
  4363.     for (j = 0; j < META_BUTTON_STATE_LAST; j++)
  4364.       if (op_lists[i][j])
  4365.         meta_draw_op_list_unref (op_lists[i][j]);
  4366. }
  4367.  
  4368. void
  4369. meta_frame_style_unref (MetaFrameStyle *style)
  4370. {
  4371.   g_return_if_fail (style != NULL);
  4372.   g_return_if_fail (style->refcount > 0);
  4373.  
  4374.   style->refcount -= 1;
  4375.  
  4376.   if (style->refcount == 0)
  4377.     {
  4378.       int i;
  4379.  
  4380.       free_button_ops (style->buttons);
  4381.  
  4382.       for (i = 0; i < META_FRAME_PIECE_LAST; i++)
  4383.         if (style->pieces[i])
  4384.           meta_draw_op_list_unref (style->pieces[i]);
  4385.  
  4386.       if (style->layout)
  4387.         meta_frame_layout_unref (style->layout);
  4388.  
  4389.       if (style->window_background_color)
  4390.         meta_color_spec_free (style->window_background_color);
  4391.  
  4392.       /* we hold a reference to any parent style */
  4393.       if (style->parent)
  4394.         meta_frame_style_unref (style->parent);
  4395.  
  4396.       DEBUG_FILL_STRUCT (style);
  4397.       g_free (style);
  4398.     }
  4399. }
  4400.  
  4401. static MetaButtonState
  4402. map_button_state (MetaButtonType           button_type,
  4403.                   const MetaFrameGeometry *fgeom,
  4404.                   int                      middle_bg_offset,
  4405.                   MetaButtonState          button_states[META_BUTTON_TYPE_LAST])
  4406. {
  4407.   MetaButtonFunction function = META_BUTTON_FUNCTION_LAST;
  4408.  
  4409.   switch (button_type)
  4410.     {
  4411.     /* First hande functions, which map directly */
  4412.     case META_BUTTON_TYPE_SHADE:
  4413.     case META_BUTTON_TYPE_ABOVE:
  4414.     case META_BUTTON_TYPE_STICK:
  4415.     case META_BUTTON_TYPE_UNSHADE:
  4416.     case META_BUTTON_TYPE_UNABOVE:
  4417.     case META_BUTTON_TYPE_UNSTICK:
  4418.     case META_BUTTON_TYPE_MENU:
  4419.     case META_BUTTON_TYPE_MINIMIZE:
  4420.     case META_BUTTON_TYPE_MAXIMIZE:
  4421.     case META_BUTTON_TYPE_CLOSE:
  4422.       return button_states[button_type];
  4423.  
  4424.     /* Map position buttons to the corresponding function */
  4425.     case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
  4426.     case META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND:
  4427.       if (fgeom->n_right_buttons > 0)
  4428.         function = fgeom->button_layout.right_buttons[0];
  4429.       break;
  4430.     case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
  4431.       if (fgeom->n_right_buttons > 0)
  4432.         function = fgeom->button_layout.right_buttons[fgeom->n_right_buttons - 1];
  4433.       break;
  4434.     case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
  4435.       if (middle_bg_offset + 1 < fgeom->n_right_buttons)
  4436.         function = fgeom->button_layout.right_buttons[middle_bg_offset + 1];
  4437.       break;
  4438.     case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
  4439.     case META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND:
  4440.       if (fgeom->n_left_buttons > 0)
  4441.         function = fgeom->button_layout.left_buttons[0];
  4442.       break;
  4443.     case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
  4444.       if (fgeom->n_left_buttons > 0)
  4445.         function = fgeom->button_layout.left_buttons[fgeom->n_left_buttons - 1];
  4446.       break;
  4447.     case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
  4448.       if (middle_bg_offset + 1 < fgeom->n_left_buttons)
  4449.         function = fgeom->button_layout.left_buttons[middle_bg_offset + 1];
  4450.       break;
  4451.     case META_BUTTON_TYPE_LAST:
  4452.       break;
  4453.     }
  4454.  
  4455.   if (function != META_BUTTON_FUNCTION_LAST)
  4456.     return button_states[map_button_function_to_type (function)];
  4457.  
  4458.   return META_BUTTON_STATE_LAST;
  4459. }
  4460.  
  4461. static MetaDrawOpList*
  4462. get_button (MetaFrameStyle *style,
  4463.             MetaButtonType  type,
  4464.             MetaButtonState state)
  4465. {
  4466.   MetaDrawOpList *op_list;
  4467.   MetaFrameStyle *parent;
  4468.  
  4469.   parent = style;
  4470.   op_list = NULL;
  4471.   while (parent && op_list == NULL)
  4472.     {
  4473.       op_list = parent->buttons[type][state];
  4474.       parent = parent->parent;
  4475.     }
  4476.  
  4477.   /* We fall back to the side buttons if we don't have
  4478.    * single button backgrounds, and to middle button
  4479.    * backgrounds if we don't have the ones on the sides
  4480.    */
  4481.  
  4482.   if (op_list == NULL &&
  4483.       type == META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND)
  4484.     return get_button (style, META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND, state);
  4485.  
  4486.   if (op_list == NULL &&
  4487.       type == META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND)
  4488.     return get_button (style, META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND, state);
  4489.  
  4490.   if (op_list == NULL &&
  4491.       (type == META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND ||
  4492.        type == META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND))
  4493.     return get_button (style, META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND,
  4494.                        state);
  4495.  
  4496.   if (op_list == NULL &&
  4497.       (type == META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND ||
  4498.        type == META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND))
  4499.     return get_button (style, META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND,
  4500.                        state);
  4501.  
  4502.   /* We fall back to normal if no prelight */
  4503.   if (op_list == NULL &&
  4504.       state == META_BUTTON_STATE_PRELIGHT)
  4505.     return get_button (style, type, META_BUTTON_STATE_NORMAL);
  4506.  
  4507.   return op_list;
  4508. }
  4509.  
  4510. gboolean
  4511. meta_frame_style_validate (MetaFrameStyle    *style,
  4512.                            guint              current_theme_version,
  4513.                            GError           **error)
  4514. {
  4515.   int i, j;
  4516.  
  4517.   g_return_val_if_fail (style != NULL, FALSE);
  4518.   g_return_val_if_fail (style->layout != NULL, FALSE);
  4519.  
  4520.   for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
  4521.     {
  4522.       /* for now the "positional" buttons are optional */
  4523.       if (i >= META_BUTTON_TYPE_CLOSE)
  4524.         {
  4525.           for (j = 0; j < META_BUTTON_STATE_LAST; j++)
  4526.             {
  4527.               if (get_button (style, i, j) == NULL &&
  4528.                   meta_theme_earliest_version_with_button (i) <= current_theme_version
  4529.                   )
  4530.                 {
  4531.                   g_set_error (error, META_THEME_ERROR,
  4532.                                META_THEME_ERROR_FAILED,
  4533.                                _("<button function=\"%s\" state=\"%s\" draw_ops=\"whatever\"/> must be specified for this frame style"),
  4534.                                meta_button_type_to_string (i),
  4535.                                meta_button_state_to_string (j));
  4536.                   return FALSE;
  4537.                 }
  4538.             }
  4539.         }
  4540.     }
  4541.  
  4542.   return TRUE;
  4543. }
  4544.  
  4545. static void
  4546. button_rect (MetaButtonType           type,
  4547.              const MetaFrameGeometry *fgeom,
  4548.              int                      middle_background_offset,
  4549.              GdkRectangle            *rect)
  4550. {
  4551.   switch (type)
  4552.     {
  4553.     case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
  4554.       *rect = fgeom->left_left_background;
  4555.       break;
  4556.  
  4557.     case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
  4558.       *rect = fgeom->left_middle_backgrounds[middle_background_offset];
  4559.       break;
  4560.      
  4561.     case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
  4562.       *rect = fgeom->left_right_background;
  4563.       break;
  4564.  
  4565.     case META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND:
  4566.       *rect = fgeom->left_single_background;
  4567.       break;
  4568.      
  4569.     case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
  4570.       *rect = fgeom->right_left_background;
  4571.       break;
  4572.      
  4573.     case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
  4574.       *rect = fgeom->right_middle_backgrounds[middle_background_offset];
  4575.       break;
  4576.      
  4577.     case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
  4578.       *rect = fgeom->right_right_background;
  4579.       break;
  4580.  
  4581.     case META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND:
  4582.       *rect = fgeom->right_single_background;
  4583.       break;
  4584.      
  4585.     case META_BUTTON_TYPE_CLOSE:
  4586.       *rect = fgeom->close_rect.visible;
  4587.       break;
  4588.  
  4589.     case META_BUTTON_TYPE_SHADE:
  4590.       *rect = fgeom->shade_rect.visible;
  4591.       break;
  4592.  
  4593.     case META_BUTTON_TYPE_UNSHADE:
  4594.       *rect = fgeom->unshade_rect.visible;
  4595.       break;
  4596.  
  4597.     case META_BUTTON_TYPE_ABOVE:
  4598.       *rect = fgeom->above_rect.visible;
  4599.       break;
  4600.  
  4601.     case META_BUTTON_TYPE_UNABOVE:
  4602.       *rect = fgeom->unabove_rect.visible;
  4603.       break;
  4604.  
  4605.     case META_BUTTON_TYPE_STICK:
  4606.       *rect = fgeom->stick_rect.visible;
  4607.       break;
  4608.  
  4609.     case META_BUTTON_TYPE_UNSTICK:
  4610.       *rect = fgeom->unstick_rect.visible;
  4611.       break;
  4612.  
  4613.     case META_BUTTON_TYPE_MAXIMIZE:
  4614.       *rect = fgeom->max_rect.visible;
  4615.       break;
  4616.  
  4617.     case META_BUTTON_TYPE_MINIMIZE:
  4618.       *rect = fgeom->min_rect.visible;
  4619.       break;
  4620.  
  4621.     case META_BUTTON_TYPE_MENU:
  4622.       *rect = fgeom->menu_rect.visible;
  4623.       break;
  4624.      
  4625.     case META_BUTTON_TYPE_LAST:
  4626.       g_assert_not_reached ();
  4627.       break;
  4628.     }
  4629. }
  4630.  
  4631. void
  4632. meta_frame_style_draw_with_style (MetaFrameStyle          *style,
  4633.                                   GtkStyleContext         *style_gtk,
  4634.                                   GtkWidget               *widget,
  4635.                                   cairo_t                 *cr,
  4636.                                   const MetaFrameGeometry *fgeom,
  4637.                                   int                      client_width,
  4638.                                   int                      client_height,
  4639.                                   PangoLayout             *title_layout,
  4640.                                   int                      text_height,
  4641.                                   MetaButtonState          button_states[META_BUTTON_TYPE_LAST],
  4642.                                   GdkPixbuf               *mini_icon,
  4643.                                   GdkPixbuf               *icon)
  4644. {
  4645.   int i, j;
  4646.   GdkRectangle visible_rect;
  4647.   GdkRectangle titlebar_rect;
  4648.   GdkRectangle left_titlebar_edge;
  4649.   GdkRectangle right_titlebar_edge;
  4650.   GdkRectangle bottom_titlebar_edge;
  4651.   GdkRectangle top_titlebar_edge;
  4652.   GdkRectangle left_edge, right_edge, bottom_edge;
  4653.   PangoRectangle logical_rect;
  4654.   MetaDrawInfo draw_info;
  4655.   const MetaFrameBorders *borders;
  4656.  
  4657.   borders = &fgeom->borders;
  4658.  
  4659.   visible_rect.x = borders->invisible.left;
  4660.   visible_rect.y = borders->invisible.top;
  4661.   visible_rect.width = fgeom->width - borders->invisible.left - borders->invisible.right;
  4662.   visible_rect.height = fgeom->height - borders->invisible.top - borders->invisible.bottom;
  4663.  
  4664.   titlebar_rect.x = visible_rect.x;
  4665.   titlebar_rect.y = visible_rect.y;
  4666.   titlebar_rect.width = visible_rect.width;
  4667.   titlebar_rect.height = borders->visible.top;
  4668.  
  4669.   left_titlebar_edge.x = titlebar_rect.x;
  4670.   left_titlebar_edge.y = titlebar_rect.y + fgeom->top_titlebar_edge;
  4671.   left_titlebar_edge.width = fgeom->left_titlebar_edge;
  4672.   left_titlebar_edge.height = titlebar_rect.height - fgeom->top_titlebar_edge - fgeom->bottom_titlebar_edge;
  4673.  
  4674.   right_titlebar_edge.y = left_titlebar_edge.y;
  4675.   right_titlebar_edge.height = left_titlebar_edge.height;
  4676.   right_titlebar_edge.width = fgeom->right_titlebar_edge;
  4677.   right_titlebar_edge.x = titlebar_rect.x + titlebar_rect.width - right_titlebar_edge.width;
  4678.  
  4679.   top_titlebar_edge.x = titlebar_rect.x;
  4680.   top_titlebar_edge.y = titlebar_rect.y;
  4681.   top_titlebar_edge.width = titlebar_rect.width;
  4682.   top_titlebar_edge.height = fgeom->top_titlebar_edge;
  4683.  
  4684.   bottom_titlebar_edge.x = titlebar_rect.x;
  4685.   bottom_titlebar_edge.width = titlebar_rect.width;
  4686.   bottom_titlebar_edge.height = fgeom->bottom_titlebar_edge;
  4687.   bottom_titlebar_edge.y = titlebar_rect.y + titlebar_rect.height - bottom_titlebar_edge.height;
  4688.  
  4689.   left_edge.x = visible_rect.x;
  4690.   left_edge.y = visible_rect.y + borders->visible.top;
  4691.   left_edge.width = borders->visible.left;
  4692.   left_edge.height = visible_rect.height - borders->visible.top - borders->visible.bottom;
  4693.  
  4694.   right_edge.x = visible_rect.x + visible_rect.width - borders->visible.right;
  4695.   right_edge.y = visible_rect.y + borders->visible.top;
  4696.   right_edge.width = borders->visible.right;
  4697.   right_edge.height = visible_rect.height - borders->visible.top - borders->visible.bottom;
  4698.  
  4699.   bottom_edge.x = visible_rect.x;
  4700.   bottom_edge.y = visible_rect.y + visible_rect.height - borders->visible.bottom;
  4701.   bottom_edge.width = visible_rect.width;
  4702.   bottom_edge.height = borders->visible.bottom;
  4703.  
  4704.   if (title_layout)
  4705.     pango_layout_get_pixel_extents (title_layout,
  4706.                                     NULL, &logical_rect);
  4707.  
  4708.   draw_info.mini_icon = mini_icon;
  4709.   draw_info.icon = icon;
  4710.   draw_info.title_layout = title_layout;
  4711.   draw_info.title_layout_width = title_layout ? logical_rect.width : 0;
  4712.   draw_info.title_layout_height = title_layout ? logical_rect.height : 0;
  4713.   draw_info.fgeom = fgeom;
  4714.  
  4715.   /* The enum is in the order the pieces should be rendered. */
  4716.   i = 0;
  4717.   while (i < META_FRAME_PIECE_LAST)
  4718.     {
  4719.       GdkRectangle rect;
  4720.      
  4721.       switch ((MetaFramePiece) i)
  4722.         {
  4723.         case META_FRAME_PIECE_ENTIRE_BACKGROUND:
  4724.           rect = visible_rect;
  4725.           break;
  4726.  
  4727.         case META_FRAME_PIECE_TITLEBAR:
  4728.           rect = titlebar_rect;
  4729.           break;
  4730.  
  4731.         case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE:
  4732.           rect = left_titlebar_edge;
  4733.           break;
  4734.  
  4735.         case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE:
  4736.           rect = right_titlebar_edge;
  4737.           break;
  4738.  
  4739.         case META_FRAME_PIECE_TOP_TITLEBAR_EDGE:
  4740.           rect = top_titlebar_edge;
  4741.           break;
  4742.  
  4743.         case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE:
  4744.           rect = bottom_titlebar_edge;
  4745.           break;
  4746.  
  4747.         case META_FRAME_PIECE_TITLEBAR_MIDDLE:
  4748.           rect.x = left_titlebar_edge.x + left_titlebar_edge.width;
  4749.           rect.y = top_titlebar_edge.y + top_titlebar_edge.height;
  4750.           rect.width = titlebar_rect.width - left_titlebar_edge.width -
  4751.             right_titlebar_edge.width;
  4752.           rect.height = titlebar_rect.height - top_titlebar_edge.height - bottom_titlebar_edge.height;
  4753.           break;
  4754.  
  4755.         case META_FRAME_PIECE_TITLE:
  4756.           rect = fgeom->title_rect;
  4757.           break;
  4758.  
  4759.         case META_FRAME_PIECE_LEFT_EDGE:
  4760.           rect = left_edge;
  4761.           break;
  4762.  
  4763.         case META_FRAME_PIECE_RIGHT_EDGE:
  4764.           rect = right_edge;
  4765.           break;
  4766.  
  4767.         case META_FRAME_PIECE_BOTTOM_EDGE:
  4768.           rect = bottom_edge;
  4769.           break;
  4770.  
  4771.         case META_FRAME_PIECE_OVERLAY:
  4772.           rect = visible_rect;
  4773.           break;
  4774.  
  4775.         case META_FRAME_PIECE_LAST:
  4776.           g_assert_not_reached ();
  4777.           break;
  4778.         }
  4779.  
  4780.       cairo_save (cr);
  4781.  
  4782.       gdk_cairo_rectangle (cr, &rect);
  4783.       cairo_clip (cr);
  4784.  
  4785.       if (gdk_cairo_get_clip_rectangle (cr, NULL))
  4786.         {
  4787.           MetaDrawOpList *op_list;
  4788.           MetaFrameStyle *parent;
  4789.  
  4790.           parent = style;
  4791.           op_list = NULL;
  4792.           while (parent && op_list == NULL)
  4793.             {
  4794.               op_list = parent->pieces[i];
  4795.               parent = parent->parent;
  4796.             }
  4797.  
  4798.           if (op_list)
  4799.             {
  4800.               MetaRectangle m_rect;
  4801.               m_rect = meta_rect (rect.x, rect.y, rect.width, rect.height);
  4802.               meta_draw_op_list_draw_with_style (op_list,
  4803.                                                  style_gtk,
  4804.                                                  widget,
  4805.                                                  cr,
  4806.                                                  &draw_info,
  4807.                                                  m_rect);
  4808.             }
  4809.         }
  4810.  
  4811.       cairo_restore (cr);
  4812.  
  4813.       /* Draw buttons just before overlay */
  4814.       if ((i + 1) == META_FRAME_PIECE_OVERLAY)
  4815.         {
  4816.           MetaDrawOpList *op_list;
  4817.           int middle_bg_offset;
  4818.  
  4819.           middle_bg_offset = 0;
  4820.           j = 0;
  4821.           while (j < META_BUTTON_TYPE_LAST)
  4822.             {
  4823.               MetaButtonState button_state;
  4824.  
  4825.               button_rect (j, fgeom, middle_bg_offset, &rect);
  4826.              
  4827.               button_state = map_button_state (j, fgeom, middle_bg_offset, button_states);
  4828.  
  4829.               op_list = get_button (style, j, button_state);
  4830.              
  4831.               if (op_list)
  4832.                 {
  4833.                   cairo_save (cr);
  4834.                   gdk_cairo_rectangle (cr, &rect);
  4835.                   cairo_clip (cr);
  4836.  
  4837.                   if (gdk_cairo_get_clip_rectangle (cr, NULL))
  4838.                     {
  4839.                       MetaRectangle m_rect;
  4840.  
  4841.                       m_rect = meta_rect (rect.x, rect.y,
  4842.                                           rect.width, rect.height);
  4843.  
  4844.                       meta_draw_op_list_draw_with_style (op_list,
  4845.                                                          style_gtk,
  4846.                                                          widget,
  4847.                                                          cr,
  4848.                                                          &draw_info,
  4849.                                                          m_rect);
  4850.                     }
  4851.  
  4852.                   cairo_restore (cr);
  4853.                 }
  4854.  
  4855.               /* MIDDLE_BACKGROUND type may get drawn more than once */
  4856.               if ((j == META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND ||
  4857.                    j == META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND) &&
  4858.                   middle_bg_offset < MAX_MIDDLE_BACKGROUNDS)
  4859.                 {
  4860.                   ++middle_bg_offset;
  4861.                 }
  4862.               else
  4863.                 {
  4864.                   middle_bg_offset = 0;
  4865.                   ++j;
  4866.                 }
  4867.             }
  4868.         }
  4869.  
  4870.       ++i;
  4871.     }
  4872. }
  4873.  
  4874. void
  4875. meta_frame_style_draw (MetaFrameStyle          *style,
  4876.                        GtkWidget               *widget,
  4877.                        cairo_t                 *cr,
  4878.                        const MetaFrameGeometry *fgeom,
  4879.                        int                      client_width,
  4880.                        int                      client_height,
  4881.                        PangoLayout             *title_layout,
  4882.                        int                      text_height,
  4883.                        MetaButtonState          button_states[META_BUTTON_TYPE_LAST],
  4884.                        GdkPixbuf               *mini_icon,
  4885.                        GdkPixbuf               *icon)
  4886. {
  4887.   meta_frame_style_draw_with_style (style, gtk_widget_get_style_context (widget), widget,
  4888.                                     cr, fgeom, client_width, client_height,
  4889.                                     title_layout, text_height,
  4890.                                     button_states, mini_icon, icon);
  4891. }
  4892.  
  4893. MetaFrameStyleSet*
  4894. meta_frame_style_set_new (MetaFrameStyleSet *parent)
  4895. {
  4896.   MetaFrameStyleSet *style_set;
  4897.  
  4898.   style_set = g_new0 (MetaFrameStyleSet, 1);
  4899.  
  4900.   style_set->parent = parent;
  4901.   if (parent)
  4902.     meta_frame_style_set_ref (parent);
  4903.  
  4904.   style_set->refcount = 1;
  4905.  
  4906.   return style_set;
  4907. }
  4908.  
  4909. static void
  4910. free_focus_styles (MetaFrameStyle *focus_styles[META_FRAME_FOCUS_LAST])
  4911. {
  4912.   int i;
  4913.  
  4914.   for (i = 0; i < META_FRAME_FOCUS_LAST; i++)
  4915.     if (focus_styles[i])
  4916.       meta_frame_style_unref (focus_styles[i]);
  4917. }
  4918.  
  4919. void
  4920. meta_frame_style_set_ref (MetaFrameStyleSet *style_set)
  4921. {
  4922.   g_return_if_fail (style_set != NULL);
  4923.  
  4924.   style_set->refcount += 1;
  4925. }
  4926.  
  4927. void
  4928. meta_frame_style_set_unref (MetaFrameStyleSet *style_set)
  4929. {
  4930.   g_return_if_fail (style_set != NULL);
  4931.   g_return_if_fail (style_set->refcount > 0);
  4932.  
  4933.   style_set->refcount -= 1;
  4934.  
  4935.   if (style_set->refcount == 0)
  4936.     {
  4937.       int i;
  4938.  
  4939.       for (i = 0; i < META_FRAME_RESIZE_LAST; i++)
  4940.         {
  4941.           free_focus_styles (style_set->normal_styles[i]);
  4942.           free_focus_styles (style_set->shaded_styles[i]);
  4943.         }
  4944.  
  4945.       free_focus_styles (style_set->maximized_styles);
  4946.       free_focus_styles (style_set->tiled_left_styles);
  4947.       free_focus_styles (style_set->tiled_right_styles);
  4948.       free_focus_styles (style_set->maximized_and_shaded_styles);
  4949.       free_focus_styles (style_set->tiled_left_and_shaded_styles);
  4950.       free_focus_styles (style_set->tiled_right_and_shaded_styles);
  4951.  
  4952.       if (style_set->parent)
  4953.         meta_frame_style_set_unref (style_set->parent);
  4954.  
  4955.       DEBUG_FILL_STRUCT (style_set);
  4956.       g_free (style_set);
  4957.     }
  4958. }
  4959.  
  4960.  
  4961. static MetaFrameStyle*
  4962. get_style (MetaFrameStyleSet *style_set,
  4963.            MetaFrameState     state,
  4964.            MetaFrameResize    resize,
  4965.            MetaFrameFocus     focus)
  4966. {
  4967.   MetaFrameStyle *style;  
  4968.  
  4969.   style = NULL;
  4970.  
  4971.   switch (state)
  4972.     {
  4973.     case META_FRAME_STATE_NORMAL:
  4974.     case META_FRAME_STATE_SHADED:
  4975.       {
  4976.         if (state == META_FRAME_STATE_SHADED)
  4977.           style = style_set->shaded_styles[resize][focus];
  4978.         else
  4979.           style = style_set->normal_styles[resize][focus];
  4980.  
  4981.         /* Try parent if we failed here */
  4982.         if (style == NULL && style_set->parent)
  4983.           style = get_style (style_set->parent, state, resize, focus);
  4984.      
  4985.         /* Allow people to omit the vert/horz/none resize modes */
  4986.         if (style == NULL &&
  4987.             resize != META_FRAME_RESIZE_BOTH)
  4988.           style = get_style (style_set, state, META_FRAME_RESIZE_BOTH, focus);
  4989.       }
  4990.       break;
  4991.     default:
  4992.       {
  4993.         MetaFrameStyle **styles;
  4994.  
  4995.         styles = NULL;
  4996.      
  4997.         switch (state)
  4998.           {
  4999.           case META_FRAME_STATE_MAXIMIZED:
  5000.             styles = style_set->maximized_styles;
  5001.             break;
  5002.           case META_FRAME_STATE_TILED_LEFT:
  5003.             styles = style_set->tiled_left_styles;
  5004.             break;
  5005.           case META_FRAME_STATE_TILED_RIGHT:
  5006.             styles = style_set->tiled_right_styles;
  5007.             break;
  5008.           case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
  5009.             styles = style_set->maximized_and_shaded_styles;
  5010.             break;
  5011.           case META_FRAME_STATE_TILED_LEFT_AND_SHADED:
  5012.             styles = style_set->tiled_left_and_shaded_styles;
  5013.             break;
  5014.           case META_FRAME_STATE_TILED_RIGHT_AND_SHADED:
  5015.             styles = style_set->tiled_right_and_shaded_styles;
  5016.             break;
  5017.           case META_FRAME_STATE_NORMAL:
  5018.           case META_FRAME_STATE_SHADED:
  5019.           case META_FRAME_STATE_LAST:
  5020.             g_assert_not_reached ();
  5021.             break;
  5022.           }
  5023.  
  5024.         style = styles[focus];
  5025.  
  5026.         /* Tiled states are optional, try falling back to non-tiled states */
  5027.         if (style == NULL)
  5028.           {
  5029.             if (state == META_FRAME_STATE_TILED_LEFT ||
  5030.                 state == META_FRAME_STATE_TILED_RIGHT)
  5031.               style = get_style (style_set, META_FRAME_STATE_NORMAL,
  5032.                                  resize, focus);
  5033.             else if (state == META_FRAME_STATE_TILED_LEFT_AND_SHADED ||
  5034.                      state == META_FRAME_STATE_TILED_RIGHT_AND_SHADED)
  5035.               style = get_style (style_set, META_FRAME_STATE_SHADED,
  5036.                                  resize, focus);
  5037.           }
  5038.  
  5039.         /* Try parent if we failed here */
  5040.         if (style == NULL && style_set->parent)
  5041.           style = get_style (style_set->parent, state, resize, focus);      
  5042.       }
  5043.     }
  5044.  
  5045.   return style;
  5046. }
  5047.  
  5048. static gboolean
  5049. check_state  (MetaFrameStyleSet *style_set,
  5050.               MetaFrameState     state,
  5051.               GError           **error)
  5052. {
  5053.   int i;
  5054.  
  5055.   for (i = 0; i < META_FRAME_FOCUS_LAST; i++)
  5056.     {
  5057.       if (get_style (style_set, state,
  5058.                      META_FRAME_RESIZE_NONE, i) == NULL)
  5059.         {
  5060.           /* Translators: This error occurs when a <frame> tag is missing
  5061.            * in theme XML.  The "<frame ...>" is intended as a noun phrase,
  5062.            * and the "missing" qualifies it.  You should translate "whatever".
  5063.            */
  5064.           g_set_error (error, META_THEME_ERROR,
  5065.                        META_THEME_ERROR_FAILED,
  5066.                        _("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
  5067.                        meta_frame_state_to_string (state),
  5068.                        meta_frame_resize_to_string (META_FRAME_RESIZE_NONE),
  5069.                        meta_frame_focus_to_string (i));
  5070.           return FALSE;
  5071.         }
  5072.     }
  5073.  
  5074.   return TRUE;
  5075. }
  5076.  
  5077. gboolean
  5078. meta_frame_style_set_validate  (MetaFrameStyleSet *style_set,
  5079.                                 GError           **error)
  5080. {
  5081.   int i, j;
  5082.  
  5083.   g_return_val_if_fail (style_set != NULL, FALSE);
  5084.  
  5085.   for (i = 0; i < META_FRAME_RESIZE_LAST; i++)
  5086.     for (j = 0; j < META_FRAME_FOCUS_LAST; j++)
  5087.       if (get_style (style_set, META_FRAME_STATE_NORMAL, i, j) == NULL)
  5088.         {
  5089.           g_set_error (error, META_THEME_ERROR,
  5090.                        META_THEME_ERROR_FAILED,
  5091.                        _("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
  5092.                        meta_frame_state_to_string (META_FRAME_STATE_NORMAL),
  5093.                        meta_frame_resize_to_string (i),
  5094.                        meta_frame_focus_to_string (j));
  5095.           return FALSE;
  5096.         }
  5097.  
  5098.   if (!check_state (style_set, META_FRAME_STATE_SHADED, error))
  5099.     return FALSE;
  5100.  
  5101.   if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED, error))
  5102.     return FALSE;
  5103.  
  5104.   if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED_AND_SHADED, error))
  5105.     return FALSE;
  5106.  
  5107.   return TRUE;
  5108. }
  5109.  
  5110. /**
  5111.  * meta_theme_get_current: (skip)
  5112.  *
  5113.  */
  5114. MetaTheme*
  5115. meta_theme_get_current (void)
  5116. {
  5117.   return meta_current_theme;
  5118. }
  5119.  
  5120. void
  5121. meta_theme_set_current (const char *name,
  5122.                         gboolean    force_reload)
  5123. {
  5124.   MetaTheme *new_theme;
  5125.   GError *err;
  5126.  
  5127.   meta_topic (META_DEBUG_THEMES, "Setting current theme to \"%s\"\n", name);
  5128.  
  5129.   if (!force_reload &&
  5130.       meta_current_theme &&
  5131.       strcmp (name, meta_current_theme->name) == 0)
  5132.     return;
  5133.  
  5134.   err = NULL;
  5135.   new_theme = meta_theme_load (name, &err);
  5136.  
  5137.   if (new_theme == NULL)
  5138.     {
  5139.       meta_warning (_("Failed to load theme \"%s\": %s\n"),
  5140.                     name, err->message);
  5141.       g_error_free (err);
  5142.     }
  5143.   else
  5144.     {
  5145.       if (meta_current_theme)
  5146.         meta_theme_free (meta_current_theme);
  5147.  
  5148.       meta_current_theme = new_theme;
  5149.  
  5150.       meta_topic (META_DEBUG_THEMES, "New theme is \"%s\"\n", meta_current_theme->name);
  5151.     }
  5152. }
  5153.  
  5154. /**
  5155.  * meta_theme_new: (skip)
  5156.  *
  5157.  */
  5158. MetaTheme*
  5159. meta_theme_new (void)
  5160. {
  5161.   MetaTheme *theme;
  5162.  
  5163.   theme = g_new0 (MetaTheme, 1);
  5164.  
  5165.   theme->images_by_filename =
  5166.     g_hash_table_new_full (g_str_hash,
  5167.                            g_str_equal,
  5168.                            g_free,
  5169.                            (GDestroyNotify) g_object_unref);
  5170.  
  5171.   theme->layouts_by_name =
  5172.     g_hash_table_new_full (g_str_hash,
  5173.                            g_str_equal,
  5174.                            g_free,
  5175.                            (GDestroyNotify) meta_frame_layout_unref);
  5176.  
  5177.   theme->draw_op_lists_by_name =
  5178.     g_hash_table_new_full (g_str_hash,
  5179.                            g_str_equal,
  5180.                            g_free,
  5181.                            (GDestroyNotify) meta_draw_op_list_unref);
  5182.  
  5183.   theme->styles_by_name =
  5184.     g_hash_table_new_full (g_str_hash,
  5185.                            g_str_equal,
  5186.                            g_free,
  5187.                            (GDestroyNotify) meta_frame_style_unref);
  5188.  
  5189.   theme->style_sets_by_name =
  5190.     g_hash_table_new_full (g_str_hash,
  5191.                            g_str_equal,
  5192.                            g_free,
  5193.                            (GDestroyNotify) meta_frame_style_set_unref);
  5194.  
  5195.   /* Create our variable quarks so we can look up variables without
  5196.      having to strcmp for the names */
  5197.   theme->quark_width = g_quark_from_static_string ("width");
  5198.   theme->quark_height = g_quark_from_static_string ("height");
  5199.   theme->quark_object_width = g_quark_from_static_string ("object_width");
  5200.   theme->quark_object_height = g_quark_from_static_string ("object_height");
  5201.   theme->quark_left_width = g_quark_from_static_string ("left_width");
  5202.   theme->quark_right_width = g_quark_from_static_string ("right_width");
  5203.   theme->quark_top_height = g_quark_from_static_string ("top_height");
  5204.   theme->quark_bottom_height = g_quark_from_static_string ("bottom_height");
  5205.   theme->quark_mini_icon_width = g_quark_from_static_string ("mini_icon_width");
  5206.   theme->quark_mini_icon_height = g_quark_from_static_string ("mini_icon_height");
  5207.   theme->quark_icon_width = g_quark_from_static_string ("icon_width");
  5208.   theme->quark_icon_height = g_quark_from_static_string ("icon_height");
  5209.   theme->quark_title_width = g_quark_from_static_string ("title_width");
  5210.   theme->quark_title_height = g_quark_from_static_string ("title_height");
  5211.   theme->quark_frame_x_center = g_quark_from_static_string ("frame_x_center");
  5212.   theme->quark_frame_y_center = g_quark_from_static_string ("frame_y_center");
  5213.   return theme;
  5214. }
  5215.  
  5216.  
  5217. void
  5218. meta_theme_free (MetaTheme *theme)
  5219. {
  5220.   int i;
  5221.  
  5222.   g_return_if_fail (theme != NULL);
  5223.  
  5224.   g_free (theme->name);
  5225.   g_free (theme->dirname);
  5226.   g_free (theme->filename);
  5227.   g_free (theme->readable_name);
  5228.   g_free (theme->date);
  5229.   g_free (theme->description);
  5230.   g_free (theme->author);
  5231.   g_free (theme->copyright);
  5232.  
  5233.   /* be more careful when destroying the theme hash tables,
  5234.      since they are only constructed as needed, and may be NULL. */
  5235.   if (theme->integer_constants)
  5236.     g_hash_table_destroy (theme->integer_constants);
  5237.   if (theme->images_by_filename)
  5238.     g_hash_table_destroy (theme->images_by_filename);
  5239.   if (theme->layouts_by_name)
  5240.     g_hash_table_destroy (theme->layouts_by_name);
  5241.   if (theme->draw_op_lists_by_name)  
  5242.     g_hash_table_destroy (theme->draw_op_lists_by_name);
  5243.   if (theme->styles_by_name)  
  5244.     g_hash_table_destroy (theme->styles_by_name);
  5245.   if (theme->style_sets_by_name)  
  5246.     g_hash_table_destroy (theme->style_sets_by_name);
  5247.  
  5248.   for (i = 0; i < META_FRAME_TYPE_LAST; i++)
  5249.     if (theme->style_sets_by_type[i])
  5250.       meta_frame_style_set_unref (theme->style_sets_by_type[i]);
  5251.  
  5252.   DEBUG_FILL_STRUCT (theme);
  5253.   g_free (theme);
  5254. }
  5255.  
  5256. gboolean
  5257. meta_theme_validate (MetaTheme *theme,
  5258.                      GError   **error)
  5259. {
  5260.   int i;
  5261.  
  5262.   g_return_val_if_fail (theme != NULL, FALSE);
  5263.  
  5264.   /* FIXME what else should be checked? */
  5265.  
  5266.   g_assert (theme->name);
  5267.  
  5268.   if (theme->readable_name == NULL)
  5269.     {
  5270.       /* Translators: This error means that a necessary XML tag (whose name
  5271.        * is given in angle brackets) was not found in a given theme (whose
  5272.        * name is given second, in quotation marks).
  5273.        */
  5274.       g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5275.                    _("No <%s> set for theme \"%s\""), "name", theme->name);
  5276.       return FALSE;
  5277.     }
  5278.  
  5279.   if (theme->author == NULL)
  5280.     {
  5281.       g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5282.                    _("No <%s> set for theme \"%s\""), "author", theme->name);
  5283.       return FALSE;
  5284.     }
  5285.  
  5286.   if (theme->date == NULL)
  5287.     {
  5288.       g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5289.                    _("No <%s> set for theme \"%s\""), "date", theme->name);
  5290.       return FALSE;
  5291.     }
  5292.  
  5293.   if (theme->description == NULL)
  5294.     {
  5295.       g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5296.                    _("No <%s> set for theme \"%s\""), "description", theme->name);
  5297.       return FALSE;
  5298.     }
  5299.  
  5300.   if (theme->copyright == NULL)
  5301.     {
  5302.       g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5303.                    _("No <%s> set for theme \"%s\""), "copyright", theme->name);
  5304.       return FALSE;
  5305.     }
  5306.  
  5307.   for (i = 0; i < (int)META_FRAME_TYPE_LAST; i++)
  5308.     if (i != (int)META_FRAME_TYPE_ATTACHED && theme->style_sets_by_type[i] == NULL)
  5309.       {
  5310.         g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5311.                      _("No frame style set for window type \"%s\" in theme \"%s\", add a <window type=\"%s\" style_set=\"whatever\"/> element"),
  5312.                      meta_frame_type_to_string (i),
  5313.                      theme->name,
  5314.                      meta_frame_type_to_string (i));
  5315.        
  5316.         return FALSE;          
  5317.       }
  5318.  
  5319.   return TRUE;
  5320. }
  5321.  
  5322. /**
  5323.  * meta_theme_load_image: (skip)
  5324.  *
  5325.  */
  5326. GdkPixbuf*
  5327. meta_theme_load_image (MetaTheme  *theme,
  5328.                        const char *filename,
  5329.                        guint size_of_theme_icons,
  5330.                        GError    **error)
  5331. {
  5332.   GdkPixbuf *pixbuf;
  5333.  
  5334.   pixbuf = g_hash_table_lookup (theme->images_by_filename,
  5335.                                 filename);
  5336.  
  5337.   if (pixbuf == NULL)
  5338.     {
  5339.        
  5340.       if (g_str_has_prefix (filename, "theme:") &&
  5341.           META_THEME_ALLOWS (theme, META_THEME_IMAGES_FROM_ICON_THEMES))
  5342.         {
  5343.           pixbuf = gtk_icon_theme_load_icon (
  5344.               gtk_icon_theme_get_default (),
  5345.               filename+6,
  5346.               size_of_theme_icons,
  5347.               0,
  5348.               error);
  5349.           if (pixbuf == NULL) return NULL;
  5350.          }
  5351.       else
  5352.         {
  5353.           char *full_path;
  5354.           full_path = g_build_filename (theme->dirname, filename, NULL);
  5355.      
  5356.           pixbuf = gdk_pixbuf_new_from_file (full_path, error);
  5357.           if (pixbuf == NULL)
  5358.             {
  5359.               g_free (full_path);
  5360.               return NULL;
  5361.             }
  5362.      
  5363.           g_free (full_path);
  5364.         }      
  5365.       g_hash_table_replace (theme->images_by_filename,
  5366.                             g_strdup (filename),
  5367.                             pixbuf);
  5368.     }
  5369.  
  5370.   g_assert (pixbuf);
  5371.  
  5372.   g_object_ref (G_OBJECT (pixbuf));
  5373.  
  5374.   return pixbuf;
  5375. }
  5376.  
  5377. static MetaFrameStyle*
  5378. theme_get_style (MetaTheme     *theme,
  5379.                  MetaFrameType  type,
  5380.                  MetaFrameFlags flags)
  5381. {
  5382.   MetaFrameState state;
  5383.   MetaFrameResize resize;
  5384.   MetaFrameFocus focus;
  5385.   MetaFrameStyle *style;
  5386.   MetaFrameStyleSet *style_set;
  5387.  
  5388.   style_set = theme->style_sets_by_type[type];
  5389.  
  5390.   if (style_set == NULL && type == META_FRAME_TYPE_ATTACHED)
  5391.     style_set = theme->style_sets_by_type[META_FRAME_TYPE_BORDER];
  5392.  
  5393.   /* Right now the parser forces a style set for all other types,
  5394.    * but this fallback code is here in case I take that out.
  5395.    */
  5396.   if (style_set == NULL)
  5397.     style_set = theme->style_sets_by_type[META_FRAME_TYPE_NORMAL];
  5398.   if (style_set == NULL)
  5399.     return NULL;
  5400.  
  5401.   switch (flags & (META_FRAME_MAXIMIZED | META_FRAME_SHADED |
  5402.                    META_FRAME_TILED_LEFT | META_FRAME_TILED_RIGHT))
  5403.     {
  5404.     case 0:
  5405.       state = META_FRAME_STATE_NORMAL;
  5406.       break;
  5407.     case META_FRAME_MAXIMIZED:
  5408.       state = META_FRAME_STATE_MAXIMIZED;
  5409.       break;
  5410.     case META_FRAME_TILED_LEFT:
  5411.       state = META_FRAME_STATE_TILED_LEFT;
  5412.       break;
  5413.     case META_FRAME_TILED_RIGHT:
  5414.       state = META_FRAME_STATE_TILED_RIGHT;
  5415.       break;
  5416.     case META_FRAME_SHADED:
  5417.       state = META_FRAME_STATE_SHADED;
  5418.       break;
  5419.     case (META_FRAME_MAXIMIZED | META_FRAME_SHADED):
  5420.       state = META_FRAME_STATE_MAXIMIZED_AND_SHADED;
  5421.       break;
  5422.     case (META_FRAME_TILED_LEFT | META_FRAME_SHADED):
  5423.       state = META_FRAME_STATE_TILED_LEFT_AND_SHADED;
  5424.       break;
  5425.     case (META_FRAME_TILED_RIGHT | META_FRAME_SHADED):
  5426.       state = META_FRAME_STATE_TILED_RIGHT_AND_SHADED;
  5427.       break;
  5428.     default:
  5429.       g_assert_not_reached ();
  5430.       state = META_FRAME_STATE_LAST; /* compiler */
  5431.       break;
  5432.     }
  5433.  
  5434.   switch (flags & (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE))
  5435.     {
  5436.     case 0:
  5437.       resize = META_FRAME_RESIZE_NONE;
  5438.       break;
  5439.     case META_FRAME_ALLOWS_VERTICAL_RESIZE:
  5440.       resize = META_FRAME_RESIZE_VERTICAL;
  5441.       break;
  5442.     case META_FRAME_ALLOWS_HORIZONTAL_RESIZE:
  5443.       resize = META_FRAME_RESIZE_HORIZONTAL;
  5444.       break;
  5445.     case (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE):
  5446.       resize = META_FRAME_RESIZE_BOTH;
  5447.       break;
  5448.     default:
  5449.       g_assert_not_reached ();
  5450.       resize = META_FRAME_RESIZE_LAST; /* compiler */
  5451.       break;
  5452.     }
  5453.  
  5454.   /* re invert the styles used for focus/unfocussed while flashing a frame */
  5455.   if (((flags & META_FRAME_HAS_FOCUS) && !(flags & META_FRAME_IS_FLASHING))
  5456.       || (!(flags & META_FRAME_HAS_FOCUS) && (flags & META_FRAME_IS_FLASHING)))
  5457.     focus = META_FRAME_FOCUS_YES;
  5458.   else
  5459.     focus = META_FRAME_FOCUS_NO;
  5460.  
  5461.   style = get_style (style_set, state, resize, focus);
  5462.  
  5463.   return style;
  5464. }
  5465.  
  5466. MetaFrameStyle*
  5467. meta_theme_get_frame_style (MetaTheme     *theme,
  5468.                             MetaFrameType  type,
  5469.                             MetaFrameFlags flags)
  5470. {
  5471.   MetaFrameStyle *style;
  5472.  
  5473.   g_return_val_if_fail (type < META_FRAME_TYPE_LAST, NULL);
  5474.  
  5475.   style = theme_get_style (theme, type, flags);
  5476.  
  5477.   return style;
  5478. }
  5479.  
  5480. double
  5481. meta_theme_get_title_scale (MetaTheme     *theme,
  5482.                             MetaFrameType  type,
  5483.                             MetaFrameFlags flags)
  5484. {
  5485.   MetaFrameStyle *style;
  5486.  
  5487.   g_return_val_if_fail (type < META_FRAME_TYPE_LAST, 1.0);
  5488.  
  5489.   style = theme_get_style (theme, type, flags);
  5490.  
  5491.   /* Parser is not supposed to allow this currently */
  5492.   if (style == NULL)
  5493.     return 1.0;
  5494.  
  5495.   return style->layout->title_scale;
  5496. }
  5497.  
  5498. void
  5499. meta_theme_draw_frame_with_style (MetaTheme              *theme,
  5500.                                   GtkStyleContext        *style_gtk,
  5501.                                   GtkWidget              *widget,
  5502.                                   cairo_t                *cr,
  5503.                                   MetaFrameType           type,
  5504.                                   MetaFrameFlags          flags,
  5505.                                   int                     client_width,
  5506.                                   int                     client_height,
  5507.                                   PangoLayout            *title_layout,
  5508.                                   int                     text_height,
  5509.                                   const MetaButtonLayout *button_layout,
  5510.                                   MetaButtonState         button_states[META_BUTTON_TYPE_LAST],
  5511.                                   GdkPixbuf              *mini_icon,
  5512.                                   GdkPixbuf              *icon)
  5513. {
  5514.   MetaFrameGeometry fgeom;
  5515.   MetaFrameStyle *style;
  5516.  
  5517.   g_return_if_fail (type < META_FRAME_TYPE_LAST);
  5518.  
  5519.   style = theme_get_style (theme, type, flags);
  5520.  
  5521.   /* Parser is not supposed to allow this currently */
  5522.   if (style == NULL)
  5523.     return;
  5524.  
  5525.   meta_frame_layout_calc_geometry (style->layout,
  5526.                                    text_height,
  5527.                                    flags,
  5528.                                    client_width, client_height,
  5529.                                    button_layout,
  5530.                                    type,
  5531.                                    &fgeom,
  5532.                                    theme);  
  5533.  
  5534.   meta_frame_style_draw_with_style (style,
  5535.                                     style_gtk,
  5536.                                     widget,
  5537.                                     cr,
  5538.                                     &fgeom,
  5539.                                     client_width, client_height,
  5540.                                     title_layout,
  5541.                                     text_height,
  5542.                                     button_states,
  5543.                                     mini_icon, icon);
  5544. }
  5545.  
  5546. void
  5547. meta_theme_draw_frame (MetaTheme              *theme,
  5548.                        GtkWidget              *widget,
  5549.                        cairo_t                *cr,
  5550.                        MetaFrameType           type,
  5551.                        MetaFrameFlags          flags,
  5552.                        int                     client_width,
  5553.                        int                     client_height,
  5554.                        PangoLayout            *title_layout,
  5555.                        int                     text_height,
  5556.                        const MetaButtonLayout *button_layout,
  5557.                        MetaButtonState         button_states[META_BUTTON_TYPE_LAST],
  5558.                        GdkPixbuf              *mini_icon,
  5559.                        GdkPixbuf              *icon)
  5560. {
  5561.   meta_theme_draw_frame_with_style (theme, gtk_widget_get_style_context (widget), widget,
  5562.                                     cr, type,flags,
  5563.                                     client_width, client_height,
  5564.                                     title_layout, text_height,
  5565.                                     button_layout, button_states,
  5566.                                     mini_icon, icon);
  5567. }
  5568.  
  5569. void
  5570. meta_theme_get_frame_borders (MetaTheme        *theme,
  5571.                               MetaFrameType     type,
  5572.                               int               text_height,
  5573.                               MetaFrameFlags    flags,
  5574.                               MetaFrameBorders *borders)
  5575. {
  5576.   MetaFrameStyle *style;
  5577.  
  5578.   g_return_if_fail (type < META_FRAME_TYPE_LAST);
  5579.  
  5580.   style = theme_get_style (theme, type, flags);
  5581.  
  5582.   meta_frame_borders_clear (borders);
  5583.  
  5584.   /* Parser is not supposed to allow this currently */
  5585.   if (style == NULL)
  5586.     return;
  5587.  
  5588.   meta_frame_layout_get_borders (style->layout,
  5589.                                  text_height,
  5590.                                  flags, type,
  5591.                                  borders);
  5592. }
  5593.  
  5594. void
  5595. meta_theme_calc_geometry (MetaTheme              *theme,
  5596.                           MetaFrameType           type,
  5597.                           int                     text_height,
  5598.                           MetaFrameFlags          flags,
  5599.                           int                     client_width,
  5600.                           int                     client_height,
  5601.                           const MetaButtonLayout *button_layout,
  5602.                           MetaFrameGeometry      *fgeom)
  5603. {
  5604.   MetaFrameStyle *style;
  5605.  
  5606.   g_return_if_fail (type < META_FRAME_TYPE_LAST);
  5607.  
  5608.   style = theme_get_style (theme, type, flags);
  5609.  
  5610.   /* Parser is not supposed to allow this currently */
  5611.   if (style == NULL)
  5612.     return;
  5613.  
  5614.   meta_frame_layout_calc_geometry (style->layout,
  5615.                                    text_height,
  5616.                                    flags,
  5617.                                    client_width, client_height,
  5618.                                    button_layout,
  5619.                                    type,
  5620.                                    fgeom,
  5621.                                    theme);
  5622. }
  5623.  
  5624. MetaFrameLayout*
  5625. meta_theme_lookup_layout (MetaTheme         *theme,
  5626.                           const char        *name)
  5627. {
  5628.   return g_hash_table_lookup (theme->layouts_by_name, name);
  5629. }
  5630.  
  5631. void
  5632. meta_theme_insert_layout (MetaTheme         *theme,
  5633.                           const char        *name,
  5634.                           MetaFrameLayout   *layout)
  5635. {
  5636.   meta_frame_layout_ref (layout);
  5637.   g_hash_table_replace (theme->layouts_by_name, g_strdup (name), layout);
  5638. }
  5639.  
  5640. MetaDrawOpList*
  5641. meta_theme_lookup_draw_op_list (MetaTheme         *theme,
  5642.                                 const char        *name)
  5643. {
  5644.   return g_hash_table_lookup (theme->draw_op_lists_by_name, name);
  5645. }
  5646.  
  5647. void
  5648. meta_theme_insert_draw_op_list (MetaTheme         *theme,
  5649.                                 const char        *name,
  5650.                                 MetaDrawOpList    *op_list)
  5651. {
  5652.   meta_draw_op_list_ref (op_list);
  5653.   g_hash_table_replace (theme->draw_op_lists_by_name, g_strdup (name), op_list);
  5654. }
  5655.  
  5656. MetaFrameStyle*
  5657. meta_theme_lookup_style (MetaTheme         *theme,
  5658.                          const char        *name)
  5659. {
  5660.   return g_hash_table_lookup (theme->styles_by_name, name);
  5661. }
  5662.  
  5663. void
  5664. meta_theme_insert_style (MetaTheme         *theme,
  5665.                          const char        *name,
  5666.                          MetaFrameStyle    *style)
  5667. {
  5668.   meta_frame_style_ref (style);
  5669.   g_hash_table_replace (theme->styles_by_name, g_strdup (name), style);
  5670. }
  5671.  
  5672. MetaFrameStyleSet*
  5673. meta_theme_lookup_style_set (MetaTheme         *theme,
  5674.                              const char        *name)
  5675. {
  5676.   return g_hash_table_lookup (theme->style_sets_by_name, name);
  5677. }
  5678.  
  5679. void
  5680. meta_theme_insert_style_set    (MetaTheme         *theme,
  5681.                                 const char        *name,
  5682.                                 MetaFrameStyleSet *style_set)
  5683. {
  5684.   meta_frame_style_set_ref (style_set);
  5685.   g_hash_table_replace (theme->style_sets_by_name, g_strdup (name), style_set);
  5686. }
  5687.  
  5688. static gboolean
  5689. first_uppercase (const char *str)
  5690. {  
  5691.   return g_ascii_isupper (*str);
  5692. }
  5693.  
  5694. gboolean
  5695. meta_theme_define_int_constant (MetaTheme   *theme,
  5696.                                 const char  *name,
  5697.                                 int          value,
  5698.                                 GError     **error)
  5699. {
  5700.   if (theme->integer_constants == NULL)
  5701.     theme->integer_constants = g_hash_table_new_full (g_str_hash,
  5702.                                                       g_str_equal,
  5703.                                                       g_free,
  5704.                                                       NULL);
  5705.  
  5706.   if (!first_uppercase (name))
  5707.     {
  5708.       g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5709.                    _("User-defined constants must begin with a capital letter; \"%s\" does not"),
  5710.                    name);
  5711.       return FALSE;
  5712.     }
  5713.  
  5714.   if (g_hash_table_lookup_extended (theme->integer_constants, name, NULL, NULL))
  5715.     {
  5716.       g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5717.                    _("Constant \"%s\" has already been defined"),
  5718.                    name);
  5719.      
  5720.       return FALSE;
  5721.     }
  5722.  
  5723.   g_hash_table_insert (theme->integer_constants,
  5724.                        g_strdup (name),
  5725.                        GINT_TO_POINTER (value));
  5726.  
  5727.   return TRUE;
  5728. }
  5729.  
  5730. gboolean
  5731. meta_theme_lookup_int_constant (MetaTheme   *theme,
  5732.                                 const char  *name,
  5733.                                 int         *value)
  5734. {
  5735.   gpointer old_value;
  5736.  
  5737.   *value = 0;
  5738.  
  5739.   if (theme->integer_constants == NULL)
  5740.     return FALSE;
  5741.  
  5742.   if (g_hash_table_lookup_extended (theme->integer_constants,
  5743.                                     name, NULL, &old_value))
  5744.     {
  5745.       *value = GPOINTER_TO_INT (old_value);
  5746.       return TRUE;
  5747.     }
  5748.   else
  5749.     {
  5750.       return FALSE;
  5751.     }
  5752. }
  5753.  
  5754. gboolean
  5755. meta_theme_define_float_constant (MetaTheme   *theme,
  5756.                                   const char  *name,
  5757.                                   double       value,
  5758.                                   GError     **error)
  5759. {
  5760.   double *d;
  5761.  
  5762.   if (theme->float_constants == NULL)
  5763.     theme->float_constants = g_hash_table_new_full (g_str_hash,
  5764.                                                     g_str_equal,
  5765.                                                     g_free,
  5766.                                                     g_free);
  5767.  
  5768.   if (!first_uppercase (name))
  5769.     {
  5770.       g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5771.                    _("User-defined constants must begin with a capital letter; \"%s\" does not"),
  5772.                    name);
  5773.       return FALSE;
  5774.     }
  5775.  
  5776.   if (g_hash_table_lookup_extended (theme->float_constants, name, NULL, NULL))
  5777.     {
  5778.       g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5779.                    _("Constant \"%s\" has already been defined"),
  5780.                    name);
  5781.      
  5782.       return FALSE;
  5783.     }
  5784.  
  5785.   d = g_new (double, 1);
  5786.   *d = value;
  5787.  
  5788.   g_hash_table_insert (theme->float_constants,
  5789.                        g_strdup (name), d);
  5790.  
  5791.   return TRUE;
  5792. }
  5793.  
  5794. gboolean
  5795. meta_theme_lookup_float_constant (MetaTheme   *theme,
  5796.                                   const char  *name,
  5797.                                   double      *value)
  5798. {
  5799.   double *d;
  5800.  
  5801.   *value = 0.0;
  5802.  
  5803.   if (theme->float_constants == NULL)
  5804.     return FALSE;
  5805.  
  5806.   d = g_hash_table_lookup (theme->float_constants, name);
  5807.  
  5808.   if (d)
  5809.     {
  5810.       *value = *d;
  5811.       return TRUE;
  5812.     }
  5813.   else
  5814.     {
  5815.       return FALSE;
  5816.     }
  5817. }
  5818.  
  5819. gboolean
  5820. meta_theme_define_color_constant (MetaTheme   *theme,
  5821.                                   const char  *name,
  5822.                                   const char  *value,
  5823.                                   GError     **error)
  5824. {
  5825.   if (theme->color_constants == NULL)
  5826.     theme->color_constants = g_hash_table_new_full (g_str_hash,
  5827.                                                     g_str_equal,
  5828.                                                     g_free,
  5829.                                                     NULL);
  5830.  
  5831.   if (!first_uppercase (name))
  5832.     {
  5833.       g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5834.                    _("User-defined constants must begin with a capital letter; \"%s\" does not"),
  5835.                    name);
  5836.       return FALSE;
  5837.     }
  5838.  
  5839.   if (g_hash_table_lookup_extended (theme->color_constants, name, NULL, NULL))
  5840.     {
  5841.       g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
  5842.                    _("Constant \"%s\" has already been defined"),
  5843.                    name);
  5844.      
  5845.       return FALSE;
  5846.     }
  5847.  
  5848.   g_hash_table_insert (theme->color_constants,
  5849.                        g_strdup (name),
  5850.                        g_strdup (value));
  5851.  
  5852.   return TRUE;
  5853. }
  5854.  
  5855. /**
  5856.  * Looks up a colour constant.
  5857.  *
  5858.  * \param theme  the theme containing the constant
  5859.  * \param name  the name of the constant
  5860.  * \param value  [out] the string representation of the colour, or NULL if it
  5861.  *               doesn't exist
  5862.  * \return  TRUE if it exists, FALSE otherwise
  5863.  */
  5864. gboolean
  5865. meta_theme_lookup_color_constant (MetaTheme   *theme,
  5866.                                   const char  *name,
  5867.                                   char       **value)
  5868. {
  5869.   char *result;
  5870.  
  5871.   *value = NULL;
  5872.  
  5873.   if (theme->color_constants == NULL)
  5874.     return FALSE;
  5875.  
  5876.   result = g_hash_table_lookup (theme->color_constants, name);
  5877.  
  5878.   if (result)
  5879.     {
  5880.       *value = result;
  5881.       return TRUE;
  5882.     }
  5883.   else
  5884.     {
  5885.       return FALSE;
  5886.     }
  5887. }
  5888.  
  5889.  
  5890. PangoFontDescription*
  5891. meta_gtk_widget_get_font_desc (GtkWidget *widget,
  5892.                                double     scale,
  5893.                    const PangoFontDescription *override)
  5894. {
  5895.   GtkStyleContext *style;
  5896.   PangoFontDescription *font_desc;
  5897.  
  5898.   g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
  5899.  
  5900.   style = gtk_widget_get_style_context (widget);
  5901.   font_desc = pango_font_description_copy (gtk_style_context_get_font (style, 0));
  5902.  
  5903.   if (override)
  5904.     pango_font_description_merge (font_desc, override, TRUE);
  5905.  
  5906.   pango_font_description_set_size (font_desc,
  5907.                                    MAX (pango_font_description_get_size (font_desc) * scale, 1));
  5908.  
  5909.   return font_desc;
  5910. }
  5911.  
  5912. /**
  5913.  * Returns the height of the letters in a particular font.
  5914.  *
  5915.  * \param font_desc  the font
  5916.  * \param context  the context of the font
  5917.  * \return  the height of the letters
  5918.  */
  5919. int
  5920. meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc,
  5921.                                       PangoContext         *context)
  5922. {
  5923.   PangoFontMetrics *metrics;
  5924.   PangoLanguage *lang;
  5925.   int retval;
  5926.  
  5927.   lang = pango_context_get_language (context);
  5928.   metrics = pango_context_get_metrics (context, font_desc, lang);
  5929.  
  5930.   retval = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
  5931.                          pango_font_metrics_get_descent (metrics));
  5932.  
  5933.   pango_font_metrics_unref (metrics);
  5934.  
  5935.   return retval;
  5936. }
  5937.  
  5938. MetaGtkColorComponent
  5939. meta_color_component_from_string (const char *str)
  5940. {
  5941.   if (strcmp ("fg", str) == 0)
  5942.     return META_GTK_COLOR_FG;
  5943.   else if (strcmp ("bg", str) == 0)
  5944.     return META_GTK_COLOR_BG;
  5945.   else if (strcmp ("light", str) == 0)
  5946.     return META_GTK_COLOR_LIGHT;
  5947.   else if (strcmp ("dark", str) == 0)
  5948.     return META_GTK_COLOR_DARK;
  5949.   else if (strcmp ("mid", str) == 0)
  5950.     return META_GTK_COLOR_MID;
  5951.   else if (strcmp ("text", str) == 0)
  5952.     return META_GTK_COLOR_TEXT;
  5953.   else if (strcmp ("base", str) == 0)
  5954.     return META_GTK_COLOR_BASE;
  5955.   else if (strcmp ("text_aa", str) == 0)
  5956.     return META_GTK_COLOR_TEXT_AA;
  5957.   else
  5958.     return META_GTK_COLOR_LAST;
  5959. }
  5960.  
  5961. const char*
  5962. meta_color_component_to_string (MetaGtkColorComponent component)
  5963. {
  5964.   switch (component)
  5965.     {
  5966.     case META_GTK_COLOR_FG:
  5967.       return "fg";
  5968.     case META_GTK_COLOR_BG:
  5969.       return "bg";
  5970.     case META_GTK_COLOR_LIGHT:
  5971.       return "light";
  5972.     case META_GTK_COLOR_DARK:
  5973.       return "dark";
  5974.     case META_GTK_COLOR_MID:
  5975.       return "mid";
  5976.     case META_GTK_COLOR_TEXT:
  5977.       return "text";
  5978.     case META_GTK_COLOR_BASE:
  5979.       return "base";
  5980.     case META_GTK_COLOR_TEXT_AA:
  5981.       return "text_aa";
  5982.     case META_GTK_COLOR_LAST:
  5983.       break;
  5984.     }
  5985.  
  5986.   return "<unknown>";
  5987. }
  5988.  
  5989. MetaButtonState
  5990. meta_button_state_from_string (const char *str)
  5991. {
  5992.   if (strcmp ("normal", str) == 0)
  5993.     return META_BUTTON_STATE_NORMAL;
  5994.   else if (strcmp ("pressed", str) == 0)
  5995.     return META_BUTTON_STATE_PRESSED;
  5996.   else if (strcmp ("prelight", str) == 0)
  5997.     return META_BUTTON_STATE_PRELIGHT;
  5998.   else
  5999.     return META_BUTTON_STATE_LAST;
  6000. }
  6001.  
  6002. const char*
  6003. meta_button_state_to_string (MetaButtonState state)
  6004. {
  6005.   switch (state)
  6006.     {
  6007.     case META_BUTTON_STATE_NORMAL:
  6008.       return "normal";
  6009.     case META_BUTTON_STATE_PRESSED:
  6010.       return "pressed";
  6011.     case META_BUTTON_STATE_PRELIGHT:
  6012.       return "prelight";
  6013.     case META_BUTTON_STATE_LAST:
  6014.       break;
  6015.     }
  6016.  
  6017.   return "<unknown>";
  6018. }
  6019.  
  6020. MetaButtonType
  6021. meta_button_type_from_string (const char *str, MetaTheme *theme)
  6022. {
  6023.   if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS))
  6024.     {
  6025.       if (strcmp ("shade", str) == 0)
  6026.         return META_BUTTON_TYPE_SHADE;
  6027.       else if (strcmp ("above", str) == 0)
  6028.         return META_BUTTON_TYPE_ABOVE;
  6029.       else if (strcmp ("stick", str) == 0)
  6030.         return META_BUTTON_TYPE_STICK;
  6031.       else if (strcmp ("unshade", str) == 0)
  6032.         return META_BUTTON_TYPE_UNSHADE;
  6033.       else if (strcmp ("unabove", str) == 0)
  6034.         return META_BUTTON_TYPE_UNABOVE;
  6035.       else if (strcmp ("unstick", str) == 0)
  6036.         return META_BUTTON_TYPE_UNSTICK;
  6037.      }
  6038.  
  6039.   if (strcmp ("close", str) == 0)
  6040.     return META_BUTTON_TYPE_CLOSE;
  6041.   else if (strcmp ("maximize", str) == 0)
  6042.     return META_BUTTON_TYPE_MAXIMIZE;
  6043.   else if (strcmp ("minimize", str) == 0)
  6044.     return META_BUTTON_TYPE_MINIMIZE;
  6045.   else if (strcmp ("menu", str) == 0)
  6046.     return META_BUTTON_TYPE_MENU;
  6047.   else if (strcmp ("left_left_background", str) == 0)
  6048.     return META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND;
  6049.   else if (strcmp ("left_middle_background", str) == 0)
  6050.     return META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND;
  6051.   else if (strcmp ("left_right_background", str) == 0)
  6052.     return META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND;
  6053.   else if (strcmp ("left_single_background", str) == 0)
  6054.     return META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND;
  6055.   else if (strcmp ("right_left_background", str) == 0)
  6056.     return META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND;
  6057.   else if (strcmp ("right_middle_background", str) == 0)
  6058.     return META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND;
  6059.   else if (strcmp ("right_right_background", str) == 0)
  6060.     return META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND;
  6061.   else if (strcmp ("right_single_background", str) == 0)
  6062.     return META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND;
  6063.   else
  6064.     return META_BUTTON_TYPE_LAST;
  6065. }
  6066.  
  6067. const char*
  6068. meta_button_type_to_string (MetaButtonType type)
  6069. {
  6070.   switch (type)
  6071.     {
  6072.     case META_BUTTON_TYPE_CLOSE:
  6073.       return "close";
  6074.     case META_BUTTON_TYPE_MAXIMIZE:
  6075.       return "maximize";
  6076.     case META_BUTTON_TYPE_MINIMIZE:
  6077.       return "minimize";
  6078.     case META_BUTTON_TYPE_SHADE:
  6079.      return "shade";
  6080.     case META_BUTTON_TYPE_ABOVE:
  6081.       return "above";
  6082.     case META_BUTTON_TYPE_STICK:
  6083.       return "stick";
  6084.     case META_BUTTON_TYPE_UNSHADE:
  6085.       return "unshade";
  6086.     case META_BUTTON_TYPE_UNABOVE:
  6087.       return "unabove";
  6088.     case META_BUTTON_TYPE_UNSTICK:
  6089.       return "unstick";
  6090.      case META_BUTTON_TYPE_MENU:
  6091.       return "menu";
  6092.     case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
  6093.       return "left_left_background";
  6094.     case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
  6095.       return "left_middle_background";
  6096.     case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
  6097.       return "left_right_background";
  6098.     case META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND:
  6099.       return "left_single_background";
  6100.     case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
  6101.       return "right_left_background";
  6102.     case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
  6103.       return "right_middle_background";
  6104.     case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
  6105.       return "right_right_background";      
  6106.     case META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND:
  6107.       return "right_single_background";
  6108.     case META_BUTTON_TYPE_LAST:
  6109.       break;
  6110.     }
  6111.  
  6112.   return "<unknown>";
  6113. }
  6114.  
  6115. MetaFramePiece
  6116. meta_frame_piece_from_string (const char *str)
  6117. {
  6118.   if (strcmp ("entire_background", str) == 0)
  6119.     return META_FRAME_PIECE_ENTIRE_BACKGROUND;
  6120.   else if (strcmp ("titlebar", str) == 0)
  6121.     return META_FRAME_PIECE_TITLEBAR;
  6122.   else if (strcmp ("titlebar_middle", str) == 0)
  6123.     return META_FRAME_PIECE_TITLEBAR_MIDDLE;
  6124.   else if (strcmp ("left_titlebar_edge", str) == 0)
  6125.     return META_FRAME_PIECE_LEFT_TITLEBAR_EDGE;
  6126.   else if (strcmp ("right_titlebar_edge", str) == 0)
  6127.     return META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE;
  6128.   else if (strcmp ("top_titlebar_edge", str) == 0)
  6129.     return META_FRAME_PIECE_TOP_TITLEBAR_EDGE;
  6130.   else if (strcmp ("bottom_titlebar_edge", str) == 0)
  6131.     return META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE;
  6132.   else if (strcmp ("title", str) == 0)
  6133.     return META_FRAME_PIECE_TITLE;
  6134.   else if (strcmp ("left_edge", str) == 0)
  6135.     return META_FRAME_PIECE_LEFT_EDGE;
  6136.   else if (strcmp ("right_edge", str) == 0)
  6137.     return META_FRAME_PIECE_RIGHT_EDGE;
  6138.   else if (strcmp ("bottom_edge", str) == 0)
  6139.     return META_FRAME_PIECE_BOTTOM_EDGE;
  6140.   else if (strcmp ("overlay", str) == 0)
  6141.     return META_FRAME_PIECE_OVERLAY;
  6142.   else
  6143.     return META_FRAME_PIECE_LAST;
  6144. }
  6145.  
  6146. const char*
  6147. meta_frame_piece_to_string (MetaFramePiece piece)
  6148. {
  6149.   switch (piece)
  6150.     {
  6151.     case META_FRAME_PIECE_ENTIRE_BACKGROUND:
  6152.       return "entire_background";
  6153.     case META_FRAME_PIECE_TITLEBAR:
  6154.       return "titlebar";
  6155.     case META_FRAME_PIECE_TITLEBAR_MIDDLE:
  6156.       return "titlebar_middle";
  6157.     case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE:
  6158.       return "left_titlebar_edge";
  6159.     case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE:
  6160.       return "right_titlebar_edge";
  6161.     case META_FRAME_PIECE_TOP_TITLEBAR_EDGE:
  6162.       return "top_titlebar_edge";
  6163.     case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE:
  6164.       return "bottom_titlebar_edge";
  6165.     case META_FRAME_PIECE_TITLE:
  6166.       return "title";
  6167.     case META_FRAME_PIECE_LEFT_EDGE:
  6168.       return "left_edge";
  6169.     case META_FRAME_PIECE_RIGHT_EDGE:
  6170.       return "right_edge";
  6171.     case META_FRAME_PIECE_BOTTOM_EDGE:
  6172.       return "bottom_edge";
  6173.     case META_FRAME_PIECE_OVERLAY:
  6174.       return "overlay";
  6175.     case META_FRAME_PIECE_LAST:
  6176.       break;
  6177.     }
  6178.  
  6179.   return "<unknown>";
  6180. }
  6181.  
  6182. MetaFrameState
  6183. meta_frame_state_from_string (const char *str)
  6184. {
  6185.   if (strcmp ("normal", str) == 0)
  6186.     return META_FRAME_STATE_NORMAL;
  6187.   else if (strcmp ("maximized", str) == 0)
  6188.     return META_FRAME_STATE_MAXIMIZED;
  6189.   else if (strcmp ("tiled_left", str) == 0)
  6190.     return META_FRAME_STATE_TILED_LEFT;
  6191.   else if (strcmp ("tiled_right", str) == 0)
  6192.     return META_FRAME_STATE_TILED_RIGHT;
  6193.   else if (strcmp ("shaded", str) == 0)
  6194.     return META_FRAME_STATE_SHADED;
  6195.   else if (strcmp ("maximized_and_shaded", str) == 0)
  6196.     return META_FRAME_STATE_MAXIMIZED_AND_SHADED;
  6197.   else if (strcmp ("tiled_left_and_shaded", str) == 0)
  6198.     return META_FRAME_STATE_TILED_LEFT_AND_SHADED;
  6199.   else if (strcmp ("tiled_right_and_shaded", str) == 0)
  6200.     return META_FRAME_STATE_TILED_RIGHT_AND_SHADED;
  6201.   else
  6202.     return META_FRAME_STATE_LAST;
  6203. }
  6204.  
  6205. const char*
  6206. meta_frame_state_to_string (MetaFrameState state)
  6207. {
  6208.   switch (state)
  6209.     {
  6210.     case META_FRAME_STATE_NORMAL:
  6211.       return "normal";
  6212.     case META_FRAME_STATE_MAXIMIZED:
  6213.       return "maximized";
  6214.     case META_FRAME_STATE_TILED_LEFT:
  6215.       return "tiled_left";
  6216.     case META_FRAME_STATE_TILED_RIGHT:
  6217.       return "tiled_right";
  6218.     case META_FRAME_STATE_SHADED:
  6219.       return "shaded";
  6220.     case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
  6221.       return "maximized_and_shaded";
  6222.     case META_FRAME_STATE_TILED_LEFT_AND_SHADED:
  6223.       return "tiled_left_and_shaded";
  6224.     case META_FRAME_STATE_TILED_RIGHT_AND_SHADED:
  6225.       return "tiled_right_and_shaded";
  6226.     case META_FRAME_STATE_LAST:
  6227.       break;
  6228.     }
  6229.  
  6230.   return "<unknown>";
  6231. }
  6232.  
  6233. MetaFrameResize
  6234. meta_frame_resize_from_string (const char *str)
  6235. {
  6236.   if (strcmp ("none", str) == 0)
  6237.     return META_FRAME_RESIZE_NONE;
  6238.   else if (strcmp ("vertical", str) == 0)
  6239.     return META_FRAME_RESIZE_VERTICAL;
  6240.   else if (strcmp ("horizontal", str) == 0)
  6241.     return META_FRAME_RESIZE_HORIZONTAL;
  6242.   else if (strcmp ("both", str) == 0)
  6243.     return META_FRAME_RESIZE_BOTH;
  6244.   else
  6245.     return META_FRAME_RESIZE_LAST;
  6246. }
  6247.  
  6248. const char*
  6249. meta_frame_resize_to_string (MetaFrameResize resize)
  6250. {
  6251.   switch (resize)
  6252.     {
  6253.     case META_FRAME_RESIZE_NONE:
  6254.       return "none";
  6255.     case META_FRAME_RESIZE_VERTICAL:
  6256.       return "vertical";
  6257.     case META_FRAME_RESIZE_HORIZONTAL:
  6258.       return "horizontal";
  6259.     case META_FRAME_RESIZE_BOTH:
  6260.       return "both";
  6261.     case META_FRAME_RESIZE_LAST:
  6262.       break;
  6263.     }
  6264.  
  6265.   return "<unknown>";
  6266. }
  6267.  
  6268. MetaFrameFocus
  6269. meta_frame_focus_from_string (const char *str)
  6270. {
  6271.   if (strcmp ("no", str) == 0)
  6272.     return META_FRAME_FOCUS_NO;
  6273.   else if (strcmp ("yes", str) == 0)
  6274.     return META_FRAME_FOCUS_YES;
  6275.   else
  6276.     return META_FRAME_FOCUS_LAST;
  6277. }
  6278.  
  6279. const char*
  6280. meta_frame_focus_to_string (MetaFrameFocus focus)
  6281. {
  6282.   switch (focus)
  6283.     {
  6284.     case META_FRAME_FOCUS_NO:
  6285.       return "no";
  6286.     case META_FRAME_FOCUS_YES:
  6287.       return "yes";
  6288.     case META_FRAME_FOCUS_LAST:
  6289.       break;
  6290.     }
  6291.  
  6292.   return "<unknown>";
  6293. }
  6294.  
  6295. MetaFrameType
  6296. meta_frame_type_from_string (const char *str)
  6297. {
  6298.   if (strcmp ("normal", str) == 0)
  6299.     return META_FRAME_TYPE_NORMAL;
  6300.   else if (strcmp ("dialog", str) == 0)
  6301.     return META_FRAME_TYPE_DIALOG;
  6302.   else if (strcmp ("modal_dialog", str) == 0)
  6303.     return META_FRAME_TYPE_MODAL_DIALOG;
  6304.   else if (strcmp ("utility", str) == 0)
  6305.     return META_FRAME_TYPE_UTILITY;
  6306.   else if (strcmp ("menu", str) == 0)
  6307.     return META_FRAME_TYPE_MENU;
  6308.   else if (strcmp ("border", str) == 0)
  6309.     return META_FRAME_TYPE_BORDER;
  6310.   else if (strcmp ("attached", str) == 0)
  6311.     return META_FRAME_TYPE_ATTACHED;
  6312. #if 0
  6313.   else if (strcmp ("toolbar", str) == 0)
  6314.     return META_FRAME_TYPE_TOOLBAR;
  6315. #endif
  6316.   else
  6317.     return META_FRAME_TYPE_LAST;
  6318. }
  6319.  
  6320. /**
  6321.  * meta_frame_type_to_string:
  6322.  *
  6323.  * Converts a frame type enum value to the name string that would
  6324.  * appear in the theme definition file.
  6325.  *
  6326.  * Return value: the string value
  6327.  */
  6328. const char*
  6329. meta_frame_type_to_string (MetaFrameType type)
  6330. {
  6331.   switch (type)
  6332.     {
  6333.     case META_FRAME_TYPE_NORMAL:
  6334.       return "normal";
  6335.     case META_FRAME_TYPE_DIALOG:
  6336.       return "dialog";
  6337.     case META_FRAME_TYPE_MODAL_DIALOG:
  6338.       return "modal_dialog";
  6339.     case META_FRAME_TYPE_UTILITY:
  6340.       return "utility";
  6341.     case META_FRAME_TYPE_MENU:
  6342.       return "menu";
  6343.     case META_FRAME_TYPE_BORDER:
  6344.       return "border";
  6345.     case META_FRAME_TYPE_ATTACHED:
  6346.       return "attached";
  6347. #if 0
  6348.     case META_FRAME_TYPE_TOOLBAR:
  6349.       return "toolbar";
  6350. #endif
  6351.     case  META_FRAME_TYPE_LAST:
  6352.       break;
  6353.     }
  6354.  
  6355.   return "<unknown>";
  6356. }
  6357.  
  6358. MetaGradientType
  6359. meta_gradient_type_from_string (const char *str)
  6360. {
  6361.   if (strcmp ("vertical", str) == 0)
  6362.     return META_GRADIENT_VERTICAL;
  6363.   else if (strcmp ("horizontal", str) == 0)
  6364.     return META_GRADIENT_HORIZONTAL;
  6365.   else if (strcmp ("diagonal", str) == 0)
  6366.     return META_GRADIENT_DIAGONAL;
  6367.   else
  6368.     return META_GRADIENT_LAST;
  6369. }
  6370.  
  6371. const char*
  6372. meta_gradient_type_to_string (MetaGradientType type)
  6373. {
  6374.   switch (type)
  6375.     {
  6376.     case META_GRADIENT_VERTICAL:
  6377.       return "vertical";
  6378.     case META_GRADIENT_HORIZONTAL:
  6379.       return "horizontal";
  6380.     case META_GRADIENT_DIAGONAL:
  6381.       return "diagonal";
  6382.     case META_GRADIENT_LAST:
  6383.       break;
  6384.     }
  6385.  
  6386.   return "<unknown>";
  6387. }
  6388.  
  6389. GtkStateFlags
  6390. meta_gtk_state_from_string (const char *str)
  6391. {
  6392.   if (g_ascii_strcasecmp ("normal", str) == 0)
  6393.     return GTK_STATE_FLAG_NORMAL;
  6394.   else if (g_ascii_strcasecmp ("prelight", str) == 0)
  6395.     return GTK_STATE_FLAG_PRELIGHT;
  6396.   else if (g_ascii_strcasecmp ("active", str) == 0)
  6397.     return GTK_STATE_FLAG_ACTIVE;
  6398.   else if (g_ascii_strcasecmp ("selected", str) == 0)
  6399.     return GTK_STATE_FLAG_SELECTED;
  6400.   else if (g_ascii_strcasecmp ("insensitive", str) == 0)
  6401.     return GTK_STATE_FLAG_INSENSITIVE;
  6402.   else if (g_ascii_strcasecmp ("inconsistent", str) == 0)
  6403.     return GTK_STATE_FLAG_INCONSISTENT;
  6404.   else if (g_ascii_strcasecmp ("focused", str) == 0)
  6405.     return GTK_STATE_FLAG_FOCUSED;
  6406.   else
  6407.     return -1; /* hack */
  6408. }
  6409.  
  6410. const char*
  6411. meta_gtk_state_to_string (GtkStateFlags state)
  6412. {
  6413.   switch (state)
  6414.     {
  6415.     case GTK_STATE_FLAG_NORMAL:
  6416.       return "NORMAL";
  6417.     case GTK_STATE_FLAG_PRELIGHT:
  6418.       return "PRELIGHT";
  6419.     case GTK_STATE_FLAG_ACTIVE:
  6420.       return "ACTIVE";
  6421.     case GTK_STATE_FLAG_SELECTED:
  6422.       return "SELECTED";
  6423.     case GTK_STATE_FLAG_INSENSITIVE:
  6424.       return "INSENSITIVE";
  6425.     case GTK_STATE_FLAG_INCONSISTENT:
  6426.       return "INCONSISTENT";
  6427.     case GTK_STATE_FLAG_FOCUSED:
  6428.       return "FOCUSED";
  6429.     }
  6430.  
  6431.   return "<unknown>";
  6432. }
  6433.  
  6434. GtkShadowType
  6435. meta_gtk_shadow_from_string (const char *str)
  6436. {
  6437.   if (strcmp ("none", str) == 0)
  6438.     return GTK_SHADOW_NONE;
  6439.   else if (strcmp ("in", str) == 0)
  6440.     return GTK_SHADOW_IN;
  6441.   else if (strcmp ("out", str) == 0)
  6442.     return GTK_SHADOW_OUT;
  6443.   else if (strcmp ("etched_in", str) == 0)
  6444.     return GTK_SHADOW_ETCHED_IN;
  6445.   else if (strcmp ("etched_out", str) == 0)
  6446.     return GTK_SHADOW_ETCHED_OUT;
  6447.   else
  6448.     return -1;
  6449. }
  6450.  
  6451. const char*
  6452. meta_gtk_shadow_to_string (GtkShadowType shadow)
  6453. {
  6454.   switch (shadow)
  6455.     {
  6456.     case GTK_SHADOW_NONE:
  6457.       return "none";
  6458.     case GTK_SHADOW_IN:
  6459.       return "in";
  6460.     case GTK_SHADOW_OUT:
  6461.       return "out";
  6462.     case GTK_SHADOW_ETCHED_IN:
  6463.       return "etched_in";
  6464.     case GTK_SHADOW_ETCHED_OUT:
  6465.       return "etched_out";
  6466.     }
  6467.  
  6468.   return "<unknown>";
  6469. }
  6470.  
  6471. GtkArrowType
  6472. meta_gtk_arrow_from_string (const char *str)
  6473. {
  6474.   if (strcmp ("up", str) == 0)
  6475.     return GTK_ARROW_UP;
  6476.   else if (strcmp ("down", str) == 0)
  6477.     return GTK_ARROW_DOWN;
  6478.   else if (strcmp ("left", str) == 0)
  6479.     return GTK_ARROW_LEFT;
  6480.   else if (strcmp ("right", str) == 0)
  6481.     return GTK_ARROW_RIGHT;
  6482.   else if (strcmp ("none", str) == 0)
  6483.     return GTK_ARROW_NONE;
  6484.   else
  6485.     return -1;
  6486. }
  6487.  
  6488. const char*
  6489. meta_gtk_arrow_to_string (GtkArrowType arrow)
  6490. {
  6491.   switch (arrow)
  6492.     {
  6493.     case GTK_ARROW_UP:
  6494.       return "up";
  6495.     case GTK_ARROW_DOWN:
  6496.       return "down";
  6497.     case GTK_ARROW_LEFT:
  6498.       return "left";
  6499.     case GTK_ARROW_RIGHT:
  6500.       return "right";
  6501.     case GTK_ARROW_NONE:
  6502.       return "none";
  6503.     }
  6504.  
  6505.   return "<unknown>";
  6506. }
  6507.  
  6508. /**
  6509.  * Returns a fill_type from a string.  The inverse of
  6510.  * meta_image_fill_type_to_string().
  6511.  *
  6512.  * \param str  a string representing a fill_type
  6513.  * \result  the fill_type, or -1 if it represents no fill_type.
  6514.  */
  6515. MetaImageFillType
  6516. meta_image_fill_type_from_string (const char *str)
  6517. {
  6518.   if (strcmp ("tile", str) == 0)
  6519.     return META_IMAGE_FILL_TILE;
  6520.   else if (strcmp ("scale", str) == 0)
  6521.     return META_IMAGE_FILL_SCALE;
  6522.   else
  6523.     return -1;
  6524. }
  6525.  
  6526. /**
  6527.  * Returns a string representation of a fill_type.  The inverse of
  6528.  * meta_image_fill_type_from_string().
  6529.  *
  6530.  * \param fill_type  the fill type
  6531.  * \result  a string representing that type
  6532.  */
  6533. const char*
  6534. meta_image_fill_type_to_string (MetaImageFillType fill_type)
  6535. {
  6536.   switch (fill_type)
  6537.     {
  6538.     case META_IMAGE_FILL_TILE:
  6539.       return "tile";
  6540.     case META_IMAGE_FILL_SCALE:
  6541.       return "scale";
  6542.     }
  6543.  
  6544.   return "<unknown>";
  6545. }
  6546.  
  6547. /**
  6548.  * Takes a colour "a", scales the lightness and saturation by a certain amount,
  6549.  * and sets "b" to the resulting colour.
  6550.  * gtkstyle.c cut-and-pastage.
  6551.  *
  6552.  * \param a  the starting colour
  6553.  * \param b  [out] the resulting colour
  6554.  * \param k  amount to scale lightness and saturation by
  6555.  */
  6556. static void
  6557. gtk_style_shade (GdkRGBA *a,
  6558.                  GdkRGBA *b,
  6559.                  gdouble   k)
  6560. {
  6561.   gdouble red;
  6562.   gdouble green;
  6563.   gdouble blue;
  6564.  
  6565.   red = a->red;
  6566.   green = a->green;
  6567.   blue = a->blue;
  6568.  
  6569.   rgb_to_hls (&red, &green, &blue);
  6570.  
  6571.   green *= k;
  6572.   if (green > 1.0)
  6573.     green = 1.0;
  6574.   else if (green < 0.0)
  6575.     green = 0.0;
  6576.  
  6577.   blue *= k;
  6578.   if (blue > 1.0)
  6579.     blue = 1.0;
  6580.   else if (blue < 0.0)
  6581.     blue = 0.0;
  6582.  
  6583.   hls_to_rgb (&red, &green, &blue);
  6584.  
  6585.   b->red = red;
  6586.   b->green = green;
  6587.   b->blue = blue;
  6588. }
  6589.  
  6590. /**
  6591.  * Converts a red/green/blue triplet to a hue/lightness/saturation triplet.
  6592.  *
  6593.  * \param r  on input, red; on output, hue
  6594.  * \param g  on input, green; on output, lightness
  6595.  * \param b  on input, blue; on output, saturation
  6596.  */
  6597. static void
  6598. rgb_to_hls (gdouble *r,
  6599.             gdouble *g,
  6600.             gdouble *b)
  6601. {
  6602.   gdouble min;
  6603.   gdouble max;
  6604.   gdouble red;
  6605.   gdouble green;
  6606.   gdouble blue;
  6607.   gdouble h, l, s;
  6608.   gdouble delta;
  6609.  
  6610.   red = *r;
  6611.   green = *g;
  6612.   blue = *b;
  6613.  
  6614.   if (red > green)
  6615.     {
  6616.       if (red > blue)
  6617.         max = red;
  6618.       else
  6619.         max = blue;
  6620.      
  6621.       if (green < blue)
  6622.         min = green;
  6623.       else
  6624.         min = blue;
  6625.     }
  6626.   else
  6627.     {
  6628.       if (green > blue)
  6629.         max = green;
  6630.       else
  6631.         max = blue;
  6632.      
  6633.       if (red < blue)
  6634.         min = red;
  6635.       else
  6636.         min = blue;
  6637.     }
  6638.  
  6639.   l = (max + min) / 2;
  6640.   s = 0;
  6641.   h = 0;
  6642.  
  6643.   if (max != min)
  6644.     {
  6645.       if (l <= 0.5)
  6646.         s = (max - min) / (max + min);
  6647.       else
  6648.         s = (max - min) / (2 - max - min);
  6649.      
  6650.       delta = max -min;
  6651.       if (red == max)
  6652.         h = (green - blue) / delta;
  6653.       else if (green == max)
  6654.         h = 2 + (blue - red) / delta;
  6655.       else if (blue == max)
  6656.         h = 4 + (red - green) / delta;
  6657.      
  6658.       h *= 60;
  6659.       if (h < 0.0)
  6660.         h += 360;
  6661.     }
  6662.  
  6663.   *r = h;
  6664.   *g = l;
  6665.   *b = s;
  6666. }
  6667.  
  6668. /**
  6669.  * Converts a hue/lightness/saturation triplet to a red/green/blue triplet.
  6670.  *
  6671.  * \param h  on input, hue; on output, red
  6672.  * \param l  on input, lightness; on output, green
  6673.  * \param s  on input, saturation; on output, blue
  6674.  */
  6675. static void
  6676. hls_to_rgb (gdouble *h,
  6677.             gdouble *l,
  6678.             gdouble *s)
  6679. {
  6680.   gdouble hue;
  6681.   gdouble lightness;
  6682.   gdouble saturation;
  6683.   gdouble m1, m2;
  6684.   gdouble r, g, b;
  6685.  
  6686.   lightness = *l;
  6687.   saturation = *s;
  6688.  
  6689.   if (lightness <= 0.5)
  6690.     m2 = lightness * (1 + saturation);
  6691.   else
  6692.     m2 = lightness + saturation - lightness * saturation;
  6693.   m1 = 2 * lightness - m2;
  6694.  
  6695.   if (saturation == 0)
  6696.     {
  6697.       *h = lightness;
  6698.       *l = lightness;
  6699.       *s = lightness;
  6700.     }
  6701.   else
  6702.     {
  6703.       hue = *h + 120;
  6704.       while (hue > 360)
  6705.         hue -= 360;
  6706.       while (hue < 0)
  6707.         hue += 360;
  6708.      
  6709.       if (hue < 60)
  6710.         r = m1 + (m2 - m1) * hue / 60;
  6711.       else if (hue < 180)
  6712.         r = m2;
  6713.       else if (hue < 240)
  6714.         r = m1 + (m2 - m1) * (240 - hue) / 60;
  6715.       else
  6716.         r = m1;
  6717.      
  6718.       hue = *h;
  6719.       while (hue > 360)
  6720.         hue -= 360;
  6721.       while (hue < 0)
  6722.         hue += 360;
  6723.      
  6724.       if (hue < 60)
  6725.         g = m1 + (m2 - m1) * hue / 60;
  6726.       else if (hue < 180)
  6727.         g = m2;
  6728.       else if (hue < 240)
  6729.         g = m1 + (m2 - m1) * (240 - hue) / 60;
  6730.       else
  6731.         g = m1;
  6732.      
  6733.       hue = *h - 120;
  6734.       while (hue > 360)
  6735.         hue -= 360;
  6736.       while (hue < 0)
  6737.         hue += 360;
  6738.      
  6739.       if (hue < 60)
  6740.         b = m1 + (m2 - m1) * hue / 60;
  6741.       else if (hue < 180)
  6742.         b = m2;
  6743.       else if (hue < 240)
  6744.         b = m1 + (m2 - m1) * (240 - hue) / 60;
  6745.       else
  6746.         b = m1;
  6747.      
  6748.       *h = r;
  6749.       *l = g;
  6750.       *s = b;
  6751.     }
  6752. }
  6753.  
  6754. #if 0
  6755. /* These are some functions I'm saving to use in optimizing
  6756.  * MetaDrawOpList, namely to pre-composite pixbufs on client side
  6757.  * prior to rendering to the server
  6758.  */
  6759. static void
  6760. draw_bg_solid_composite (const MetaTextureSpec *bg,
  6761.                          const MetaTextureSpec *fg,
  6762.                          double                 alpha,
  6763.                          GtkWidget             *widget,
  6764.                          GdkDrawable           *drawable,
  6765.                          const GdkRectangle    *clip,
  6766.                          MetaTextureDrawMode    mode,
  6767.                          double                 xalign,
  6768.                          double                 yalign,
  6769.                          int                    x,
  6770.                          int                    y,
  6771.                          int                    width,
  6772.                          int                    height)
  6773. {
  6774.   GdkRGBA bg_color;
  6775.  
  6776.   g_assert (bg->type == META_TEXTURE_SOLID);
  6777.   g_assert (fg->type != META_TEXTURE_COMPOSITE);
  6778.   g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
  6779.  
  6780.   meta_color_spec_render (bg->data.solid.color_spec,
  6781.                           widget,
  6782.                           &bg_color);
  6783.  
  6784.   switch (fg->type)
  6785.     {
  6786.     case META_TEXTURE_SOLID:
  6787.       {
  6788.         GdkRGBA fg_color;
  6789.  
  6790.         meta_color_spec_render (fg->data.solid.color_spec,
  6791.                                 widget,
  6792.                                 &fg_color);
  6793.  
  6794.         color_composite (&bg_color, &fg_color,
  6795.                          alpha, &fg_color);
  6796.  
  6797.         draw_color_rectangle (widget, drawable, &fg_color, clip,
  6798.                               x, y, width, height);
  6799.       }
  6800.       break;
  6801.  
  6802.     case META_TEXTURE_GRADIENT:
  6803.       /* FIXME I think we could just composite all the colors in
  6804.        * the gradient prior to generating the gradient?
  6805.        */
  6806.       /* FALL THRU */
  6807.     case META_TEXTURE_IMAGE:
  6808.       {
  6809.         GdkPixbuf *pixbuf;
  6810.         GdkPixbuf *composited;
  6811.  
  6812.         pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
  6813.                                            width, height);
  6814.  
  6815.         if (pixbuf == NULL)
  6816.           return;
  6817.  
  6818.         composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
  6819.                                      gdk_pixbuf_get_has_alpha (pixbuf), 8,
  6820.                                      gdk_pixbuf_get_width (pixbuf),
  6821.                                      gdk_pixbuf_get_height (pixbuf));
  6822.  
  6823.         if (composited == NULL)
  6824.           {
  6825.             g_object_unref (G_OBJECT (pixbuf));
  6826.             return;
  6827.           }
  6828.  
  6829.         gdk_pixbuf_composite_color (pixbuf,
  6830.                                     composited,
  6831.                                     0, 0,
  6832.                                     gdk_pixbuf_get_width (pixbuf),
  6833.                                     gdk_pixbuf_get_height (pixbuf),
  6834.                                     0.0, 0.0, /* offsets */
  6835.                                     1.0, 1.0, /* scale */
  6836.                                     GDK_INTERP_BILINEAR,
  6837.                                     255 * alpha,
  6838.                                     0, 0,     /* check offsets */
  6839.                                     0,        /* check size */
  6840.                                     GDK_COLOR_RGB (bg_color),
  6841.                                     GDK_COLOR_RGB (bg_color));
  6842.  
  6843.         /* Need to draw background since pixbuf is not
  6844.          * necessarily covering the whole thing
  6845.          */
  6846.         draw_color_rectangle (widget, drawable, &bg_color, clip,
  6847.                               x, y, width, height);
  6848.  
  6849.         render_pixbuf_aligned (drawable, clip, composited,
  6850.                                xalign, yalign,
  6851.                                x, y, width, height);
  6852.  
  6853.         g_object_unref (G_OBJECT (pixbuf));
  6854.         g_object_unref (G_OBJECT (composited));
  6855.       }
  6856.       break;
  6857.  
  6858.     case META_TEXTURE_BLANK:
  6859.     case META_TEXTURE_COMPOSITE:
  6860.     case META_TEXTURE_SHAPE_LIST:
  6861.       g_assert_not_reached ();
  6862.       break;
  6863.     }
  6864. }
  6865.  
  6866. static void
  6867. draw_bg_gradient_composite (const MetaTextureSpec *bg,
  6868.                             const MetaTextureSpec *fg,
  6869.                             double                 alpha,
  6870.                             GtkWidget             *widget,
  6871.                             GdkDrawable           *drawable,
  6872.                             const GdkRectangle    *clip,
  6873.                             MetaTextureDrawMode    mode,
  6874.                             double                 xalign,
  6875.                             double                 yalign,
  6876.                             int                    x,
  6877.                             int                    y,
  6878.                             int                    width,
  6879.                             int                    height)
  6880. {
  6881.   g_assert (bg->type == META_TEXTURE_GRADIENT);
  6882.   g_assert (fg->type != META_TEXTURE_COMPOSITE);
  6883.   g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
  6884.  
  6885.   switch (fg->type)
  6886.     {
  6887.     case META_TEXTURE_SOLID:
  6888.     case META_TEXTURE_GRADIENT:
  6889.     case META_TEXTURE_IMAGE:
  6890.       {
  6891.         GdkPixbuf *bg_pixbuf;
  6892.         GdkPixbuf *fg_pixbuf;
  6893.         GdkPixbuf *composited;
  6894.         int fg_width, fg_height;
  6895.  
  6896.         bg_pixbuf = meta_texture_spec_render (bg, widget, mode, 255,
  6897.                                               width, height);
  6898.  
  6899.         if (bg_pixbuf == NULL)
  6900.           return;
  6901.  
  6902.         fg_pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
  6903.                                               width, height);
  6904.  
  6905.         if (fg_pixbuf == NULL)
  6906.           {
  6907.             g_object_unref (G_OBJECT (bg_pixbuf));
  6908.             return;
  6909.           }
  6910.  
  6911.         /* gradients always fill the entire target area */
  6912.         g_assert (gdk_pixbuf_get_width (bg_pixbuf) == width);
  6913.         g_assert (gdk_pixbuf_get_height (bg_pixbuf) == height);
  6914.  
  6915.         composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
  6916.                                      gdk_pixbuf_get_has_alpha (bg_pixbuf), 8,
  6917.                                      gdk_pixbuf_get_width (bg_pixbuf),
  6918.                                      gdk_pixbuf_get_height (bg_pixbuf));
  6919.  
  6920.         if (composited == NULL)
  6921.           {
  6922.             g_object_unref (G_OBJECT (bg_pixbuf));
  6923.             g_object_unref (G_OBJECT (fg_pixbuf));
  6924.             return;
  6925.           }
  6926.  
  6927.         fg_width = gdk_pixbuf_get_width (fg_pixbuf);
  6928.         fg_height = gdk_pixbuf_get_height (fg_pixbuf);
  6929.  
  6930.         /* If we wanted to be all cool we could deal with the
  6931.          * offsets and try to composite only in the clip rectangle,
  6932.          * but I just don't care enough to figure it out.
  6933.          */
  6934.  
  6935.         gdk_pixbuf_composite (fg_pixbuf,
  6936.                               composited,
  6937.                               x + (width - fg_width) * xalign,
  6938.                               y + (height - fg_height) * yalign,
  6939.                               gdk_pixbuf_get_width (fg_pixbuf),
  6940.                               gdk_pixbuf_get_height (fg_pixbuf),
  6941.                               0.0, 0.0, /* offsets */
  6942.                               1.0, 1.0, /* scale */
  6943.                               GDK_INTERP_BILINEAR,
  6944.                               255 * alpha);
  6945.  
  6946.         gdk_cairo_set_source_pixbuf (cr, composited, x, y);
  6947.         cairo_paint (cr);
  6948.  
  6949.         g_object_unref (G_OBJECT (bg_pixbuf));
  6950.         g_object_unref (G_OBJECT (fg_pixbuf));
  6951.         g_object_unref (G_OBJECT (composited));
  6952.       }
  6953.       break;
  6954.  
  6955.     case META_TEXTURE_BLANK:
  6956.     case META_TEXTURE_SHAPE_LIST:
  6957.     case META_TEXTURE_COMPOSITE:
  6958.       g_assert_not_reached ();
  6959.       break;
  6960.     }
  6961. }
  6962. #endif
  6963.  
  6964. /**
  6965.  * Returns the earliest version of the theme format which required support
  6966.  * for a particular button.  (For example, "shade" first appeared in v2, and
  6967.  * "close" in v1.)
  6968.  *
  6969.  * \param type  the button type
  6970.  * \return  the number of the theme format
  6971.  */
  6972. guint
  6973. meta_theme_earliest_version_with_button (MetaButtonType type)
  6974. {
  6975.   switch (type)
  6976.     {
  6977.     case META_BUTTON_TYPE_CLOSE:
  6978.     case META_BUTTON_TYPE_MAXIMIZE:
  6979.     case META_BUTTON_TYPE_MINIMIZE:
  6980.     case META_BUTTON_TYPE_MENU:
  6981.     case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
  6982.     case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
  6983.     case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
  6984.     case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
  6985.     case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
  6986.     case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
  6987.       return 1000;
  6988.      
  6989.     case META_BUTTON_TYPE_SHADE:
  6990.     case META_BUTTON_TYPE_ABOVE:
  6991.     case META_BUTTON_TYPE_STICK:
  6992.     case META_BUTTON_TYPE_UNSHADE:
  6993.     case META_BUTTON_TYPE_UNABOVE:
  6994.     case META_BUTTON_TYPE_UNSTICK:
  6995.       return 2000;
  6996.  
  6997.     case META_BUTTON_TYPE_LEFT_SINGLE_BACKGROUND:
  6998.     case META_BUTTON_TYPE_RIGHT_SINGLE_BACKGROUND:
  6999.       return 3003;
  7000.  
  7001.     default:
  7002.       meta_warning("Unknown button %d\n", type);
  7003.       return 1000;
  7004.     }
  7005. }
  7006.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement