~ubuntu-branches/ubuntu/wily/mutter/wily

« back to all changes in this revision

Viewing changes to .pc/19_gtk3_theme_states.patch/src/ui/theme.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Terry
  • Date: 2010-12-09 10:55:23 UTC
  • Revision ID: james.westby@ubuntu.com-20101209105523-a0fhznet2ssp9whv
Tags: 2.91.3-0ubuntu2
* Rebuild for libgirepository rename
* debian/patches/18_gtk3_gdk_macros.patch,
  debian/patches/19_gtk3_theme_states.patch,
  debian/patches/20_gtk3_testgradient.patch:
  - A collection of patches from upstream git to fix building against
    gtk3.

Show diffs side-by-side

added added

removed removed

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