~osomon/unity-2d/metacity-respect-launcher

« back to all changes in this revision

Viewing changes to .pc/15_show_maximized_titlebars.patch/src/ui/theme.c

  • Committer: Bazaar Package Importer
  • Author(s): Oliver Grawert
  • Date: 2011-01-19 13:20:50 UTC
  • Revision ID: james.westby@ubuntu.com-20110119132050-haxwdq0d7o8nlnc4
Tags: 1:2.30.3-0ubuntu3
* debian/patches/15_show_maximized_titlebars.patch:
  needed to make decorations work properly with the new unity-2d
* debian/control:
  add Breaks: entry to libmetacity-private0 for the current compiz version,
  since we need a compiz rebuild for the new ABI

Show diffs side-by-side

added added

removed removed

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