Bug Summary

File:ui/theme.c
Warning:line 3179, column 8
Cast a region whose size is not a multiple of the destination type size

Annotated Source Code

Press '?' to see keyboard shortcuts

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