~muktupavels/metacity/adwaita-icon-theme-lp-1414613

« back to all changes in this revision

Viewing changes to src/theme.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Holbach
  • Date: 2005-10-03 22:44:28 UTC
  • mfrom: (1.2.1 upstream) (2.1.1 sarge)
  • Revision ID: james.westby@ubuntu.com-20051003224428-ft31gkmz12qpzohj
Tags: 1:2.12.1-0ubuntu1
* New upstream release:
  - Thanks to Ray Strode, Havoc Pennington, and Elijah Newren for
    improvements in this release.
  - Truncate ridiculously long titles to avoid crashing or letting the
    pager crash (Ray, Havoc, Elijah) [#315070] (Ubuntu: #15995)
  - Get the tabbing window outline to work with gtk+ 2.8.4 again
    (Elijah) [#317528] (Ubuntu: #16589)
  - Translations: Mahay Alam Khan (bn), Francisco Javier F. Serrador (es), 
    Ivar Smolin (et), I\uffffaki Larra\uffffaga Murgoitio (eu), Luca 
    Ferretti (it), Christian Rose (sv), Clytie Siddall (vi), Funda 
    Wang (zh_CN)
* debian/control.in:
  - Bumped Standards-Version.
* debian/patches/003_bordersdrawingfix.patch:
  - dropped, fixed upstream.

Show diffs side-by-side

added added

removed removed

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