1
/* Metacity Theme Rendering */
4
* Copyright (C) 2001 Havoc Pennington
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License as
8
* published by the Free Software Foundation; either version 2 of the
9
* License, or (at your option) any later version.
11
* This program is distributed in the hope that it will be useful, but
12
* WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24
#include "theme-parser.h"
27
#include <gtk/gtkwidget.h>
32
#define GDK_COLOR_RGBA(color) \
34
(((color).red / 256) << 24) | \
35
(((color).green / 256) << 16) | \
36
(((color).blue / 256) << 8)))
38
#define GDK_COLOR_RGB(color) \
39
((guint32) ((((color).red / 256) << 16) | \
40
(((color).green / 256) << 8) | \
41
(((color).blue / 256))))
43
#define ALPHA_TO_UCHAR(d) ((unsigned char) ((d) * 255))
45
#define DEBUG_FILL_STRUCT(s) memset ((s), 0xef, sizeof (*(s)))
46
#define CLAMP_UCHAR(v) ((guchar) (CLAMP (((int)v), (int)0, (int)255)))
47
#define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
49
static void gtk_style_shade (GdkColor *a,
52
static void rgb_to_hls (gdouble *r,
55
static void hls_to_rgb (gdouble *h,
60
colorize_pixbuf (GdkPixbuf *orig,
72
const guchar *src_pixels;
75
pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (orig), gdk_pixbuf_get_has_alpha (orig),
76
gdk_pixbuf_get_bits_per_sample (orig),
77
gdk_pixbuf_get_width (orig), gdk_pixbuf_get_height (orig));
82
orig_rowstride = gdk_pixbuf_get_rowstride (orig);
83
dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
84
width = gdk_pixbuf_get_width (pixbuf);
85
height = gdk_pixbuf_get_height (pixbuf);
86
has_alpha = gdk_pixbuf_get_has_alpha (orig);
87
src_pixels = gdk_pixbuf_get_pixels (orig);
88
dest_pixels = gdk_pixbuf_get_pixels (pixbuf);
90
for (y = 0; y < height; y++)
92
src = src_pixels + y * orig_rowstride;
93
dest = dest_pixels + y * dest_rowstride;
95
for (x = 0; x < width; x++)
99
intensity = INTENSITY (src[0], src[1], src[2]) / 255.0;
101
if (intensity <= 0.5)
103
/* Go from black at intensity = 0.0 to new_color at intensity = 0.5 */
104
dr = (new_color->red * intensity * 2.0) / 65535.0;
105
dg = (new_color->green * intensity * 2.0) / 65535.0;
106
db = (new_color->blue * intensity * 2.0) / 65535.0;
110
/* Go from new_color at intensity = 0.5 to white at intensity = 1.0 */
111
dr = (new_color->red + (65535 - new_color->red) * (intensity - 0.5) * 2.0) / 65535.0;
112
dg = (new_color->green + (65535 - new_color->green) * (intensity - 0.5) * 2.0) / 65535.0;
113
db = (new_color->blue + (65535 - new_color->blue) * (intensity - 0.5) * 2.0) / 65535.0;
116
dest[0] = CLAMP_UCHAR (255 * dr);
117
dest[1] = CLAMP_UCHAR (255 * dg);
118
dest[2] = CLAMP_UCHAR (255 * db);
138
color_composite (const GdkColor *bg,
146
alpha = alpha_d * 0xffff;
147
color->red = color->red + (((fg->red - color->red) * alpha + 0x8000) >> 16);
148
color->green = color->green + (((fg->green - color->green) * alpha + 0x8000) >> 16);
149
color->blue = color->blue + (((fg->blue - color->blue) * alpha + 0x8000) >> 16);
153
init_border (GtkBorder *border)
162
meta_frame_layout_new (void)
164
MetaFrameLayout *layout;
166
layout = g_new0 (MetaFrameLayout, 1);
168
layout->refcount = 1;
170
/* Fill with -1 values to detect invalid themes */
171
layout->left_width = -1;
172
layout->right_width = -1;
173
layout->bottom_height = -1;
175
init_border (&layout->title_border);
177
layout->title_vertical_pad = -1;
179
layout->right_titlebar_edge = -1;
180
layout->left_titlebar_edge = -1;
182
layout->button_sizing = META_BUTTON_SIZING_LAST;
183
layout->button_aspect = 1.0;
184
layout->button_width = -1;
185
layout->button_height = -1;
187
layout->has_title = TRUE;
188
layout->title_scale = 1.0;
190
init_border (&layout->button_border);
196
validate_border (const GtkBorder *border,
203
else if (border->bottom < 0)
205
else if (border->left < 0)
207
else if (border->right < 0)
214
validate_geometry_value (int val,
220
g_set_error (error, META_THEME_ERROR,
221
META_THEME_ERROR_FRAME_GEOMETRY,
222
_("frame geometry does not specify \"%s\" dimension"),
231
validate_geometry_border (const GtkBorder *border,
237
if (!validate_border (border, &bad))
239
g_set_error (error, META_THEME_ERROR,
240
META_THEME_ERROR_FRAME_GEOMETRY,
241
_("frame geometry does not specify dimension \"%s\" for border \"%s\""),
250
meta_frame_layout_validate (const MetaFrameLayout *layout,
253
g_return_val_if_fail (layout != NULL, FALSE);
255
#define CHECK_GEOMETRY_VALUE(vname) if (!validate_geometry_value (layout->vname, #vname, error)) return FALSE
257
#define CHECK_GEOMETRY_BORDER(bname) if (!validate_geometry_border (&layout->bname, #bname, error)) return FALSE
259
CHECK_GEOMETRY_VALUE (left_width);
260
CHECK_GEOMETRY_VALUE (right_width);
261
CHECK_GEOMETRY_VALUE (bottom_height);
263
CHECK_GEOMETRY_BORDER (title_border);
265
CHECK_GEOMETRY_VALUE (title_vertical_pad);
267
CHECK_GEOMETRY_VALUE (right_titlebar_edge);
268
CHECK_GEOMETRY_VALUE (left_titlebar_edge);
270
switch (layout->button_sizing)
272
case META_BUTTON_SIZING_ASPECT:
273
if (layout->button_aspect < (0.1) ||
274
layout->button_aspect > (15.0))
276
g_set_error (error, META_THEME_ERROR,
277
META_THEME_ERROR_FRAME_GEOMETRY,
278
_("Button aspect ratio %g is not reasonable"),
279
layout->button_aspect);
283
case META_BUTTON_SIZING_FIXED:
284
CHECK_GEOMETRY_VALUE (button_width);
285
CHECK_GEOMETRY_VALUE (button_height);
287
case META_BUTTON_SIZING_LAST:
288
g_set_error (error, META_THEME_ERROR,
289
META_THEME_ERROR_FRAME_GEOMETRY,
290
_("Frame geometry does not specify size of buttons"));
295
CHECK_GEOMETRY_BORDER (button_border);
301
meta_frame_layout_copy (const MetaFrameLayout *src)
303
MetaFrameLayout *layout;
305
layout = g_new0 (MetaFrameLayout, 1);
309
layout->refcount = 1;
315
meta_frame_layout_ref (MetaFrameLayout *layout)
317
g_return_if_fail (layout != NULL);
319
layout->refcount += 1;
323
meta_frame_layout_unref (MetaFrameLayout *layout)
325
g_return_if_fail (layout != NULL);
326
g_return_if_fail (layout->refcount > 0);
328
layout->refcount -= 1;
330
if (layout->refcount == 0)
332
DEBUG_FILL_STRUCT (layout);
338
meta_frame_layout_get_borders (const MetaFrameLayout *layout,
340
MetaFrameFlags flags,
346
int buttons_height, title_height;
348
g_return_if_fail (top_height != NULL);
349
g_return_if_fail (bottom_height != NULL);
350
g_return_if_fail (left_width != NULL);
351
g_return_if_fail (right_width != NULL);
353
if (!layout->has_title)
356
buttons_height = layout->button_height +
357
layout->button_border.top + layout->button_border.bottom;
358
title_height = text_height +
359
layout->title_vertical_pad +
360
layout->title_border.top + layout->title_border.bottom;
364
*top_height = MAX (buttons_height, title_height);
368
*left_width = layout->left_width;
370
*right_width = layout->right_width;
374
if (flags & META_FRAME_SHADED)
377
*bottom_height = layout->bottom_height;
380
if (flags & META_FRAME_FULLSCREEN)
394
rect_for_function (MetaFrameGeometry *fgeom,
395
MetaFrameFlags flags,
396
MetaButtonFunction function)
400
case META_BUTTON_FUNCTION_MENU:
401
if (flags & META_FRAME_ALLOWS_MENU)
402
return &fgeom->menu_rect;
405
case META_BUTTON_FUNCTION_MINIMIZE:
406
if (flags & META_FRAME_ALLOWS_MINIMIZE)
407
return &fgeom->min_rect;
410
case META_BUTTON_FUNCTION_MAXIMIZE:
411
if (flags & META_FRAME_ALLOWS_MAXIMIZE)
412
return &fgeom->max_rect;
415
case META_BUTTON_FUNCTION_CLOSE:
416
if (flags & META_FRAME_ALLOWS_DELETE)
417
return &fgeom->close_rect;
420
case META_BUTTON_FUNCTION_LAST:
428
strip_button (GdkRectangle *func_rects[MAX_BUTTONS_PER_CORNER],
429
GdkRectangle *bg_rects[MAX_BUTTONS_PER_CORNER],
431
GdkRectangle *to_strip)
438
if (func_rects[i] == to_strip)
442
/* shift the other rects back in the array */
445
func_rects[i] = func_rects[i+1];
446
bg_rects[i] = bg_rects[i+1];
451
func_rects[i] = NULL;
460
return FALSE; /* did not strip anything */
464
meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
466
MetaFrameFlags flags,
469
const MetaButtonLayout *button_layout,
470
MetaFrameGeometry *fgeom)
472
int i, n_left, n_right;
475
int title_right_edge;
477
int button_width, button_height;
478
int min_size_for_rounding;
480
/* the left/right rects in order; the max # of rects
481
* is the number of button functions
483
GdkRectangle *left_func_rects[MAX_BUTTONS_PER_CORNER];
484
GdkRectangle *right_func_rects[MAX_BUTTONS_PER_CORNER];
485
GdkRectangle *left_bg_rects[MAX_BUTTONS_PER_CORNER];
486
GdkRectangle *right_bg_rects[MAX_BUTTONS_PER_CORNER];
488
meta_frame_layout_get_borders (layout, text_height,
491
&fgeom->bottom_height,
493
&fgeom->right_width);
495
width = client_width + fgeom->left_width + fgeom->right_width;
497
height = ((flags & META_FRAME_SHADED) ? 0: client_height) +
498
fgeom->top_height + fgeom->bottom_height;
500
fgeom->width = width;
501
fgeom->height = height;
503
fgeom->top_titlebar_edge = layout->title_border.top;
504
fgeom->bottom_titlebar_edge = layout->title_border.bottom;
505
fgeom->left_titlebar_edge = layout->left_titlebar_edge;
506
fgeom->right_titlebar_edge = layout->right_titlebar_edge;
512
switch (layout->button_sizing)
514
case META_BUTTON_SIZING_ASPECT:
515
button_height = fgeom->top_height - layout->button_border.top - layout->button_border.bottom;
516
button_width = button_height / layout->button_aspect;
518
case META_BUTTON_SIZING_FIXED:
519
button_width = layout->button_width;
520
button_height = layout->button_height;
522
case META_BUTTON_SIZING_LAST:
523
g_assert_not_reached ();
527
/* FIXME all this code sort of pretends that duplicate buttons
528
* with the same function are allowed, but that breaks the
529
* code in frames.c, so isn't really allowed right now.
530
* Would need left_close_rect, right_close_rect, etc.
533
/* Init all button rects to 0, lame hack */
534
memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0',
535
LENGTH_OF_BUTTON_RECTS);
540
while (i < MAX_BUTTONS_PER_CORNER)
542
/* NULL all unused */
543
left_func_rects[i] = NULL;
544
right_func_rects[i] = NULL;
546
/* Try to fill in rects */
547
if (button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST)
549
left_func_rects[n_left] = rect_for_function (fgeom, flags,
550
button_layout->left_buttons[i]);
551
if (left_func_rects[n_left] != NULL)
555
if (button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST)
557
right_func_rects[n_right] = rect_for_function (fgeom, flags,
558
button_layout->right_buttons[i]);
559
if (right_func_rects[n_right] != NULL)
567
while (i < MAX_BUTTONS_PER_CORNER)
569
left_bg_rects[i] = NULL;
570
right_bg_rects[i] = NULL;
578
if (i == 0) /* prefer left background if only one button */
579
left_bg_rects[i] = &fgeom->left_left_background;
580
else if (i == (n_left - 1))
581
left_bg_rects[i] = &fgeom->left_right_background;
583
left_bg_rects[i] = &fgeom->left_middle_backgrounds[i-1];
591
if (i == (n_right - 1)) /* prefer right background if only one button */
592
right_bg_rects[i] = &fgeom->right_right_background;
594
right_bg_rects[i] = &fgeom->right_left_background;
596
right_bg_rects[i] = &fgeom->right_middle_backgrounds[i-1];
601
/* Be sure buttons fit */
602
while (n_left > 0 || n_right > 0)
604
int space_used_by_buttons;
607
space_available = fgeom->width - layout->left_titlebar_edge - layout->right_titlebar_edge;
609
space_used_by_buttons = 0;
614
space_used_by_buttons += button_width;
617
space_used_by_buttons += layout->button_border.left + layout->button_border.right;
625
space_used_by_buttons += button_width;
628
space_used_by_buttons += layout->button_border.left + layout->button_border.right;
633
if (space_used_by_buttons <= space_available)
634
break; /* Everything fits, bail out */
636
/* Otherwise we need to shave out a button. Shave
637
* min, max, close, then menu (menu is most useful);
638
* prefer the default button locations.
640
if (strip_button (left_func_rects, left_bg_rects,
641
&n_left, &fgeom->min_rect))
643
else if (strip_button (right_func_rects, right_bg_rects,
644
&n_right, &fgeom->min_rect))
646
else if (strip_button (left_func_rects, left_bg_rects,
647
&n_left, &fgeom->max_rect))
649
else if (strip_button (right_func_rects, right_bg_rects,
650
&n_right, &fgeom->max_rect))
652
else if (strip_button (left_func_rects, left_bg_rects,
653
&n_left, &fgeom->close_rect))
655
else if (strip_button (right_func_rects, right_bg_rects,
656
&n_right, &fgeom->close_rect))
658
else if (strip_button (right_func_rects, right_bg_rects,
659
&n_right, &fgeom->menu_rect))
661
else if (strip_button (left_func_rects, left_bg_rects,
662
&n_left, &fgeom->menu_rect))
666
meta_bug ("Could not find a button to strip. n_left = %d n_right = %d\n",
671
/* center buttons vertically */
672
button_y = (fgeom->top_height -
673
(button_height + layout->button_border.top + layout->button_border.bottom)) / 2 + layout->button_border.top;
675
/* right edge of farthest-right button */
676
x = width - layout->right_titlebar_edge;
683
if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */
686
rect = right_func_rects[i];
688
rect->x = x - layout->button_border.right - button_width;
690
rect->width = button_width;
691
rect->height = button_height;
693
*(right_bg_rects[i]) = *rect;
695
x = rect->x - layout->button_border.left;
700
/* save right edge of titlebar for later use */
701
title_right_edge = x - layout->title_border.right;
703
/* Now x changes to be position from the left and we go through
704
* the left-side buttons
706
x = layout->left_titlebar_edge;
713
rect = left_func_rects[i];
715
rect->x = x + layout->button_border.left;
717
rect->width = button_width;
718
rect->height = button_height;
720
x = rect->x + rect->width + layout->button_border.right;
722
*(left_bg_rects[i]) = *rect;
727
/* We always fill as much vertical space as possible with title rect,
728
* rather than centering it like the buttons
730
fgeom->title_rect.x = x + layout->title_border.left;
731
fgeom->title_rect.y = layout->title_border.top;
732
fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x;
733
fgeom->title_rect.height = fgeom->top_height - layout->title_border.top - layout->title_border.bottom;
735
/* Nuke title if it won't fit */
736
if (fgeom->title_rect.width < 0 ||
737
fgeom->title_rect.height < 0)
739
fgeom->title_rect.width = 0;
740
fgeom->title_rect.height = 0;
743
if (flags & META_FRAME_SHADED)
744
min_size_for_rounding = 0;
746
min_size_for_rounding = 5;
748
fgeom->top_left_corner_rounded = FALSE;
749
fgeom->top_right_corner_rounded = FALSE;
750
fgeom->bottom_left_corner_rounded = FALSE;
751
fgeom->bottom_right_corner_rounded = FALSE;
753
if (fgeom->top_height + fgeom->left_width >= min_size_for_rounding)
754
fgeom->top_left_corner_rounded = layout->top_left_corner_rounded;
755
if (fgeom->top_height + fgeom->right_width >= min_size_for_rounding)
756
fgeom->top_right_corner_rounded = layout->top_right_corner_rounded;
758
if (fgeom->bottom_height + fgeom->left_width >= min_size_for_rounding)
759
fgeom->bottom_left_corner_rounded = layout->bottom_left_corner_rounded;
760
if (fgeom->bottom_height + fgeom->right_width >= min_size_for_rounding)
761
fgeom->bottom_right_corner_rounded = layout->bottom_right_corner_rounded;
765
meta_gradient_spec_new (MetaGradientType type)
767
MetaGradientSpec *spec;
769
spec = g_new (MetaGradientSpec, 1);
772
spec->color_specs = NULL;
778
free_color_spec (gpointer spec, gpointer user_data)
780
meta_color_spec_free (spec);
784
meta_gradient_spec_free (MetaGradientSpec *spec)
786
g_return_if_fail (spec != NULL);
788
g_slist_foreach (spec->color_specs, free_color_spec, NULL);
789
g_slist_free (spec->color_specs);
791
DEBUG_FILL_STRUCT (spec);
796
meta_gradient_spec_render (const MetaGradientSpec *spec,
807
n_colors = g_slist_length (spec->color_specs);
812
colors = g_new (GdkColor, n_colors);
815
tmp = spec->color_specs;
818
meta_color_spec_render (tmp->data, widget, &colors[i]);
824
pixbuf = meta_gradient_create_multi (width, height,
834
meta_gradient_spec_validate (MetaGradientSpec *spec,
837
g_return_val_if_fail (spec != NULL, FALSE);
839
if (g_slist_length (spec->color_specs) < 2)
841
g_set_error (error, META_THEME_ERROR,
842
META_THEME_ERROR_FAILED,
843
_("Gradients should have at least two colors"));
850
MetaAlphaGradientSpec*
851
meta_alpha_gradient_spec_new (MetaGradientType type,
854
MetaAlphaGradientSpec *spec;
856
g_return_val_if_fail (n_alphas > 0, NULL);
858
spec = g_new0 (MetaAlphaGradientSpec, 1);
861
spec->alphas = g_new0 (unsigned char, n_alphas);
862
spec->n_alphas = n_alphas;
868
meta_alpha_gradient_spec_free (MetaAlphaGradientSpec *spec)
870
g_return_if_fail (spec != NULL);
872
g_free (spec->alphas);
877
meta_color_spec_new (MetaColorSpecType type)
883
size = G_STRUCT_OFFSET (MetaColorSpec, data);
887
case META_COLOR_SPEC_BASIC:
888
size += sizeof (dummy.data.basic);
891
case META_COLOR_SPEC_GTK:
892
size += sizeof (dummy.data.gtk);
895
case META_COLOR_SPEC_BLEND:
896
size += sizeof (dummy.data.blend);
899
case META_COLOR_SPEC_SHADE:
900
size += sizeof (dummy.data.shade);
904
spec = g_malloc0 (size);
912
meta_color_spec_free (MetaColorSpec *spec)
914
g_return_if_fail (spec != NULL);
918
case META_COLOR_SPEC_BASIC:
919
DEBUG_FILL_STRUCT (&spec->data.basic);
922
case META_COLOR_SPEC_GTK:
923
DEBUG_FILL_STRUCT (&spec->data.gtk);
926
case META_COLOR_SPEC_BLEND:
927
if (spec->data.blend.foreground)
928
meta_color_spec_free (spec->data.blend.foreground);
929
if (spec->data.blend.background)
930
meta_color_spec_free (spec->data.blend.background);
931
DEBUG_FILL_STRUCT (&spec->data.blend);
934
case META_COLOR_SPEC_SHADE:
935
if (spec->data.shade.base)
936
meta_color_spec_free (spec->data.shade.base);
937
DEBUG_FILL_STRUCT (&spec->data.shade);
945
meta_color_spec_new_from_string (const char *str,
952
if (str[0] == 'g' && str[1] == 't' && str[2] == 'k' && str[3] == ':')
956
const char *end_bracket;
959
MetaGtkColorComponent component;
962
while (*bracket && *bracket != '[')
965
if (*bracket == '\0')
967
g_set_error (err, META_THEME_ERROR,
968
META_THEME_ERROR_FAILED,
969
_("GTK color specification must have the state in brackets, e.g. gtk:fg[NORMAL] where NORMAL is the state; could not parse \"%s\""),
974
end_bracket = bracket;
976
while (*end_bracket && *end_bracket != ']')
979
if (*end_bracket == '\0')
981
g_set_error (err, META_THEME_ERROR,
982
META_THEME_ERROR_FAILED,
983
_("GTK color specification must have a close bracket after the state, e.g. gtk:fg[NORMAL] where NORMAL is the state; could not parse \"%s\""),
988
tmp = g_strndup (bracket + 1, end_bracket - bracket - 1);
989
state = meta_gtk_state_from_string (tmp);
990
if (((int) state) == -1)
992
g_set_error (err, META_THEME_ERROR,
993
META_THEME_ERROR_FAILED,
994
_("Did not understand state \"%s\" in color specification"),
1001
tmp = g_strndup (str + 4, bracket - str - 4);
1002
component = meta_color_component_from_string (tmp);
1003
if (component == META_GTK_COLOR_LAST)
1005
g_set_error (err, META_THEME_ERROR,
1006
META_THEME_ERROR_FAILED,
1007
_("Did not understand color component \"%s\" in color specification"),
1014
spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
1015
spec->data.gtk.state = state;
1016
spec->data.gtk.component = component;
1017
g_assert (spec->data.gtk.state < N_GTK_STATES);
1018
g_assert (spec->data.gtk.component < META_GTK_COLOR_LAST);
1020
else if (str[0] == 'b' && str[1] == 'l' && str[2] == 'e' && str[3] == 'n' &&
1021
str[4] == 'd' && str[5] == '/')
1030
split = g_strsplit (str, "/", 4);
1032
if (split[0] == NULL || split[1] == NULL ||
1033
split[2] == NULL || split[3] == NULL)
1035
g_set_error (err, META_THEME_ERROR,
1036
META_THEME_ERROR_FAILED,
1037
_("Blend format is \"blend/bg_color/fg_color/alpha\", \"%s\" does not fit the format"),
1043
alpha = g_ascii_strtod (split[3], &end);
1044
if (end == split[3])
1046
g_set_error (err, META_THEME_ERROR,
1047
META_THEME_ERROR_FAILED,
1048
_("Could not parse alpha value \"%s\" in blended color"),
1054
if (alpha < (0.0 - 1e6) || alpha > (1.0 + 1e6))
1056
g_set_error (err, META_THEME_ERROR,
1057
META_THEME_ERROR_FAILED,
1058
_("Alpha value \"%s\" in blended color is not between 0.0 and 1.0"),
1067
bg = meta_color_spec_new_from_string (split[1], err);
1074
fg = meta_color_spec_new_from_string (split[2], err);
1077
meta_color_spec_free (bg);
1084
spec = meta_color_spec_new (META_COLOR_SPEC_BLEND);
1085
spec->data.blend.alpha = alpha;
1086
spec->data.blend.background = bg;
1087
spec->data.blend.foreground = fg;
1089
else if (str[0] == 's' && str[1] == 'h' && str[2] == 'a' && str[3] == 'd' &&
1090
str[4] == 'e' && str[5] == '/')
1096
MetaColorSpec *base;
1098
split = g_strsplit (str, "/", 3);
1100
if (split[0] == NULL || split[1] == NULL ||
1103
g_set_error (err, META_THEME_ERROR,
1104
META_THEME_ERROR_FAILED,
1105
_("Shade format is \"shade/base_color/factor\", \"%s\" does not fit the format"),
1111
factor = g_ascii_strtod (split[2], &end);
1112
if (end == split[2])
1114
g_set_error (err, META_THEME_ERROR,
1115
META_THEME_ERROR_FAILED,
1116
_("Could not parse shade factor \"%s\" in shaded color"),
1122
if (factor < (0.0 - 1e6))
1124
g_set_error (err, META_THEME_ERROR,
1125
META_THEME_ERROR_FAILED,
1126
_("Shade factor \"%s\" in shaded color is negative"),
1134
base = meta_color_spec_new_from_string (split[1], err);
1143
spec = meta_color_spec_new (META_COLOR_SPEC_SHADE);
1144
spec->data.shade.factor = factor;
1145
spec->data.shade.base = base;
1149
spec = meta_color_spec_new (META_COLOR_SPEC_BASIC);
1151
if (!gdk_color_parse (str, &spec->data.basic.color))
1153
g_set_error (err, META_THEME_ERROR,
1154
META_THEME_ERROR_FAILED,
1155
_("Could not parse color \"%s\""),
1157
meta_color_spec_free (spec);
1168
meta_color_spec_new_gtk (MetaGtkColorComponent component,
1171
MetaColorSpec *spec;
1173
spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
1175
spec->data.gtk.component = component;
1176
spec->data.gtk.state = state;
1182
meta_color_spec_render (MetaColorSpec *spec,
1186
g_return_if_fail (spec != NULL);
1187
g_return_if_fail (GTK_IS_WIDGET (widget));
1188
g_return_if_fail (widget->style != NULL);
1192
case META_COLOR_SPEC_BASIC:
1193
*color = spec->data.basic.color;
1196
case META_COLOR_SPEC_GTK:
1197
switch (spec->data.gtk.component)
1199
case META_GTK_COLOR_BG:
1200
*color = widget->style->bg[spec->data.gtk.state];
1202
case META_GTK_COLOR_FG:
1203
*color = widget->style->fg[spec->data.gtk.state];
1205
case META_GTK_COLOR_BASE:
1206
*color = widget->style->base[spec->data.gtk.state];
1208
case META_GTK_COLOR_TEXT:
1209
*color = widget->style->text[spec->data.gtk.state];
1211
case META_GTK_COLOR_LIGHT:
1212
*color = widget->style->light[spec->data.gtk.state];
1214
case META_GTK_COLOR_DARK:
1215
*color = widget->style->dark[spec->data.gtk.state];
1217
case META_GTK_COLOR_MID:
1218
*color = widget->style->mid[spec->data.gtk.state];
1220
case META_GTK_COLOR_TEXT_AA:
1221
*color = widget->style->text_aa[spec->data.gtk.state];
1223
case META_GTK_COLOR_LAST:
1224
g_assert_not_reached ();
1229
case META_COLOR_SPEC_BLEND:
1233
meta_color_spec_render (spec->data.blend.background, widget, &bg);
1234
meta_color_spec_render (spec->data.blend.foreground, widget, &fg);
1236
color_composite (&bg, &fg, spec->data.blend.alpha, color);
1240
case META_COLOR_SPEC_SHADE:
1244
meta_color_spec_render (spec->data.shade.base, widget, &base);
1246
gtk_style_shade (&base, &base, spec->data.shade.factor);
1260
POS_TOKEN_OPEN_PAREN,
1261
POS_TOKEN_CLOSE_PAREN
1277
op_name (PosOperatorType type)
1283
case POS_OP_SUBTRACT:
1285
case POS_OP_MULTIPLY:
1302
static PosOperatorType
1303
op_from_string (const char *p,
1315
return POS_OP_SUBTRACT;
1318
return POS_OP_MULTIPLY;
1321
return POS_OP_DIVIDE;
1336
else if (p[0] == '`' &&
1378
free_tokens (PosToken *tokens,
1383
/* n_tokens can be 0 since tokens may have been allocated more than
1384
* it was initialized
1388
while (i < n_tokens)
1390
if (tokens[i].type == POS_TOKEN_VARIABLE)
1391
g_free (tokens[i].d.v.name);
1398
parse_number (const char *p,
1399
const char **end_return,
1403
const char *start = p;
1408
while (*p && (*p == '.' || g_ascii_isdigit (*p)))
1413
char buf[7] = { '\0' };
1414
buf[g_unichar_to_utf8 (g_utf8_get_char (p), buf)] = '\0';
1415
g_set_error (err, META_THEME_ERROR,
1416
META_THEME_ERROR_BAD_CHARACTER,
1417
_("Coordinate expression contains character '%s' which is not allowed"),
1424
/* we need this to exclude floats like "1e6" */
1425
num_str = g_strndup (start, p - start);
1437
next->type = POS_TOKEN_DOUBLE;
1438
next->d.d.val = g_ascii_strtod (num_str, &end);
1442
g_set_error (err, META_THEME_ERROR,
1443
META_THEME_ERROR_FAILED,
1444
_("Coordinate expression contains floating point number '%s' which could not be parsed"),
1452
next->type = POS_TOKEN_INT;
1453
next->d.i.val = strtol (num_str, &end, 10);
1456
g_set_error (err, META_THEME_ERROR,
1457
META_THEME_ERROR_FAILED,
1458
_("Coordinate expression contains integer '%s' which could not be parsed"),
1467
g_assert (next->type == POS_TOKEN_INT || next->type == POS_TOKEN_DOUBLE);
1472
#define IS_VARIABLE_CHAR(c) (g_ascii_isalpha ((c)) || (c) == '_')
1475
pos_tokenize (const char *expr,
1476
PosToken **tokens_p,
1490
tokens = g_new (PosToken, allocated);
1498
if (n_tokens == allocated)
1501
tokens = g_renew (PosToken, tokens, allocated);
1504
next = &tokens[n_tokens];
1511
case '-': /* negative numbers aren't allowed so this is easy */
1514
next->type = POS_TOKEN_OPERATOR;
1515
next->d.o.op = op_from_string (p, &len);
1516
if (next->d.o.op != POS_OP_NONE)
1519
p = p + (len - 1); /* -1 since we ++p later */
1523
g_set_error (err, META_THEME_ERROR,
1524
META_THEME_ERROR_FAILED,
1525
_("Coordinate expression contained unknown operator at the start of this text: \"%s\""),
1533
next->type = POS_TOKEN_OPEN_PAREN;
1538
next->type = POS_TOKEN_CLOSE_PAREN;
1548
if (IS_VARIABLE_CHAR (*p))
1550
/* Assume variable */
1551
const char *start = p;
1552
while (*p && IS_VARIABLE_CHAR (*p))
1554
g_assert (p != start);
1555
next->type = POS_TOKEN_VARIABLE;
1556
next->d.v.name = g_strndup (start, p - start);
1558
--p; /* since we ++p again at the end of while loop */
1565
if (!parse_number (p, &end, next, err))
1569
p = end - 1; /* -1 since we ++p again at the end of while loop */
1580
g_set_error (err, META_THEME_ERROR,
1581
META_THEME_ERROR_FAILED,
1582
_("Coordinate expression was empty or not understood"));
1588
*n_tokens_p = n_tokens;
1593
g_assert (err == NULL || *err != NULL);
1595
free_tokens (tokens, n_tokens);
1601
debug_print_tokens (PosToken *tokens,
1607
while (i < n_tokens)
1609
PosToken *t = &tokens[i];
1616
g_print ("\"%d\"", t->d.i.val);
1618
case POS_TOKEN_DOUBLE:
1619
g_print ("\"%g\"", t->d.d.val);
1621
case POS_TOKEN_OPEN_PAREN:
1624
case POS_TOKEN_CLOSE_PAREN:
1627
case POS_TOKEN_VARIABLE:
1628
g_print ("\"%s\"", t->d.v.name);
1630
case POS_TOKEN_OPERATOR:
1631
g_print ("\"%s\"", op_name (t->d.o.op));
1662
debug_print_exprs (PosExpr *exprs,
1670
switch (exprs[i].type)
1673
g_print (" %d", exprs[i].d.int_val);
1675
case POS_EXPR_DOUBLE:
1676
g_print (" %g", exprs[i].d.double_val);
1678
case POS_EXPR_OPERATOR:
1679
g_print (" %s", op_name (exprs[i].d.operator));
1690
do_operation (PosExpr *a,
1695
/* Promote types to double if required */
1696
if (a->type == POS_EXPR_DOUBLE ||
1697
b->type == POS_EXPR_DOUBLE)
1699
if (a->type != POS_EXPR_DOUBLE)
1701
a->type = POS_EXPR_DOUBLE;
1702
a->d.double_val = a->d.int_val;
1704
if (b->type != POS_EXPR_DOUBLE)
1706
b->type = POS_EXPR_DOUBLE;
1707
b->d.double_val = b->d.int_val;
1711
g_assert (a->type == b->type);
1713
if (a->type == POS_EXPR_INT)
1717
case POS_OP_MULTIPLY:
1718
a->d.int_val = a->d.int_val * b->d.int_val;
1721
if (b->d.int_val == 0)
1723
g_set_error (err, META_THEME_ERROR,
1724
META_THEME_ERROR_DIVIDE_BY_ZERO,
1725
_("Coordinate expression results in division by zero"));
1728
a->d.int_val = a->d.int_val / b->d.int_val;
1731
if (b->d.int_val == 0)
1733
g_set_error (err, META_THEME_ERROR,
1734
META_THEME_ERROR_DIVIDE_BY_ZERO,
1735
_("Coordinate expression results in division by zero"));
1738
a->d.int_val = a->d.int_val % b->d.int_val;
1741
a->d.int_val = a->d.int_val + b->d.int_val;
1743
case POS_OP_SUBTRACT:
1744
a->d.int_val = a->d.int_val - b->d.int_val;
1747
a->d.int_val = MAX (a->d.int_val, b->d.int_val);
1750
a->d.int_val = MIN (a->d.int_val, b->d.int_val);
1753
g_assert_not_reached ();
1757
else if (a->type == POS_EXPR_DOUBLE)
1761
case POS_OP_MULTIPLY:
1762
a->d.double_val = a->d.double_val * b->d.double_val;
1765
if (b->d.double_val == 0.0)
1767
g_set_error (err, META_THEME_ERROR,
1768
META_THEME_ERROR_DIVIDE_BY_ZERO,
1769
_("Coordinate expression results in division by zero"));
1772
a->d.double_val = a->d.double_val / b->d.double_val;
1775
g_set_error (err, META_THEME_ERROR,
1776
META_THEME_ERROR_MOD_ON_FLOAT,
1777
_("Coordinate expression tries to use mod operator on a floating-point number"));
1781
a->d.double_val = a->d.double_val + b->d.double_val;
1783
case POS_OP_SUBTRACT:
1784
a->d.double_val = a->d.double_val - b->d.double_val;
1787
a->d.double_val = MAX (a->d.double_val, b->d.double_val);
1790
a->d.double_val = MIN (a->d.double_val, b->d.double_val);
1793
g_assert_not_reached ();
1798
g_assert_not_reached ();
1804
do_operations (PosExpr *exprs,
1812
g_print ("Doing prec %d ops on %d exprs\n", precedence, *n_exprs);
1813
debug_print_exprs (exprs, *n_exprs);
1817
while (i < *n_exprs)
1821
/* exprs[i-1] first operand
1823
* exprs[i+1] second operand
1825
* we replace first operand with result of mul/div/mod,
1826
* or skip over operator and second operand if we have
1830
if (exprs[i-1].type == POS_EXPR_OPERATOR)
1832
g_set_error (err, META_THEME_ERROR,
1833
META_THEME_ERROR_FAILED,
1834
_("Coordinate expression has an operator \"%s\" where an operand was expected"),
1835
op_name (exprs[i-1].d.operator));
1839
if (exprs[i].type != POS_EXPR_OPERATOR)
1841
g_set_error (err, META_THEME_ERROR,
1842
META_THEME_ERROR_FAILED,
1843
_("Coordinate expression had an operand where an operator was expected"));
1847
if (i == (*n_exprs - 1))
1849
g_set_error (err, META_THEME_ERROR,
1850
META_THEME_ERROR_FAILED,
1851
_("Coordinate expression ended with an operator instead of an operand"));
1855
g_assert ((i+1) < *n_exprs);
1857
if (exprs[i+1].type == POS_EXPR_OPERATOR)
1859
g_set_error (err, META_THEME_ERROR,
1860
META_THEME_ERROR_FAILED,
1861
_("Coordinate expression has operator \"%c\" following operator \"%c\" with no operand in between"),
1862
exprs[i+1].d.operator,
1863
exprs[i].d.operator);
1872
switch (exprs[i].d.operator)
1876
case POS_OP_MULTIPLY:
1878
if (!do_operation (&exprs[i-1], &exprs[i+1],
1879
exprs[i].d.operator,
1886
switch (exprs[i].d.operator)
1889
case POS_OP_SUBTRACT:
1891
if (!do_operation (&exprs[i-1], &exprs[i+1],
1892
exprs[i].d.operator,
1898
/* I have no rationale at all for making these low-precedence */
1900
switch (exprs[i].d.operator)
1905
if (!do_operation (&exprs[i-1], &exprs[i+1],
1906
exprs[i].d.operator,
1916
/* exprs[i-1] first operand (now result)
1918
* exprs[i+1] second operand
1919
* exprs[i+2] new operator
1921
* we move new operator just after first operand
1923
if ((i+2) < *n_exprs)
1925
g_memmove (&exprs[i], &exprs[i+2],
1926
sizeof (PosExpr) * (*n_exprs - i - 2));
1933
/* Skip operator and next operand */
1942
pos_eval_helper (PosToken *tokens,
1944
const MetaPositionExprEnv *env,
1948
/* lazy-ass hardcoded limit on expression size */
1949
#define MAX_EXPRS 32
1953
PosExpr exprs[MAX_EXPRS];
1960
g_print ("Pos eval helper on %d tokens:\n", n_tokens);
1961
debug_print_tokens (tokens, n_tokens);
1964
/* Our first goal is to get a list of PosExpr, essentially
1965
* substituting variables and handling parentheses.
1972
while (i < n_tokens)
1974
PosToken *t = &tokens[i];
1976
if (n_exprs >= MAX_EXPRS)
1978
g_set_error (err, META_THEME_ERROR,
1979
META_THEME_ERROR_FAILED,
1980
_("Coordinate expression parser overflowed its buffer, this is really a Metacity bug, but are you sure you need a huge expression like that?"));
1984
if (paren_level == 0)
1989
exprs[n_exprs].type = POS_EXPR_INT;
1990
exprs[n_exprs].d.int_val = t->d.i.val;
1994
case POS_TOKEN_DOUBLE:
1995
exprs[n_exprs].type = POS_EXPR_DOUBLE;
1996
exprs[n_exprs].d.double_val = t->d.d.val;
2000
case POS_TOKEN_OPEN_PAREN:
2002
if (paren_level == 1)
2006
case POS_TOKEN_CLOSE_PAREN:
2007
g_set_error (err, META_THEME_ERROR,
2008
META_THEME_ERROR_BAD_PARENS,
2009
_("Coordinate expression had a close parenthesis with no open parenthesis"));
2013
case POS_TOKEN_VARIABLE:
2014
exprs[n_exprs].type = POS_EXPR_INT;
2016
/* FIXME we should just dump all this crap
2017
* in a hash, maybe keep width/height out
2018
* for optimization purposes
2020
if (strcmp (t->d.v.name, "width") == 0)
2021
exprs[n_exprs].d.int_val = env->width;
2022
else if (strcmp (t->d.v.name, "height") == 0)
2023
exprs[n_exprs].d.int_val = env->height;
2024
else if (env->object_width >= 0 &&
2025
strcmp (t->d.v.name, "object_width") == 0)
2026
exprs[n_exprs].d.int_val = env->object_width;
2027
else if (env->object_height >= 0 &&
2028
strcmp (t->d.v.name, "object_height") == 0)
2029
exprs[n_exprs].d.int_val = env->object_height;
2030
else if (strcmp (t->d.v.name, "left_width") == 0)
2031
exprs[n_exprs].d.int_val = env->left_width;
2032
else if (strcmp (t->d.v.name, "right_width") == 0)
2033
exprs[n_exprs].d.int_val = env->right_width;
2034
else if (strcmp (t->d.v.name, "top_height") == 0)
2035
exprs[n_exprs].d.int_val = env->top_height;
2036
else if (strcmp (t->d.v.name, "bottom_height") == 0)
2037
exprs[n_exprs].d.int_val = env->bottom_height;
2038
else if (strcmp (t->d.v.name, "mini_icon_width") == 0)
2039
exprs[n_exprs].d.int_val = env->mini_icon_width;
2040
else if (strcmp (t->d.v.name, "mini_icon_height") == 0)
2041
exprs[n_exprs].d.int_val = env->mini_icon_height;
2042
else if (strcmp (t->d.v.name, "icon_width") == 0)
2043
exprs[n_exprs].d.int_val = env->icon_width;
2044
else if (strcmp (t->d.v.name, "icon_height") == 0)
2045
exprs[n_exprs].d.int_val = env->icon_height;
2046
else if (strcmp (t->d.v.name, "title_width") == 0)
2047
exprs[n_exprs].d.int_val = env->title_width;
2048
else if (strcmp (t->d.v.name, "title_height") == 0)
2049
exprs[n_exprs].d.int_val = env->title_height;
2050
/* In practice we only hit this code on initial theme
2051
* parse; after that we always optimize constants away
2053
else if (env->theme &&
2054
meta_theme_lookup_int_constant (env->theme,
2058
exprs[n_exprs].d.int_val = ival;
2060
else if (env->theme &&
2061
meta_theme_lookup_float_constant (env->theme,
2065
exprs[n_exprs].type = POS_EXPR_DOUBLE;
2066
exprs[n_exprs].d.double_val = dval;
2070
g_set_error (err, META_THEME_ERROR,
2071
META_THEME_ERROR_UNKNOWN_VARIABLE,
2072
_("Coordinate expression had unknown variable or constant \"%s\""),
2079
case POS_TOKEN_OPERATOR:
2080
exprs[n_exprs].type = POS_EXPR_OPERATOR;
2081
exprs[n_exprs].d.operator = t->d.o.op;
2088
g_assert (paren_level > 0);
2093
case POS_TOKEN_DOUBLE:
2094
case POS_TOKEN_VARIABLE:
2095
case POS_TOKEN_OPERATOR:
2098
case POS_TOKEN_OPEN_PAREN:
2102
case POS_TOKEN_CLOSE_PAREN:
2103
if (paren_level == 1)
2105
/* We closed a toplevel paren group, so recurse */
2106
if (!pos_eval_helper (&tokens[first_paren+1],
2107
i - first_paren - 1,
2125
if (paren_level > 0)
2127
g_set_error (err, META_THEME_ERROR,
2128
META_THEME_ERROR_BAD_PARENS,
2129
_("Coordinate expression had an open parenthesis with no close parenthesis"));
2133
/* Now we have no parens and no vars; so we just do all the multiplies
2134
* and divides, then all the add and subtract.
2138
g_set_error (err, META_THEME_ERROR,
2139
META_THEME_ERROR_FAILED,
2140
_("Coordinate expression doesn't seem to have any operators or operands"));
2144
/* precedence 1 ops */
2146
while (precedence >= 0)
2148
if (!do_operations (exprs, &n_exprs, precedence, err))
2153
g_assert (n_exprs == 1);
2161
* expr = int | double | expr * expr | expr / expr |
2162
* expr + expr | expr - expr | (expr)
2164
* so very not worth fooling with bison, yet so very painful by hand.
2167
pos_eval (PosToken *tokens,
2169
const MetaPositionExprEnv *env,
2177
if (pos_eval_helper (tokens, n_tokens, env, &expr, err))
2182
*val_p = expr.d.int_val;
2184
case POS_EXPR_DOUBLE:
2185
*val_p = expr.d.double_val;
2187
case POS_EXPR_OPERATOR:
2188
g_assert_not_reached ();
2199
/* We always return both X and Y, but only one will be meaningful in
2204
meta_parse_position_expression (const char *expr,
2205
const MetaPositionExprEnv *env,
2210
/* All positions are in a coordinate system with x, y at the origin.
2211
* The expression can have -, +, *, / as operators, floating point
2212
* or integer constants, and the variables "width" and "height" and
2213
* optionally "object_width" and object_height". Negative numbers
2220
if (!pos_tokenize (expr, &tokens, &n_tokens, err))
2222
g_assert (err == NULL || *err != NULL);
2227
g_print ("Tokenized \"%s\" to --->\n", expr);
2228
debug_print_tokens (tokens, n_tokens);
2231
if (pos_eval (tokens, n_tokens, env, &val, err))
2234
*x_return = env->x + val;
2236
*y_return = env->y + val;
2237
free_tokens (tokens, n_tokens);
2242
g_assert (err == NULL || *err != NULL);
2243
free_tokens (tokens, n_tokens);
2250
meta_parse_size_expression (const char *expr,
2251
const MetaPositionExprEnv *env,
2259
if (!pos_tokenize (expr, &tokens, &n_tokens, err))
2261
g_assert (err == NULL || *err != NULL);
2266
g_print ("Tokenized \"%s\" to --->\n", expr);
2267
debug_print_tokens (tokens, n_tokens);
2270
if (pos_eval (tokens, n_tokens, env, &val, err))
2273
*val_return = MAX (val, 1); /* require that sizes be at least 1x1 */
2274
free_tokens (tokens, n_tokens);
2279
g_assert (err == NULL || *err != NULL);
2280
free_tokens (tokens, n_tokens);
2285
/* To do this we tokenize, replace variable tokens
2286
* that are constants, then reassemble. The purpose
2287
* here is to optimize expressions so we don't do hash
2288
* lookups to eval them. Obviously it's a tradeoff that
2289
* slows down theme load times.
2292
meta_theme_replace_constants (MetaTheme *theme,
2300
char buf[G_ASCII_DTOSTR_BUF_SIZE];
2304
if (!pos_tokenize (expr, &tokens, &n_tokens, err))
2306
g_assert (err == NULL || *err != NULL);
2311
g_print ("Tokenized \"%s\" to --->\n", expr);
2312
debug_print_tokens (tokens, n_tokens);
2315
str = g_string_new (NULL);
2318
while (i < n_tokens)
2320
PosToken *t = &tokens[i];
2322
/* spaces so we don't accidentally merge variables
2323
* or anything like that
2326
g_string_append_c (str, ' ');
2331
g_string_append_printf (str, "%d", t->d.i.val);
2333
case POS_TOKEN_DOUBLE:
2334
g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE,
2336
g_string_append (str, buf);
2338
case POS_TOKEN_OPEN_PAREN:
2339
g_string_append_c (str, '(');
2341
case POS_TOKEN_CLOSE_PAREN:
2342
g_string_append_c (str, ')');
2344
case POS_TOKEN_VARIABLE:
2345
if (meta_theme_lookup_int_constant (theme, t->d.v.name, &ival))
2346
g_string_append_printf (str, "%d", ival);
2347
else if (meta_theme_lookup_float_constant (theme, t->d.v.name, &dval))
2349
g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE,
2351
g_string_append (str, buf);
2355
g_string_append (str, t->d.v.name);
2358
case POS_TOKEN_OPERATOR:
2359
g_string_append (str, op_name (t->d.o.op));
2366
free_tokens (tokens, n_tokens);
2368
return g_string_free (str, FALSE);
2372
parse_x_position_unchecked (const char *expr,
2373
const MetaPositionExprEnv *env)
2380
if (!meta_parse_position_expression (expr, env,
2384
meta_warning (_("Theme contained an expression \"%s\" that resulted in an error: %s\n"),
2385
expr, error->message);
2387
g_error_free (error);
2394
parse_y_position_unchecked (const char *expr,
2395
const MetaPositionExprEnv *env)
2402
if (!meta_parse_position_expression (expr, env,
2406
meta_warning (_("Theme contained an expression \"%s\" that resulted in an error: %s\n"),
2407
expr, error->message);
2409
g_error_free (error);
2416
parse_size_unchecked (const char *expr,
2417
MetaPositionExprEnv *env)
2424
if (!meta_parse_size_expression (expr, env,
2427
meta_warning (_("Theme contained an expression \"%s\" that resulted in an error: %s\n"),
2428
expr, error->message);
2430
g_error_free (error);
2438
meta_draw_op_new (MetaDrawType type)
2444
size = G_STRUCT_OFFSET (MetaDrawOp, data);
2448
case META_DRAW_LINE:
2449
size += sizeof (dummy.data.line);
2452
case META_DRAW_RECTANGLE:
2453
size += sizeof (dummy.data.rectangle);
2457
size += sizeof (dummy.data.arc);
2460
case META_DRAW_CLIP:
2461
size += sizeof (dummy.data.clip);
2464
case META_DRAW_TINT:
2465
size += sizeof (dummy.data.tint);
2468
case META_DRAW_GRADIENT:
2469
size += sizeof (dummy.data.gradient);
2472
case META_DRAW_IMAGE:
2473
size += sizeof (dummy.data.image);
2476
case META_DRAW_GTK_ARROW:
2477
size += sizeof (dummy.data.gtk_arrow);
2480
case META_DRAW_GTK_BOX:
2481
size += sizeof (dummy.data.gtk_box);
2484
case META_DRAW_GTK_VLINE:
2485
size += sizeof (dummy.data.gtk_vline);
2488
case META_DRAW_ICON:
2489
size += sizeof (dummy.data.icon);
2492
case META_DRAW_TITLE:
2493
size += sizeof (dummy.data.title);
2495
case META_DRAW_OP_LIST:
2496
size += sizeof (dummy.data.op_list);
2498
case META_DRAW_TILE:
2499
size += sizeof (dummy.data.tile);
2503
op = g_malloc0 (size);
2511
meta_draw_op_free (MetaDrawOp *op)
2513
g_return_if_fail (op != NULL);
2517
case META_DRAW_LINE:
2518
if (op->data.line.color_spec)
2519
meta_color_spec_free (op->data.line.color_spec);
2520
g_free (op->data.line.x1);
2521
g_free (op->data.line.y1);
2522
g_free (op->data.line.x2);
2523
g_free (op->data.line.y2);
2526
case META_DRAW_RECTANGLE:
2527
if (op->data.rectangle.color_spec)
2528
g_free (op->data.rectangle.color_spec);
2529
g_free (op->data.rectangle.x);
2530
g_free (op->data.rectangle.y);
2531
g_free (op->data.rectangle.width);
2532
g_free (op->data.rectangle.height);
2536
if (op->data.arc.color_spec)
2537
g_free (op->data.arc.color_spec);
2538
g_free (op->data.arc.x);
2539
g_free (op->data.arc.y);
2540
g_free (op->data.arc.width);
2541
g_free (op->data.arc.height);
2544
case META_DRAW_CLIP:
2545
g_free (op->data.clip.x);
2546
g_free (op->data.clip.y);
2547
g_free (op->data.clip.width);
2548
g_free (op->data.clip.height);
2551
case META_DRAW_TINT:
2552
if (op->data.tint.color_spec)
2553
meta_color_spec_free (op->data.tint.color_spec);
2554
if (op->data.tint.alpha_spec)
2555
meta_alpha_gradient_spec_free (op->data.tint.alpha_spec);
2556
g_free (op->data.tint.x);
2557
g_free (op->data.tint.y);
2558
g_free (op->data.tint.width);
2559
g_free (op->data.tint.height);
2562
case META_DRAW_GRADIENT:
2563
if (op->data.gradient.gradient_spec)
2564
meta_gradient_spec_free (op->data.gradient.gradient_spec);
2565
if (op->data.gradient.alpha_spec)
2566
meta_alpha_gradient_spec_free (op->data.gradient.alpha_spec);
2567
g_free (op->data.gradient.x);
2568
g_free (op->data.gradient.y);
2569
g_free (op->data.gradient.width);
2570
g_free (op->data.gradient.height);
2573
case META_DRAW_IMAGE:
2574
if (op->data.image.alpha_spec)
2575
meta_alpha_gradient_spec_free (op->data.image.alpha_spec);
2576
if (op->data.image.pixbuf)
2577
g_object_unref (G_OBJECT (op->data.image.pixbuf));
2578
if (op->data.image.colorize_spec)
2579
meta_color_spec_free (op->data.image.colorize_spec);
2580
if (op->data.image.colorize_cache_pixbuf)
2581
g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
2582
g_free (op->data.image.x);
2583
g_free (op->data.image.y);
2584
g_free (op->data.image.width);
2585
g_free (op->data.image.height);
2588
case META_DRAW_GTK_ARROW:
2589
g_free (op->data.gtk_arrow.x);
2590
g_free (op->data.gtk_arrow.y);
2591
g_free (op->data.gtk_arrow.width);
2592
g_free (op->data.gtk_arrow.height);
2595
case META_DRAW_GTK_BOX:
2596
g_free (op->data.gtk_box.x);
2597
g_free (op->data.gtk_box.y);
2598
g_free (op->data.gtk_box.width);
2599
g_free (op->data.gtk_box.height);
2602
case META_DRAW_GTK_VLINE:
2603
g_free (op->data.gtk_vline.x);
2604
g_free (op->data.gtk_vline.y1);
2605
g_free (op->data.gtk_vline.y2);
2608
case META_DRAW_ICON:
2609
if (op->data.icon.alpha_spec)
2610
meta_alpha_gradient_spec_free (op->data.icon.alpha_spec);
2611
g_free (op->data.icon.x);
2612
g_free (op->data.icon.y);
2613
g_free (op->data.icon.width);
2614
g_free (op->data.icon.height);
2617
case META_DRAW_TITLE:
2618
if (op->data.title.color_spec)
2619
meta_color_spec_free (op->data.title.color_spec);
2620
g_free (op->data.title.x);
2621
g_free (op->data.title.y);
2624
case META_DRAW_OP_LIST:
2625
if (op->data.op_list.op_list)
2626
meta_draw_op_list_unref (op->data.op_list.op_list);
2627
g_free (op->data.op_list.x);
2628
g_free (op->data.op_list.y);
2629
g_free (op->data.op_list.width);
2630
g_free (op->data.op_list.height);
2633
case META_DRAW_TILE:
2634
if (op->data.tile.op_list)
2635
meta_draw_op_list_unref (op->data.tile.op_list);
2636
g_free (op->data.tile.x);
2637
g_free (op->data.tile.y);
2638
g_free (op->data.tile.width);
2639
g_free (op->data.tile.height);
2640
g_free (op->data.tile.tile_xoffset);
2641
g_free (op->data.tile.tile_yoffset);
2642
g_free (op->data.tile.tile_width);
2643
g_free (op->data.tile.tile_height);
2651
get_gc_for_primitive (GtkWidget *widget,
2652
GdkDrawable *drawable,
2653
MetaColorSpec *color_spec,
2654
const GdkRectangle *clip,
2661
meta_color_spec_render (color_spec, widget, &color);
2663
values.foreground = color;
2665
gdk_rgb_find_color (gdk_drawable_get_colormap (drawable),
2666
&values.foreground);
2668
values.line_width = line_width;
2670
gc = gdk_gc_new_with_values (drawable, &values,
2671
GDK_GC_FOREGROUND | GDK_GC_LINE_WIDTH);
2674
gdk_gc_set_clip_rectangle (gc,
2675
(GdkRectangle*) clip); /* const cast */
2681
apply_alpha (GdkPixbuf *pixbuf,
2682
MetaAlphaGradientSpec *spec,
2683
gboolean force_copy)
2685
GdkPixbuf *new_pixbuf;
2686
gboolean needs_alpha;
2688
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
2690
needs_alpha = spec && (spec->n_alphas > 1 ||
2691
spec->alphas[0] != 0xff);
2696
if (!gdk_pixbuf_get_has_alpha (pixbuf))
2698
new_pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
2699
g_object_unref (G_OBJECT (pixbuf));
2700
pixbuf = new_pixbuf;
2702
else if (force_copy)
2704
new_pixbuf = gdk_pixbuf_copy (pixbuf);
2705
g_object_unref (G_OBJECT (pixbuf));
2706
pixbuf = new_pixbuf;
2709
g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
2711
meta_gradient_add_alpha (pixbuf, spec->alphas, spec->n_alphas, spec->type);
2717
render_pixbuf (GdkDrawable *drawable,
2718
const GdkRectangle *clip,
2723
/* grumble, render_to_drawable_alpha does not accept a clip
2724
* mask, so we have to go through some BS
2726
/* FIXME once GTK 1.3.13 has been out a while we can use
2727
* render_to_drawable() which now does alpha with clip.
2729
* Though the gdk_rectangle_intersect() check may be a useful
2730
* optimization anyway.
2732
GdkRectangle pixbuf_rect;
2733
GdkRectangle draw_rect;
2737
pixbuf_rect.width = gdk_pixbuf_get_width (pixbuf);
2738
pixbuf_rect.height = gdk_pixbuf_get_height (pixbuf);
2742
if (!gdk_rectangle_intersect ((GdkRectangle*)clip,
2743
&pixbuf_rect, &draw_rect))
2748
draw_rect = pixbuf_rect;
2751
gdk_pixbuf_render_to_drawable_alpha (pixbuf,
2753
draw_rect.x - pixbuf_rect.x,
2754
draw_rect.y - pixbuf_rect.y,
2755
draw_rect.x, draw_rect.y,
2758
GDK_PIXBUF_ALPHA_FULL, /* ignored */
2760
GDK_RGB_DITHER_NORMAL,
2761
draw_rect.x - pixbuf_rect.x,
2762
draw_rect.y - pixbuf_rect.y);
2766
pixbuf_tile (GdkPixbuf *tile,
2775
tile_width = gdk_pixbuf_get_width (tile);
2776
tile_height = gdk_pixbuf_get_height (tile);
2778
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
2779
gdk_pixbuf_get_has_alpha (tile),
2790
w = MIN (tile_width, width - i);
2791
h = MIN (tile_height, height - j);
2793
gdk_pixbuf_copy_area (tile,
2809
replicate_rows (GdkPixbuf *src,
2815
unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
2816
unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
2817
unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
2819
unsigned char *dest_pixels;
2821
unsigned int dest_rowstride;
2824
result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
2826
dest_rowstride = gdk_pixbuf_get_rowstride (result);
2827
dest_pixels = gdk_pixbuf_get_pixels (result);
2829
for (i = 0; i < height; i++)
2830
memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
2836
replicate_cols (GdkPixbuf *src,
2842
unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
2843
unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
2844
unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
2846
unsigned char *dest_pixels;
2848
unsigned int dest_rowstride;
2851
result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
2853
dest_rowstride = gdk_pixbuf_get_rowstride (result);
2854
dest_pixels = gdk_pixbuf_get_pixels (result);
2856
for (i = 0; i < height; i++)
2858
unsigned char *p = dest_pixels + dest_rowstride * i;
2859
unsigned char *q = pixels + src_rowstride * i;
2861
unsigned char r = *(q++);
2862
unsigned char g = *(q++);
2863
unsigned char b = *(q++);
2865
if (n_channels == 4)
2871
for (j = 0; j < width; j++)
2881
for (j = 0; j < width; j++)
2894
scale_and_alpha_pixbuf (GdkPixbuf *src,
2895
MetaAlphaGradientSpec *alpha_spec,
2896
MetaImageFillType fill_type,
2899
gboolean vertical_stripes,
2900
gboolean horizontal_stripes)
2903
GdkPixbuf *temp_pixbuf;
2909
if (gdk_pixbuf_get_width (pixbuf) == width &&
2910
gdk_pixbuf_get_height (pixbuf) == height)
2912
g_object_ref (G_OBJECT (pixbuf));
2916
if (fill_type == META_IMAGE_FILL_TILE)
2918
pixbuf = pixbuf_tile (pixbuf, width, height);
2922
int src_h, src_w, dest_h, dest_w;
2923
src_h = gdk_pixbuf_get_height (src);
2924
src_w = gdk_pixbuf_get_width (src);
2926
/* prefer to replicate_cols if possible, as that
2927
* is faster (no memory reads)
2929
if (horizontal_stripes)
2931
dest_w = gdk_pixbuf_get_width (src);
2934
else if (vertical_stripes)
2937
dest_h = gdk_pixbuf_get_height (src);
2946
if (dest_w == src_w && dest_h == src_h)
2949
g_object_ref (G_OBJECT (temp_pixbuf));
2953
temp_pixbuf = gdk_pixbuf_scale_simple (src,
2955
GDK_INTERP_BILINEAR);
2958
/* prefer to replicate_cols if possible, as that
2959
* is faster (no memory reads)
2961
if (horizontal_stripes)
2963
pixbuf = replicate_cols (temp_pixbuf, 0, 0, width, height);
2964
g_object_unref (G_OBJECT (temp_pixbuf));
2966
else if (vertical_stripes)
2968
pixbuf = replicate_rows (temp_pixbuf, 0, 0, width, height);
2969
g_object_unref (G_OBJECT (temp_pixbuf));
2973
pixbuf = temp_pixbuf;
2979
pixbuf = apply_alpha (pixbuf, alpha_spec, pixbuf == src);
2985
draw_op_as_pixbuf (const MetaDrawOp *op,
2987
const MetaDrawInfo *info,
2991
/* Try to get the op as a pixbuf, assuming w/h in the op
2992
* matches the width/height passed in. return NULL
2993
* if the op can't be converted to an equivalent pixbuf.
3001
case META_DRAW_LINE:
3004
case META_DRAW_RECTANGLE:
3005
if (op->data.rectangle.filled)
3009
meta_color_spec_render (op->data.rectangle.color_spec,
3013
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
3017
gdk_pixbuf_fill (pixbuf, GDK_COLOR_RGBA (color));
3024
case META_DRAW_CLIP:
3027
case META_DRAW_TINT:
3033
meta_color_spec_render (op->data.rectangle.color_spec,
3038
op->data.tint.alpha_spec &&
3039
(op->data.tint.alpha_spec->n_alphas > 1 ||
3040
op->data.tint.alpha_spec->alphas[0] != 0xff);
3042
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
3048
rgba = GDK_COLOR_RGBA (color);
3050
gdk_pixbuf_fill (pixbuf, rgba);
3052
else if (op->data.tint.alpha_spec->n_alphas == 1)
3054
rgba = GDK_COLOR_RGBA (color);
3056
rgba |= op->data.tint.alpha_spec->alphas[0];
3058
gdk_pixbuf_fill (pixbuf, rgba);
3062
rgba = GDK_COLOR_RGBA (color);
3064
gdk_pixbuf_fill (pixbuf, rgba);
3066
meta_gradient_add_alpha (pixbuf,
3067
op->data.tint.alpha_spec->alphas,
3068
op->data.tint.alpha_spec->n_alphas,
3069
op->data.tint.alpha_spec->type);
3074
case META_DRAW_GRADIENT:
3076
pixbuf = meta_gradient_spec_render (op->data.gradient.gradient_spec,
3077
widget, width, height);
3079
pixbuf = apply_alpha (pixbuf,
3080
op->data.gradient.alpha_spec,
3086
case META_DRAW_IMAGE:
3088
if (op->data.image.colorize_spec)
3092
meta_color_spec_render (op->data.image.colorize_spec,
3095
if (op->data.image.colorize_cache_pixbuf == NULL ||
3096
op->data.image.colorize_cache_pixel != GDK_COLOR_RGB (color))
3098
if (op->data.image.colorize_cache_pixbuf)
3099
g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
3101
/* const cast here */
3102
((MetaDrawOp*)op)->data.image.colorize_cache_pixbuf =
3103
colorize_pixbuf (op->data.image.pixbuf,
3105
((MetaDrawOp*)op)->data.image.colorize_cache_pixel =
3106
GDK_COLOR_RGB (color);
3109
if (op->data.image.colorize_cache_pixbuf)
3111
pixbuf = scale_and_alpha_pixbuf (op->data.image.colorize_cache_pixbuf,
3112
op->data.image.alpha_spec,
3113
op->data.image.fill_type,
3115
op->data.image.vertical_stripes,
3116
op->data.image.horizontal_stripes);
3121
pixbuf = scale_and_alpha_pixbuf (op->data.image.pixbuf,
3122
op->data.image.alpha_spec,
3123
op->data.image.fill_type,
3125
op->data.image.vertical_stripes,
3126
op->data.image.horizontal_stripes);
3131
case META_DRAW_GTK_ARROW:
3132
case META_DRAW_GTK_BOX:
3133
case META_DRAW_GTK_VLINE:
3136
case META_DRAW_ICON:
3137
if (info->mini_icon &&
3138
width <= gdk_pixbuf_get_width (info->mini_icon) &&
3139
height <= gdk_pixbuf_get_height (info->mini_icon))
3140
pixbuf = scale_and_alpha_pixbuf (info->mini_icon,
3141
op->data.icon.alpha_spec,
3142
op->data.icon.fill_type,
3145
else if (info->icon)
3146
pixbuf = scale_and_alpha_pixbuf (info->icon,
3147
op->data.icon.alpha_spec,
3148
op->data.icon.fill_type,
3153
case META_DRAW_TITLE:
3156
case META_DRAW_OP_LIST:
3159
case META_DRAW_TILE:
3167
fill_env (MetaPositionExprEnv *env,
3168
const MetaDrawInfo *info,
3174
/* FIXME this stuff could be raised into draw_op_list_draw() probably
3179
env->height = height;
3180
env->object_width = -1;
3181
env->object_height = -1;
3184
env->left_width = info->fgeom->left_width;
3185
env->right_width = info->fgeom->right_width;
3186
env->top_height = info->fgeom->top_height;
3187
env->bottom_height = info->fgeom->bottom_height;
3191
env->left_width = 0;
3192
env->right_width = 0;
3193
env->top_height = 0;
3194
env->bottom_height = 0;
3197
env->mini_icon_width = info->mini_icon ? gdk_pixbuf_get_width (info->mini_icon) : 0;
3198
env->mini_icon_height = info->mini_icon ? gdk_pixbuf_get_height (info->mini_icon) : 0;
3199
env->icon_width = info->icon ? gdk_pixbuf_get_width (info->icon) : 0;
3200
env->icon_height = info->icon ? gdk_pixbuf_get_height (info->icon) : 0;
3202
env->title_width = info->title_layout_width;
3203
env->title_height = info->title_layout_height;
3204
env->theme = NULL; /* not required, constants have been optimized out */
3208
meta_draw_op_draw_with_env (const MetaDrawOp *op,
3210
GdkDrawable *drawable,
3211
const GdkRectangle *clip,
3212
const MetaDrawInfo *info,
3217
MetaPositionExprEnv *env)
3223
case META_DRAW_LINE:
3227
gc = get_gc_for_primitive (widget, drawable,
3228
op->data.line.color_spec,
3230
op->data.line.width);
3232
if (op->data.line.dash_on_length > 0 &&
3233
op->data.line.dash_off_length > 0)
3236
dash_list[0] = op->data.line.dash_on_length;
3237
dash_list[1] = op->data.line.dash_off_length;
3238
gdk_gc_set_dashes (gc, 0, dash_list, 2);
3241
x1 = parse_x_position_unchecked (op->data.line.x1, env);
3242
y1 = parse_y_position_unchecked (op->data.line.y1, env);
3243
x2 = parse_x_position_unchecked (op->data.line.x2, env);
3244
y2 = parse_y_position_unchecked (op->data.line.y2, env);
3246
gdk_draw_line (drawable, gc, x1, y1, x2, y2);
3248
g_object_unref (G_OBJECT (gc));
3252
case META_DRAW_RECTANGLE:
3254
int rx, ry, rwidth, rheight;
3256
gc = get_gc_for_primitive (widget, drawable,
3257
op->data.rectangle.color_spec,
3260
rx = parse_x_position_unchecked (op->data.rectangle.x, env);
3261
ry = parse_y_position_unchecked (op->data.rectangle.y, env);
3262
rwidth = parse_size_unchecked (op->data.rectangle.width, env);
3263
rheight = parse_size_unchecked (op->data.rectangle.height, env);
3265
gdk_draw_rectangle (drawable, gc,
3266
op->data.rectangle.filled,
3267
rx, ry, rwidth, rheight);
3269
g_object_unref (G_OBJECT (gc));
3275
int rx, ry, rwidth, rheight;
3277
gc = get_gc_for_primitive (widget, drawable,
3278
op->data.arc.color_spec,
3281
rx = parse_x_position_unchecked (op->data.arc.x, env);
3282
ry = parse_y_position_unchecked (op->data.arc.y, env);
3283
rwidth = parse_size_unchecked (op->data.arc.width, env);
3284
rheight = parse_size_unchecked (op->data.arc.height, env);
3286
gdk_draw_arc (drawable,
3288
op->data.arc.filled,
3289
rx, ry, rwidth, rheight,
3290
op->data.arc.start_angle * (360.0 * 64.0) -
3291
(90.0 * 64.0), /* start at 12 instead of 3 oclock */
3292
op->data.arc.extent_angle * (360.0 * 64.0));
3294
g_object_unref (G_OBJECT (gc));
3298
case META_DRAW_CLIP:
3301
case META_DRAW_TINT:
3303
int rx, ry, rwidth, rheight;
3304
gboolean needs_alpha;
3306
needs_alpha = op->data.tint.alpha_spec &&
3307
(op->data.tint.alpha_spec->n_alphas > 1 ||
3308
op->data.tint.alpha_spec->alphas[0] != 0xff);
3310
rx = parse_x_position_unchecked (op->data.tint.x, env);
3311
ry = parse_y_position_unchecked (op->data.tint.y, env);
3312
rwidth = parse_size_unchecked (op->data.tint.width, env);
3313
rheight = parse_size_unchecked (op->data.tint.height, env);
3317
gc = get_gc_for_primitive (widget, drawable,
3318
op->data.tint.color_spec,
3321
gdk_draw_rectangle (drawable, gc,
3323
rx, ry, rwidth, rheight);
3325
g_object_unref (G_OBJECT (gc));
3331
pixbuf = draw_op_as_pixbuf (op, widget, info,
3336
render_pixbuf (drawable, clip, pixbuf, rx, ry);
3338
g_object_unref (G_OBJECT (pixbuf));
3344
case META_DRAW_GRADIENT:
3346
int rx, ry, rwidth, rheight;
3349
rx = parse_x_position_unchecked (op->data.gradient.x, env);
3350
ry = parse_y_position_unchecked (op->data.gradient.y, env);
3351
rwidth = parse_size_unchecked (op->data.gradient.width, env);
3352
rheight = parse_size_unchecked (op->data.gradient.height, env);
3354
pixbuf = draw_op_as_pixbuf (op, widget, info,
3359
render_pixbuf (drawable, clip, pixbuf, rx, ry);
3361
g_object_unref (G_OBJECT (pixbuf));
3366
case META_DRAW_IMAGE:
3368
int rx, ry, rwidth, rheight;
3371
if (op->data.image.pixbuf)
3373
env->object_width = gdk_pixbuf_get_width (op->data.image.pixbuf);
3374
env->object_height = gdk_pixbuf_get_height (op->data.image.pixbuf);
3377
rwidth = parse_size_unchecked (op->data.image.width, env);
3378
rheight = parse_size_unchecked (op->data.image.height, env);
3380
pixbuf = draw_op_as_pixbuf (op, widget, info,
3385
rx = parse_x_position_unchecked (op->data.image.x, env);
3386
ry = parse_y_position_unchecked (op->data.image.y, env);
3388
render_pixbuf (drawable, clip, pixbuf, rx, ry);
3390
g_object_unref (G_OBJECT (pixbuf));
3395
case META_DRAW_GTK_ARROW:
3397
int rx, ry, rwidth, rheight;
3399
rx = parse_x_position_unchecked (op->data.gtk_arrow.x, env);
3400
ry = parse_y_position_unchecked (op->data.gtk_arrow.y, env);
3401
rwidth = parse_size_unchecked (op->data.gtk_arrow.width, env);
3402
rheight = parse_size_unchecked (op->data.gtk_arrow.height, env);
3404
gtk_paint_arrow (widget->style,
3406
op->data.gtk_arrow.state,
3407
op->data.gtk_arrow.shadow,
3408
(GdkRectangle*) clip,
3411
op->data.gtk_arrow.arrow,
3412
op->data.gtk_arrow.filled,
3413
rx, ry, rwidth, rheight);
3417
case META_DRAW_GTK_BOX:
3419
int rx, ry, rwidth, rheight;
3421
rx = parse_x_position_unchecked (op->data.gtk_box.x, env);
3422
ry = parse_y_position_unchecked (op->data.gtk_box.y, env);
3423
rwidth = parse_size_unchecked (op->data.gtk_box.width, env);
3424
rheight = parse_size_unchecked (op->data.gtk_box.height, env);
3426
gtk_paint_box (widget->style,
3428
op->data.gtk_box.state,
3429
op->data.gtk_box.shadow,
3430
(GdkRectangle*) clip,
3433
rx, ry, rwidth, rheight);
3437
case META_DRAW_GTK_VLINE:
3441
rx = parse_x_position_unchecked (op->data.gtk_vline.x, env);
3442
ry1 = parse_y_position_unchecked (op->data.gtk_vline.y1, env);
3443
ry2 = parse_y_position_unchecked (op->data.gtk_vline.y2, env);
3445
gtk_paint_vline (widget->style,
3447
op->data.gtk_vline.state,
3448
(GdkRectangle*) clip,
3455
case META_DRAW_ICON:
3457
int rx, ry, rwidth, rheight;
3460
rwidth = parse_size_unchecked (op->data.icon.width, env);
3461
rheight = parse_size_unchecked (op->data.icon.height, env);
3463
pixbuf = draw_op_as_pixbuf (op, widget, info,
3468
rx = parse_x_position_unchecked (op->data.icon.x, env);
3469
ry = parse_y_position_unchecked (op->data.icon.y, env);
3471
render_pixbuf (drawable, clip, pixbuf, rx, ry);
3473
g_object_unref (G_OBJECT (pixbuf));
3478
case META_DRAW_TITLE:
3479
if (info->title_layout)
3483
gc = get_gc_for_primitive (widget, drawable,
3484
op->data.title.color_spec,
3487
rx = parse_x_position_unchecked (op->data.title.x, env);
3488
ry = parse_y_position_unchecked (op->data.title.y, env);
3490
gdk_draw_layout (drawable, gc,
3492
info->title_layout);
3494
g_object_unref (G_OBJECT (gc));
3498
case META_DRAW_OP_LIST:
3500
int rx, ry, rwidth, rheight;
3502
rx = parse_x_position_unchecked (op->data.op_list.x, env);
3503
ry = parse_y_position_unchecked (op->data.op_list.y, env);
3504
rwidth = parse_size_unchecked (op->data.op_list.width, env);
3505
rheight = parse_size_unchecked (op->data.op_list.height, env);
3507
meta_draw_op_list_draw (op->data.op_list.op_list,
3508
widget, drawable, clip, info,
3509
rx, ry, rwidth, rheight);
3513
case META_DRAW_TILE:
3515
int rx, ry, rwidth, rheight;
3516
int tile_xoffset, tile_yoffset, tile_width, tile_height;
3517
GdkRectangle new_clip;
3520
rx = parse_x_position_unchecked (op->data.tile.x, env);
3521
ry = parse_y_position_unchecked (op->data.tile.y, env);
3522
rwidth = parse_size_unchecked (op->data.tile.width, env);
3523
rheight = parse_size_unchecked (op->data.tile.height, env);
3527
new_clip.width = rwidth;
3528
new_clip.height = rheight;
3530
if (clip == NULL || gdk_rectangle_intersect ((GdkRectangle*)clip, &new_clip,
3533
tile_xoffset = parse_x_position_unchecked (op->data.tile.tile_xoffset, env);
3534
tile_yoffset = parse_y_position_unchecked (op->data.tile.tile_yoffset, env);
3535
/* tile offset should not include x/y */
3539
tile_width = parse_size_unchecked (op->data.tile.tile_width, env);
3540
tile_height = parse_size_unchecked (op->data.tile.tile_height, env);
3542
tile_x = rx - tile_xoffset;
3544
while (tile_x < (rx + rwidth))
3546
tile_y = ry - tile_yoffset;
3547
while (tile_y < (ry + rheight))
3549
meta_draw_op_list_draw (op->data.tile.op_list,
3550
widget, drawable, &new_clip, info,
3551
tile_x, tile_y, tile_width, tile_height);
3553
tile_y += tile_height;
3556
tile_x += tile_width;
3565
meta_draw_op_draw (const MetaDrawOp *op,
3567
GdkDrawable *drawable,
3568
const GdkRectangle *clip,
3569
const MetaDrawInfo *info,
3575
MetaPositionExprEnv env;
3577
fill_env (&env, info, x, y, width, height);
3579
meta_draw_op_draw_with_env (op, widget, drawable, clip,
3580
info, x, y, width, height,
3585
meta_draw_op_list_new (int n_preallocs)
3587
MetaDrawOpList *op_list;
3589
g_return_val_if_fail (n_preallocs >= 0, NULL);
3591
op_list = g_new (MetaDrawOpList, 1);
3593
op_list->refcount = 1;
3594
op_list->n_allocated = n_preallocs;
3595
op_list->ops = g_new (MetaDrawOp*, op_list->n_allocated);
3602
meta_draw_op_list_ref (MetaDrawOpList *op_list)
3604
g_return_if_fail (op_list != NULL);
3606
op_list->refcount += 1;
3610
meta_draw_op_list_unref (MetaDrawOpList *op_list)
3612
g_return_if_fail (op_list != NULL);
3613
g_return_if_fail (op_list->refcount > 0);
3615
op_list->refcount -= 1;
3617
if (op_list->refcount == 0)
3622
while (i < op_list->n_ops)
3624
meta_draw_op_free (op_list->ops[i]);
3628
g_free (op_list->ops);
3630
DEBUG_FILL_STRUCT (op_list);
3636
meta_draw_op_list_draw (const MetaDrawOpList *op_list,
3638
GdkDrawable *drawable,
3639
const GdkRectangle *clip,
3640
const MetaDrawInfo *info,
3647
GdkRectangle active_clip;
3648
GdkRectangle orig_clip;
3649
MetaPositionExprEnv env;
3651
if (op_list->n_ops == 0)
3654
fill_env (&env, info, x, y, width, height);
3656
/* FIXME this can be optimized, potentially a lot, by
3657
* compressing multiple ops when possible. For example,
3658
* anything convertible to a pixbuf can be composited
3659
* client-side, and putting a color tint over a pixbuf
3660
* can be done without creating the solid-color pixbuf.
3662
* To implement this my plan is to have the idea of a
3663
* compiled draw op (with the string expressions already
3664
* evaluated), we make an array of those, and then fold
3665
* adjacent items when possible.
3673
orig_clip.width = width;
3674
orig_clip.height = height;
3677
active_clip = orig_clip;
3680
while (i < op_list->n_ops)
3682
MetaDrawOp *op = op_list->ops[i];
3684
if (op->type == META_DRAW_CLIP)
3686
active_clip.x = parse_x_position_unchecked (op->data.clip.x, &env);
3687
active_clip.y = parse_y_position_unchecked (op->data.clip.y, &env);
3688
active_clip.width = parse_size_unchecked (op->data.clip.width, &env);
3689
active_clip.height = parse_size_unchecked (op->data.clip.height, &env);
3691
gdk_rectangle_intersect (&orig_clip, &active_clip, &active_clip);
3693
else if (active_clip.width > 0 &&
3694
active_clip.height > 0)
3696
meta_draw_op_draw_with_env (op,
3697
widget, drawable, &active_clip, info,
3698
x, y, width, height,
3707
meta_draw_op_list_append (MetaDrawOpList *op_list,
3710
if (op_list->n_ops == op_list->n_allocated)
3712
op_list->n_allocated *= 2;
3713
op_list->ops = g_renew (MetaDrawOp*, op_list->ops, op_list->n_allocated);
3716
op_list->ops[op_list->n_ops] = op;
3717
op_list->n_ops += 1;
3721
meta_draw_op_list_validate (MetaDrawOpList *op_list,
3724
g_return_val_if_fail (op_list != NULL, FALSE);
3726
/* empty lists are OK, nothing else to check really */
3731
/* This is not done in validate, since we wouldn't know the name
3732
* of the list to report the error. It might be nice to
3733
* store names inside the list sometime.
3736
meta_draw_op_list_contains (MetaDrawOpList *op_list,
3737
MetaDrawOpList *child)
3741
/* mmm, huge tree recursion */
3744
while (i < op_list->n_ops)
3746
if (op_list->ops[i]->type == META_DRAW_OP_LIST)
3748
if (op_list->ops[i]->data.op_list.op_list == child)
3751
if (meta_draw_op_list_contains (op_list->ops[i]->data.op_list.op_list,
3755
else if (op_list->ops[i]->type == META_DRAW_TILE)
3757
if (op_list->ops[i]->data.tile.op_list == child)
3760
if (meta_draw_op_list_contains (op_list->ops[i]->data.tile.op_list,
3772
meta_frame_style_new (MetaFrameStyle *parent)
3774
MetaFrameStyle *style;
3776
style = g_new0 (MetaFrameStyle, 1);
3778
style->refcount = 1;
3780
style->parent = parent;
3782
meta_frame_style_ref (parent);
3788
meta_frame_style_ref (MetaFrameStyle *style)
3790
g_return_if_fail (style != NULL);
3792
style->refcount += 1;
3796
free_button_ops (MetaDrawOpList *op_lists[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST])
3801
while (i < META_BUTTON_TYPE_LAST)
3804
while (j < META_BUTTON_STATE_LAST)
3807
meta_draw_op_list_unref (op_lists[i][j]);
3817
meta_frame_style_unref (MetaFrameStyle *style)
3819
g_return_if_fail (style != NULL);
3820
g_return_if_fail (style->refcount > 0);
3822
style->refcount -= 1;
3824
if (style->refcount == 0)
3828
free_button_ops (style->buttons);
3831
while (i < META_FRAME_PIECE_LAST)
3833
if (style->pieces[i])
3834
meta_draw_op_list_unref (style->pieces[i]);
3840
meta_frame_layout_unref (style->layout);
3842
/* we hold a reference to any parent style */
3844
meta_frame_style_unref (style->parent);
3846
DEBUG_FILL_STRUCT (style);
3851
static MetaDrawOpList*
3852
get_button (MetaFrameStyle *style,
3853
MetaButtonType type,
3854
MetaButtonState state)
3856
MetaDrawOpList *op_list;
3857
MetaFrameStyle *parent;
3861
while (parent && op_list == NULL)
3863
op_list = parent->buttons[type][state];
3864
parent = parent->parent;
3867
/* We fall back to normal if no prelight */
3868
if (op_list == NULL &&
3869
state == META_BUTTON_STATE_PRELIGHT)
3870
return get_button (style, type, META_BUTTON_STATE_NORMAL);
3872
/* We fall back to middle button backgrounds if we don't
3873
* have the ones on the sides
3876
if (op_list == NULL &&
3877
(type == META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND ||
3878
type == META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND))
3879
return get_button (style, META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND,
3882
if (op_list == NULL &&
3883
(type == META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND ||
3884
type == META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND))
3885
return get_button (style, META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND,
3892
meta_frame_style_validate (MetaFrameStyle *style,
3897
g_return_val_if_fail (style != NULL, FALSE);
3898
g_return_val_if_fail (style->layout != NULL, FALSE);
3901
while (i < META_BUTTON_TYPE_LAST)
3903
/* for now the "positional" buttons are optional */
3904
if (i >= META_BUTTON_TYPE_CLOSE)
3907
while (j < META_BUTTON_STATE_LAST)
3909
if (get_button (style, i, j) == NULL)
3911
g_set_error (error, META_THEME_ERROR,
3912
META_THEME_ERROR_FAILED,
3913
_("<button function=\"%s\" state=\"%s\" draw_ops=\"whatever\"/> must be specified for this frame style"),
3914
meta_button_type_to_string (i),
3915
meta_button_state_to_string (j));
3930
button_rect (MetaButtonType type,
3931
const MetaFrameGeometry *fgeom,
3932
int middle_background_offset,
3937
case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
3938
*rect = fgeom->left_left_background;
3941
case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
3942
*rect = fgeom->left_middle_backgrounds[middle_background_offset];
3945
case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
3946
*rect = fgeom->left_right_background;
3949
case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
3950
*rect = fgeom->right_left_background;
3953
case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
3954
*rect = fgeom->right_middle_backgrounds[middle_background_offset];
3957
case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
3958
*rect = fgeom->right_right_background;
3961
case META_BUTTON_TYPE_CLOSE:
3962
*rect = fgeom->close_rect;
3965
case META_BUTTON_TYPE_MAXIMIZE:
3966
*rect = fgeom->max_rect;
3969
case META_BUTTON_TYPE_MINIMIZE:
3970
*rect = fgeom->min_rect;
3973
case META_BUTTON_TYPE_MENU:
3974
*rect = fgeom->menu_rect;
3977
case META_BUTTON_TYPE_LAST:
3978
g_assert_not_reached ();
3984
meta_frame_style_draw (MetaFrameStyle *style,
3986
GdkDrawable *drawable,
3989
const GdkRectangle *clip,
3990
const MetaFrameGeometry *fgeom,
3993
PangoLayout *title_layout,
3995
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
3996
GdkPixbuf *mini_icon,
4000
GdkRectangle titlebar_rect;
4001
GdkRectangle left_titlebar_edge;
4002
GdkRectangle right_titlebar_edge;
4003
GdkRectangle bottom_titlebar_edge;
4004
GdkRectangle top_titlebar_edge;
4005
GdkRectangle left_edge, right_edge, bottom_edge;
4006
PangoRectangle extents;
4007
MetaDrawInfo draw_info;
4009
titlebar_rect.x = 0;
4010
titlebar_rect.y = 0;
4011
titlebar_rect.width = fgeom->width;
4012
titlebar_rect.height = fgeom->top_height;
4014
left_titlebar_edge.x = titlebar_rect.x;
4015
left_titlebar_edge.y = titlebar_rect.y + fgeom->top_titlebar_edge;
4016
left_titlebar_edge.width = fgeom->left_titlebar_edge;
4017
left_titlebar_edge.height = titlebar_rect.height - fgeom->top_titlebar_edge - fgeom->bottom_titlebar_edge;
4019
right_titlebar_edge.y = left_titlebar_edge.y;
4020
right_titlebar_edge.height = left_titlebar_edge.height;
4021
right_titlebar_edge.width = fgeom->right_titlebar_edge;
4022
right_titlebar_edge.x = titlebar_rect.x + titlebar_rect.width - right_titlebar_edge.width;
4024
top_titlebar_edge.x = titlebar_rect.x;
4025
top_titlebar_edge.y = titlebar_rect.y;
4026
top_titlebar_edge.width = titlebar_rect.width;
4027
top_titlebar_edge.height = fgeom->top_titlebar_edge;
4029
bottom_titlebar_edge.x = titlebar_rect.x;
4030
bottom_titlebar_edge.width = titlebar_rect.width;
4031
bottom_titlebar_edge.height = fgeom->bottom_titlebar_edge;
4032
bottom_titlebar_edge.y = titlebar_rect.y + titlebar_rect.height - bottom_titlebar_edge.height;
4035
left_edge.y = fgeom->top_height;
4036
left_edge.width = fgeom->left_width;
4037
left_edge.height = fgeom->height - fgeom->top_height - fgeom->bottom_height;
4039
right_edge.x = fgeom->width - fgeom->right_width;
4040
right_edge.y = fgeom->top_height;
4041
right_edge.width = fgeom->right_width;
4042
right_edge.height = fgeom->height - fgeom->top_height - fgeom->bottom_height;
4045
bottom_edge.y = fgeom->height - fgeom->bottom_height;
4046
bottom_edge.width = fgeom->width;
4047
bottom_edge.height = fgeom->bottom_height;
4050
pango_layout_get_pixel_extents (title_layout,
4053
draw_info.mini_icon = mini_icon;
4054
draw_info.icon = icon;
4055
draw_info.title_layout = title_layout;
4056
draw_info.title_layout_width = title_layout ? extents.width : 0;
4057
draw_info.title_layout_height = title_layout ? extents.height : 0;
4058
draw_info.fgeom = fgeom;
4060
/* The enum is in the order the pieces should be rendered. */
4062
while (i < META_FRAME_PIECE_LAST)
4065
GdkRectangle combined_clip;
4067
switch ((MetaFramePiece) i)
4069
case META_FRAME_PIECE_ENTIRE_BACKGROUND:
4072
rect.width = fgeom->width;
4073
rect.height = fgeom->height;
4076
case META_FRAME_PIECE_TITLEBAR:
4077
rect = titlebar_rect;
4080
case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE:
4081
rect = left_titlebar_edge;
4084
case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE:
4085
rect = right_titlebar_edge;
4088
case META_FRAME_PIECE_TOP_TITLEBAR_EDGE:
4089
rect = top_titlebar_edge;
4092
case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE:
4093
rect = bottom_titlebar_edge;
4096
case META_FRAME_PIECE_TITLEBAR_MIDDLE:
4097
rect.x = left_titlebar_edge.x + left_titlebar_edge.width;
4098
rect.y = top_titlebar_edge.y + top_titlebar_edge.height;
4099
rect.width = titlebar_rect.width - left_titlebar_edge.width -
4100
right_titlebar_edge.width;
4101
rect.height = titlebar_rect.height - top_titlebar_edge.height - bottom_titlebar_edge.height;
4104
case META_FRAME_PIECE_TITLE:
4105
rect = fgeom->title_rect;
4108
case META_FRAME_PIECE_LEFT_EDGE:
4112
case META_FRAME_PIECE_RIGHT_EDGE:
4116
case META_FRAME_PIECE_BOTTOM_EDGE:
4120
case META_FRAME_PIECE_OVERLAY:
4123
rect.width = fgeom->width;
4124
rect.height = fgeom->height;
4127
case META_FRAME_PIECE_LAST:
4128
g_assert_not_reached ();
4136
combined_clip = rect;
4138
gdk_rectangle_intersect ((GdkRectangle*) clip, /* const cast */
4142
if (combined_clip.width > 0 && combined_clip.height > 0)
4144
MetaDrawOpList *op_list;
4145
MetaFrameStyle *parent;
4149
while (parent && op_list == NULL)
4151
op_list = parent->pieces[i];
4152
parent = parent->parent;
4156
meta_draw_op_list_draw (op_list,
4161
rect.x, rect.y, rect.width, rect.height);
4165
/* Draw buttons just before overlay */
4166
if ((i + 1) == META_FRAME_PIECE_OVERLAY)
4168
int middle_bg_offset;
4170
middle_bg_offset = 0;
4172
while (j < META_BUTTON_TYPE_LAST)
4174
button_rect (j, fgeom, middle_bg_offset, &rect);
4180
combined_clip = rect;
4182
gdk_rectangle_intersect ((GdkRectangle*) clip, /* const cast */
4186
if (combined_clip.width > 0 && combined_clip.height > 0)
4188
MetaDrawOpList *op_list;
4190
op_list = get_button (style, j, button_states[j]);
4193
meta_draw_op_list_draw (op_list,
4198
rect.x, rect.y, rect.width, rect.height);
4201
/* MIDDLE_BACKGROUND type may get drawn more than once */
4202
if ((j == META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND ||
4203
j == META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND) &&
4204
middle_bg_offset < MAX_MIDDLE_BACKGROUNDS)
4210
middle_bg_offset = 0;
4221
meta_frame_style_set_new (MetaFrameStyleSet *parent)
4223
MetaFrameStyleSet *style_set;
4225
style_set = g_new0 (MetaFrameStyleSet, 1);
4227
style_set->parent = parent;
4229
meta_frame_style_set_ref (parent);
4231
style_set->refcount = 1;
4237
free_focus_styles (MetaFrameStyle *focus_styles[META_FRAME_FOCUS_LAST])
4242
while (i < META_FRAME_FOCUS_LAST)
4244
if (focus_styles[i])
4245
meta_frame_style_unref (focus_styles[i]);
4252
meta_frame_style_set_ref (MetaFrameStyleSet *style_set)
4254
g_return_if_fail (style_set != NULL);
4256
style_set->refcount += 1;
4260
meta_frame_style_set_unref (MetaFrameStyleSet *style_set)
4262
g_return_if_fail (style_set != NULL);
4263
g_return_if_fail (style_set->refcount > 0);
4265
style_set->refcount -= 1;
4267
if (style_set->refcount == 0)
4272
while (i < META_FRAME_RESIZE_LAST)
4274
free_focus_styles (style_set->normal_styles[i]);
4279
free_focus_styles (style_set->maximized_styles);
4280
free_focus_styles (style_set->shaded_styles);
4281
free_focus_styles (style_set->maximized_and_shaded_styles);
4283
if (style_set->parent)
4284
meta_frame_style_set_unref (style_set->parent);
4286
DEBUG_FILL_STRUCT (style_set);
4292
static MetaFrameStyle*
4293
get_style (MetaFrameStyleSet *style_set,
4294
MetaFrameState state,
4295
MetaFrameResize resize,
4296
MetaFrameFocus focus)
4298
MetaFrameStyle *style;
4302
if (state == META_FRAME_STATE_NORMAL)
4304
style = style_set->normal_styles[resize][focus];
4306
/* Try parent if we failed here */
4307
if (style == NULL && style_set->parent)
4308
style = get_style (style_set->parent, state, resize, focus);
4310
/* Allow people to omit the vert/horz/none resize modes */
4311
if (style == NULL &&
4312
resize != META_FRAME_RESIZE_BOTH)
4313
style = get_style (style_set, state, META_FRAME_RESIZE_BOTH, focus);
4317
MetaFrameStyle **styles;
4323
case META_FRAME_STATE_SHADED:
4324
styles = style_set->shaded_styles;
4326
case META_FRAME_STATE_MAXIMIZED:
4327
styles = style_set->maximized_styles;
4329
case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
4330
styles = style_set->maximized_and_shaded_styles;
4332
case META_FRAME_STATE_NORMAL:
4333
case META_FRAME_STATE_LAST:
4334
g_assert_not_reached ();
4338
style = styles[focus];
4340
/* Try parent if we failed here */
4341
if (style == NULL && style_set->parent)
4342
style = get_style (style_set->parent, state, resize, focus);
4349
check_state (MetaFrameStyleSet *style_set,
4350
MetaFrameState state,
4356
while (i < META_FRAME_FOCUS_LAST)
4358
if (get_style (style_set, state,
4359
META_FRAME_RESIZE_NONE, i) == NULL)
4361
g_set_error (error, META_THEME_ERROR,
4362
META_THEME_ERROR_FAILED,
4363
_("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
4364
meta_frame_state_to_string (state),
4365
meta_frame_resize_to_string (META_FRAME_RESIZE_NONE),
4366
meta_frame_focus_to_string (i));
4377
meta_frame_style_set_validate (MetaFrameStyleSet *style_set,
4382
g_return_val_if_fail (style_set != NULL, FALSE);
4385
while (i < META_FRAME_RESIZE_LAST)
4388
while (j < META_FRAME_FOCUS_LAST)
4390
if (get_style (style_set, META_FRAME_STATE_NORMAL,
4393
g_set_error (error, META_THEME_ERROR,
4394
META_THEME_ERROR_FAILED,
4395
_("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
4396
meta_frame_state_to_string (META_FRAME_STATE_NORMAL),
4397
meta_frame_resize_to_string (i),
4398
meta_frame_focus_to_string (j));
4407
if (!check_state (style_set, META_FRAME_STATE_SHADED, error))
4410
if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED, error))
4413
if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED_AND_SHADED, error))
4419
static MetaTheme *meta_current_theme = NULL;
4422
meta_theme_get_current (void)
4424
return meta_current_theme;
4428
meta_theme_set_current (const char *name,
4429
gboolean force_reload)
4431
MetaTheme *new_theme;
4434
meta_topic (META_DEBUG_THEMES, "Setting current theme to \"%s\"\n", name);
4436
if (!force_reload &&
4437
meta_current_theme &&
4438
strcmp (name, meta_current_theme->name) == 0)
4442
new_theme = meta_theme_load (name, &err);
4444
if (new_theme == NULL)
4446
meta_warning (_("Failed to load theme \"%s\": %s\n"),
4447
name, err->message);
4452
if (meta_current_theme)
4453
meta_theme_free (meta_current_theme);
4455
meta_current_theme = new_theme;
4457
meta_topic (META_DEBUG_THEMES, "New theme is \"%s\"\n", meta_current_theme->name);
4462
meta_theme_new (void)
4466
theme = g_new0 (MetaTheme, 1);
4468
theme->images_by_filename =
4469
g_hash_table_new_full (g_str_hash,
4472
(GDestroyNotify) g_object_unref);
4474
theme->layouts_by_name =
4475
g_hash_table_new_full (g_str_hash,
4478
(GDestroyNotify) meta_frame_layout_unref);
4480
theme->draw_op_lists_by_name =
4481
g_hash_table_new_full (g_str_hash,
4484
(GDestroyNotify) meta_draw_op_list_unref);
4486
theme->styles_by_name =
4487
g_hash_table_new_full (g_str_hash,
4490
(GDestroyNotify) meta_frame_style_unref);
4492
theme->style_sets_by_name =
4493
g_hash_table_new_full (g_str_hash,
4496
(GDestroyNotify) meta_frame_style_set_unref);
4503
free_menu_ops (MetaDrawOpList *op_lists[META_MENU_ICON_TYPE_LAST][N_GTK_STATES])
4508
while (i < META_MENU_ICON_TYPE_LAST)
4511
while (j < N_GTK_STATES)
4514
meta_draw_op_list_unref (op_lists[i][j]);
4524
meta_theme_free (MetaTheme *theme)
4528
g_return_if_fail (theme != NULL);
4530
g_free (theme->name);
4531
g_free (theme->dirname);
4532
g_free (theme->filename);
4533
g_free (theme->readable_name);
4534
g_free (theme->date);
4535
g_free (theme->description);
4536
g_free (theme->author);
4537
g_free (theme->copyright);
4539
g_hash_table_destroy (theme->integer_constants);
4540
g_hash_table_destroy (theme->images_by_filename);
4541
g_hash_table_destroy (theme->layouts_by_name);
4542
g_hash_table_destroy (theme->draw_op_lists_by_name);
4543
g_hash_table_destroy (theme->styles_by_name);
4544
g_hash_table_destroy (theme->style_sets_by_name);
4547
while (i < META_FRAME_TYPE_LAST)
4549
if (theme->style_sets_by_type[i])
4550
meta_frame_style_set_unref (theme->style_sets_by_type[i]);
4554
free_menu_ops (theme->menu_icons);
4556
DEBUG_FILL_STRUCT (theme);
4560
static MetaDrawOpList*
4561
get_menu_icon (MetaTheme *theme,
4562
MetaMenuIconType type,
4565
MetaDrawOpList *op_list;
4567
op_list = theme->menu_icons[type][state];
4569
/* We fall back to normal if other states aren't found */
4570
if (op_list == NULL &&
4571
state != GTK_STATE_NORMAL)
4572
return get_menu_icon (theme, type, GTK_STATE_NORMAL);
4578
meta_theme_validate (MetaTheme *theme,
4583
g_return_val_if_fail (theme != NULL, FALSE);
4585
/* FIXME what else should be checked? */
4587
g_assert (theme->name);
4589
if (theme->readable_name == NULL)
4591
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4592
_("No <%s> set for theme \"%s\""), "name", theme->name);
4596
if (theme->author == NULL)
4598
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4599
_("No <%s> set for theme \"%s\""), "author", theme->name);
4603
if (theme->date == NULL)
4605
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4606
_("No <%s> set for theme \"%s\""), "date", theme->name);
4610
if (theme->description == NULL)
4612
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4613
_("No <%s> set for theme \"%s\""), "description", theme->name);
4617
if (theme->copyright == NULL)
4619
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4620
_("No <%s> set for theme \"%s\""), "copyright", theme->name);
4625
while (i < (int) META_FRAME_TYPE_LAST)
4627
if (theme->style_sets_by_type[i] == NULL)
4629
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4630
_("No frame style set for window type \"%s\" in theme \"%s\", add a <window type=\"%s\" style_set=\"whatever\"/> element"),
4631
meta_frame_type_to_string (i),
4633
meta_frame_type_to_string (i));
4642
while (i < META_MENU_ICON_TYPE_LAST)
4645
while (j < N_GTK_STATES)
4648
if (get_menu_icon (theme, i, j) == NULL)
4650
g_set_error (error, META_THEME_ERROR,
4651
META_THEME_ERROR_FAILED,
4652
_("<menu_icon function=\"%s\" state=\"%s\" draw_ops=\"whatever\"/> must be specified for this theme"),
4653
meta_menu_icon_type_to_string (i),
4654
meta_gtk_state_to_string (j));
4668
meta_theme_load_image (MetaTheme *theme,
4669
const char *filename,
4674
pixbuf = g_hash_table_lookup (theme->images_by_filename,
4681
full_path = g_build_filename (theme->dirname, filename, NULL);
4683
pixbuf = gdk_pixbuf_new_from_file (full_path, error);
4692
g_hash_table_replace (theme->images_by_filename,
4693
g_strdup (filename),
4699
g_object_ref (G_OBJECT (pixbuf));
4704
static MetaFrameStyle*
4705
theme_get_style (MetaTheme *theme,
4707
MetaFrameFlags flags)
4709
MetaFrameState state;
4710
MetaFrameResize resize;
4711
MetaFrameFocus focus;
4712
MetaFrameStyle *style;
4713
MetaFrameStyleSet *style_set;
4715
style_set = theme->style_sets_by_type[type];
4717
/* Right now the parser forces a style set for all types,
4718
* but this fallback code is here in case I take that out.
4720
if (style_set == NULL)
4721
style_set = theme->style_sets_by_type[META_FRAME_TYPE_NORMAL];
4722
if (style_set == NULL)
4725
switch (flags & (META_FRAME_MAXIMIZED | META_FRAME_SHADED))
4728
state = META_FRAME_STATE_NORMAL;
4730
case META_FRAME_MAXIMIZED:
4731
state = META_FRAME_STATE_MAXIMIZED;
4733
case META_FRAME_SHADED:
4734
state = META_FRAME_STATE_SHADED;
4736
case (META_FRAME_MAXIMIZED | META_FRAME_SHADED):
4737
state = META_FRAME_STATE_MAXIMIZED_AND_SHADED;
4740
g_assert_not_reached ();
4741
state = META_FRAME_STATE_LAST; /* compiler */
4745
switch (flags & (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE))
4748
resize = META_FRAME_RESIZE_NONE;
4750
case META_FRAME_ALLOWS_VERTICAL_RESIZE:
4751
resize = META_FRAME_RESIZE_VERTICAL;
4753
case META_FRAME_ALLOWS_HORIZONTAL_RESIZE:
4754
resize = META_FRAME_RESIZE_HORIZONTAL;
4756
case (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE):
4757
resize = META_FRAME_RESIZE_BOTH;
4760
g_assert_not_reached ();
4761
resize = META_FRAME_RESIZE_LAST; /* compiler */
4765
/* re invert the styles used for focus/unfocussed while flashing a frame */
4766
if (((flags & META_FRAME_HAS_FOCUS) && !(flags & META_FRAME_IS_FLASHING))
4767
|| (!(flags & META_FRAME_HAS_FOCUS) && (flags & META_FRAME_IS_FLASHING)))
4768
focus = META_FRAME_FOCUS_YES;
4770
focus = META_FRAME_FOCUS_NO;
4772
style = get_style (style_set, state, resize, focus);
4778
meta_theme_get_frame_style (MetaTheme *theme,
4780
MetaFrameFlags flags)
4782
MetaFrameStyle *style;
4784
g_return_val_if_fail (type < META_FRAME_TYPE_LAST, NULL);
4786
style = theme_get_style (theme, type, flags);
4792
meta_theme_get_title_scale (MetaTheme *theme,
4794
MetaFrameFlags flags)
4796
MetaFrameStyle *style;
4798
g_return_val_if_fail (type < META_FRAME_TYPE_LAST, 1.0);
4800
style = theme_get_style (theme, type, flags);
4802
/* Parser is not supposed to allow this currently */
4806
return style->layout->title_scale;
4810
meta_theme_draw_frame (MetaTheme *theme,
4812
GdkDrawable *drawable,
4813
const GdkRectangle *clip,
4817
MetaFrameFlags flags,
4820
PangoLayout *title_layout,
4822
const MetaButtonLayout *button_layout,
4823
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
4824
GdkPixbuf *mini_icon,
4827
MetaFrameGeometry fgeom;
4828
MetaFrameStyle *style;
4830
g_return_if_fail (type < META_FRAME_TYPE_LAST);
4832
style = theme_get_style (theme, type, flags);
4834
/* Parser is not supposed to allow this currently */
4838
meta_frame_layout_calc_geometry (style->layout,
4841
client_width, client_height,
4845
meta_frame_style_draw (style,
4851
client_width, client_height,
4859
meta_theme_draw_menu_icon (MetaTheme *theme,
4861
GdkDrawable *drawable,
4862
const GdkRectangle *clip,
4867
MetaMenuIconType type)
4870
MetaDrawOpList *op_list;
4872
g_return_if_fail (type < META_BUTTON_TYPE_LAST);
4874
op_list = get_menu_icon (theme, type,
4875
GTK_WIDGET_STATE (widget));
4877
info.mini_icon = NULL;
4879
info.title_layout = NULL;
4880
info.title_layout_width = 0;
4881
info.title_layout_height = 0;
4884
meta_draw_op_list_draw (op_list,
4889
x_offset, y_offset, width, height);
4893
meta_theme_get_frame_borders (MetaTheme *theme,
4896
MetaFrameFlags flags,
4902
MetaFrameStyle *style;
4904
g_return_if_fail (type < META_FRAME_TYPE_LAST);
4915
style = theme_get_style (theme, type, flags);
4917
/* Parser is not supposed to allow this currently */
4921
meta_frame_layout_get_borders (style->layout,
4924
top_height, bottom_height,
4925
left_width, right_width);
4929
meta_theme_calc_geometry (MetaTheme *theme,
4932
MetaFrameFlags flags,
4935
const MetaButtonLayout *button_layout,
4936
MetaFrameGeometry *fgeom)
4938
MetaFrameStyle *style;
4940
g_return_if_fail (type < META_FRAME_TYPE_LAST);
4942
style = theme_get_style (theme, type, flags);
4944
/* Parser is not supposed to allow this currently */
4948
meta_frame_layout_calc_geometry (style->layout,
4951
client_width, client_height,
4957
meta_theme_lookup_layout (MetaTheme *theme,
4960
return g_hash_table_lookup (theme->layouts_by_name, name);
4964
meta_theme_insert_layout (MetaTheme *theme,
4966
MetaFrameLayout *layout)
4968
meta_frame_layout_ref (layout);
4969
g_hash_table_replace (theme->layouts_by_name, g_strdup (name), layout);
4973
meta_theme_lookup_draw_op_list (MetaTheme *theme,
4976
return g_hash_table_lookup (theme->draw_op_lists_by_name, name);
4980
meta_theme_insert_draw_op_list (MetaTheme *theme,
4982
MetaDrawOpList *op_list)
4984
meta_draw_op_list_ref (op_list);
4985
g_hash_table_replace (theme->draw_op_lists_by_name, g_strdup (name), op_list);
4989
meta_theme_lookup_style (MetaTheme *theme,
4992
return g_hash_table_lookup (theme->styles_by_name, name);
4996
meta_theme_insert_style (MetaTheme *theme,
4998
MetaFrameStyle *style)
5000
meta_frame_style_ref (style);
5001
g_hash_table_replace (theme->styles_by_name, g_strdup (name), style);
5005
meta_theme_lookup_style_set (MetaTheme *theme,
5008
return g_hash_table_lookup (theme->style_sets_by_name, name);
5012
meta_theme_insert_style_set (MetaTheme *theme,
5014
MetaFrameStyleSet *style_set)
5016
meta_frame_style_set_ref (style_set);
5017
g_hash_table_replace (theme->style_sets_by_name, g_strdup (name), style_set);
5021
first_uppercase (const char *str)
5023
return g_ascii_isupper (*str);
5027
meta_theme_define_int_constant (MetaTheme *theme,
5032
if (theme->integer_constants == NULL)
5033
theme->integer_constants = g_hash_table_new_full (g_str_hash,
5038
if (!first_uppercase (name))
5040
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5041
_("User-defined constants must begin with a capital letter; \"%s\" does not"),
5046
if (g_hash_table_lookup_extended (theme->integer_constants, name, NULL, NULL))
5048
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5049
_("Constant \"%s\" has already been defined"),
5055
g_hash_table_insert (theme->integer_constants,
5057
GINT_TO_POINTER (value));
5063
meta_theme_lookup_int_constant (MetaTheme *theme,
5071
if (theme->integer_constants == NULL)
5074
if (g_hash_table_lookup_extended (theme->integer_constants,
5075
name, NULL, &old_value))
5077
*value = GPOINTER_TO_INT (old_value);
5087
meta_theme_define_float_constant (MetaTheme *theme,
5094
if (theme->float_constants == NULL)
5095
theme->float_constants = g_hash_table_new_full (g_str_hash,
5100
if (!first_uppercase (name))
5102
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5103
_("User-defined constants must begin with a capital letter; \"%s\" does not"),
5108
if (g_hash_table_lookup_extended (theme->float_constants, name, NULL, NULL))
5110
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5111
_("Constant \"%s\" has already been defined"),
5117
d = g_new (double, 1);
5120
g_hash_table_insert (theme->float_constants,
5121
g_strdup (name), d);
5127
meta_theme_lookup_float_constant (MetaTheme *theme,
5135
if (theme->float_constants == NULL)
5138
d = g_hash_table_lookup (theme->float_constants, name);
5151
PangoFontDescription*
5152
meta_gtk_widget_get_font_desc (GtkWidget *widget,
5154
const PangoFontDescription *override)
5156
PangoFontDescription *font_desc;
5158
g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
5160
font_desc = pango_font_description_copy (widget->style->font_desc);
5163
pango_font_description_merge (font_desc, override, TRUE);
5165
pango_font_description_set_size (font_desc,
5166
MAX (pango_font_description_get_size (font_desc) * scale, 1));
5172
meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc,
5173
PangoContext *context)
5175
PangoFontMetrics *metrics;
5176
PangoLanguage *lang;
5179
lang = pango_context_get_language (context);
5180
metrics = pango_context_get_metrics (context, font_desc, lang);
5182
retval = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
5183
pango_font_metrics_get_descent (metrics));
5185
pango_font_metrics_unref (metrics);
5190
MetaGtkColorComponent
5191
meta_color_component_from_string (const char *str)
5193
if (strcmp ("fg", str) == 0)
5194
return META_GTK_COLOR_FG;
5195
else if (strcmp ("bg", str) == 0)
5196
return META_GTK_COLOR_BG;
5197
else if (strcmp ("light", str) == 0)
5198
return META_GTK_COLOR_LIGHT;
5199
else if (strcmp ("dark", str) == 0)
5200
return META_GTK_COLOR_DARK;
5201
else if (strcmp ("mid", str) == 0)
5202
return META_GTK_COLOR_MID;
5203
else if (strcmp ("text", str) == 0)
5204
return META_GTK_COLOR_TEXT;
5205
else if (strcmp ("base", str) == 0)
5206
return META_GTK_COLOR_BASE;
5207
else if (strcmp ("text_aa", str) == 0)
5208
return META_GTK_COLOR_TEXT_AA;
5210
return META_GTK_COLOR_LAST;
5214
meta_color_component_to_string (MetaGtkColorComponent component)
5218
case META_GTK_COLOR_FG:
5220
case META_GTK_COLOR_BG:
5222
case META_GTK_COLOR_LIGHT:
5224
case META_GTK_COLOR_DARK:
5226
case META_GTK_COLOR_MID:
5228
case META_GTK_COLOR_TEXT:
5230
case META_GTK_COLOR_BASE:
5232
case META_GTK_COLOR_TEXT_AA:
5234
case META_GTK_COLOR_LAST:
5242
meta_button_state_from_string (const char *str)
5244
if (strcmp ("normal", str) == 0)
5245
return META_BUTTON_STATE_NORMAL;
5246
else if (strcmp ("pressed", str) == 0)
5247
return META_BUTTON_STATE_PRESSED;
5248
else if (strcmp ("prelight", str) == 0)
5249
return META_BUTTON_STATE_PRELIGHT;
5251
return META_BUTTON_STATE_LAST;
5255
meta_button_state_to_string (MetaButtonState state)
5259
case META_BUTTON_STATE_NORMAL:
5261
case META_BUTTON_STATE_PRESSED:
5263
case META_BUTTON_STATE_PRELIGHT:
5265
case META_BUTTON_STATE_LAST:
5273
meta_button_type_from_string (const char *str)
5275
if (strcmp ("close", str) == 0)
5276
return META_BUTTON_TYPE_CLOSE;
5277
else if (strcmp ("maximize", str) == 0)
5278
return META_BUTTON_TYPE_MAXIMIZE;
5279
else if (strcmp ("minimize", str) == 0)
5280
return META_BUTTON_TYPE_MINIMIZE;
5281
else if (strcmp ("menu", str) == 0)
5282
return META_BUTTON_TYPE_MENU;
5283
else if (strcmp ("left_left_background", str) == 0)
5284
return META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND;
5285
else if (strcmp ("left_middle_background", str) == 0)
5286
return META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND;
5287
else if (strcmp ("left_right_background", str) == 0)
5288
return META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND;
5289
else if (strcmp ("right_left_background", str) == 0)
5290
return META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND;
5291
else if (strcmp ("right_middle_background", str) == 0)
5292
return META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND;
5293
else if (strcmp ("right_right_background", str) == 0)
5294
return META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND;
5296
return META_BUTTON_TYPE_LAST;
5300
meta_button_type_to_string (MetaButtonType type)
5304
case META_BUTTON_TYPE_CLOSE:
5306
case META_BUTTON_TYPE_MAXIMIZE:
5308
case META_BUTTON_TYPE_MINIMIZE:
5310
case META_BUTTON_TYPE_MENU:
5312
case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
5313
return "left_left_background";
5314
case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
5315
return "left_middle_background";
5316
case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
5317
return "left_right_background";
5318
case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
5319
return "right_left_background";
5320
case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
5321
return "right_middle_background";
5322
case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
5323
return "right_right_background";
5324
case META_BUTTON_TYPE_LAST:
5332
meta_menu_icon_type_from_string (const char *str)
5334
if (strcmp ("close", str) == 0)
5335
return META_MENU_ICON_TYPE_CLOSE;
5336
else if (strcmp ("maximize", str) == 0)
5337
return META_MENU_ICON_TYPE_MAXIMIZE;
5338
else if (strcmp ("minimize", str) == 0)
5339
return META_MENU_ICON_TYPE_MINIMIZE;
5340
else if (strcmp ("unmaximize", str) == 0)
5341
return META_MENU_ICON_TYPE_UNMAXIMIZE;
5343
return META_MENU_ICON_TYPE_LAST;
5347
meta_menu_icon_type_to_string (MetaMenuIconType type)
5351
case META_MENU_ICON_TYPE_CLOSE:
5353
case META_MENU_ICON_TYPE_MAXIMIZE:
5355
case META_MENU_ICON_TYPE_MINIMIZE:
5357
case META_MENU_ICON_TYPE_UNMAXIMIZE:
5358
return "unmaximize";
5359
case META_MENU_ICON_TYPE_LAST:
5367
meta_frame_piece_from_string (const char *str)
5369
if (strcmp ("entire_background", str) == 0)
5370
return META_FRAME_PIECE_ENTIRE_BACKGROUND;
5371
else if (strcmp ("titlebar", str) == 0)
5372
return META_FRAME_PIECE_TITLEBAR;
5373
else if (strcmp ("titlebar_middle", str) == 0)
5374
return META_FRAME_PIECE_TITLEBAR_MIDDLE;
5375
else if (strcmp ("left_titlebar_edge", str) == 0)
5376
return META_FRAME_PIECE_LEFT_TITLEBAR_EDGE;
5377
else if (strcmp ("right_titlebar_edge", str) == 0)
5378
return META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE;
5379
else if (strcmp ("top_titlebar_edge", str) == 0)
5380
return META_FRAME_PIECE_TOP_TITLEBAR_EDGE;
5381
else if (strcmp ("bottom_titlebar_edge", str) == 0)
5382
return META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE;
5383
else if (strcmp ("title", str) == 0)
5384
return META_FRAME_PIECE_TITLE;
5385
else if (strcmp ("left_edge", str) == 0)
5386
return META_FRAME_PIECE_LEFT_EDGE;
5387
else if (strcmp ("right_edge", str) == 0)
5388
return META_FRAME_PIECE_RIGHT_EDGE;
5389
else if (strcmp ("bottom_edge", str) == 0)
5390
return META_FRAME_PIECE_BOTTOM_EDGE;
5391
else if (strcmp ("overlay", str) == 0)
5392
return META_FRAME_PIECE_OVERLAY;
5394
return META_FRAME_PIECE_LAST;
5398
meta_frame_piece_to_string (MetaFramePiece piece)
5402
case META_FRAME_PIECE_ENTIRE_BACKGROUND:
5403
return "entire_background";
5404
case META_FRAME_PIECE_TITLEBAR:
5406
case META_FRAME_PIECE_TITLEBAR_MIDDLE:
5407
return "titlebar_middle";
5408
case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE:
5409
return "left_titlebar_edge";
5410
case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE:
5411
return "right_titlebar_edge";
5412
case META_FRAME_PIECE_TOP_TITLEBAR_EDGE:
5413
return "top_titlebar_edge";
5414
case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE:
5415
return "bottom_titlebar_edge";
5416
case META_FRAME_PIECE_TITLE:
5418
case META_FRAME_PIECE_LEFT_EDGE:
5420
case META_FRAME_PIECE_RIGHT_EDGE:
5421
return "right_edge";
5422
case META_FRAME_PIECE_BOTTOM_EDGE:
5423
return "bottom_edge";
5424
case META_FRAME_PIECE_OVERLAY:
5426
case META_FRAME_PIECE_LAST:
5434
meta_frame_state_from_string (const char *str)
5436
if (strcmp ("normal", str) == 0)
5437
return META_FRAME_STATE_NORMAL;
5438
else if (strcmp ("maximized", str) == 0)
5439
return META_FRAME_STATE_MAXIMIZED;
5440
else if (strcmp ("shaded", str) == 0)
5441
return META_FRAME_STATE_SHADED;
5442
else if (strcmp ("maximized_and_shaded", str) == 0)
5443
return META_FRAME_STATE_MAXIMIZED_AND_SHADED;
5445
return META_FRAME_STATE_LAST;
5449
meta_frame_state_to_string (MetaFrameState state)
5453
case META_FRAME_STATE_NORMAL:
5455
case META_FRAME_STATE_MAXIMIZED:
5457
case META_FRAME_STATE_SHADED:
5459
case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
5460
return "maximized_and_shaded";
5461
case META_FRAME_STATE_LAST:
5469
meta_frame_resize_from_string (const char *str)
5471
if (strcmp ("none", str) == 0)
5472
return META_FRAME_RESIZE_NONE;
5473
else if (strcmp ("vertical", str) == 0)
5474
return META_FRAME_RESIZE_VERTICAL;
5475
else if (strcmp ("horizontal", str) == 0)
5476
return META_FRAME_RESIZE_HORIZONTAL;
5477
else if (strcmp ("both", str) == 0)
5478
return META_FRAME_RESIZE_BOTH;
5480
return META_FRAME_RESIZE_LAST;
5484
meta_frame_resize_to_string (MetaFrameResize resize)
5488
case META_FRAME_RESIZE_NONE:
5490
case META_FRAME_RESIZE_VERTICAL:
5492
case META_FRAME_RESIZE_HORIZONTAL:
5493
return "horizontal";
5494
case META_FRAME_RESIZE_BOTH:
5496
case META_FRAME_RESIZE_LAST:
5504
meta_frame_focus_from_string (const char *str)
5506
if (strcmp ("no", str) == 0)
5507
return META_FRAME_FOCUS_NO;
5508
else if (strcmp ("yes", str) == 0)
5509
return META_FRAME_FOCUS_YES;
5511
return META_FRAME_FOCUS_LAST;
5515
meta_frame_focus_to_string (MetaFrameFocus focus)
5519
case META_FRAME_FOCUS_NO:
5521
case META_FRAME_FOCUS_YES:
5523
case META_FRAME_FOCUS_LAST:
5531
meta_frame_type_from_string (const char *str)
5533
if (strcmp ("normal", str) == 0)
5534
return META_FRAME_TYPE_NORMAL;
5535
else if (strcmp ("dialog", str) == 0)
5536
return META_FRAME_TYPE_DIALOG;
5537
else if (strcmp ("modal_dialog", str) == 0)
5538
return META_FRAME_TYPE_MODAL_DIALOG;
5539
else if (strcmp ("utility", str) == 0)
5540
return META_FRAME_TYPE_UTILITY;
5541
else if (strcmp ("menu", str) == 0)
5542
return META_FRAME_TYPE_MENU;
5543
else if (strcmp ("border", str) == 0)
5544
return META_FRAME_TYPE_BORDER;
5546
else if (strcmp ("toolbar", str) == 0)
5547
return META_FRAME_TYPE_TOOLBAR;
5550
return META_FRAME_TYPE_LAST;
5554
meta_frame_type_to_string (MetaFrameType type)
5558
case META_FRAME_TYPE_NORMAL:
5560
case META_FRAME_TYPE_DIALOG:
5562
case META_FRAME_TYPE_MODAL_DIALOG:
5563
return "modal_dialog";
5564
case META_FRAME_TYPE_UTILITY:
5566
case META_FRAME_TYPE_MENU:
5568
case META_FRAME_TYPE_BORDER:
5571
case META_FRAME_TYPE_TOOLBAR:
5574
case META_FRAME_TYPE_LAST:
5582
meta_gradient_type_from_string (const char *str)
5584
if (strcmp ("vertical", str) == 0)
5585
return META_GRADIENT_VERTICAL;
5586
else if (strcmp ("horizontal", str) == 0)
5587
return META_GRADIENT_HORIZONTAL;
5588
else if (strcmp ("diagonal", str) == 0)
5589
return META_GRADIENT_DIAGONAL;
5591
return META_GRADIENT_LAST;
5595
meta_gradient_type_to_string (MetaGradientType type)
5599
case META_GRADIENT_VERTICAL:
5601
case META_GRADIENT_HORIZONTAL:
5602
return "horizontal";
5603
case META_GRADIENT_DIAGONAL:
5605
case META_GRADIENT_LAST:
5613
meta_gtk_state_from_string (const char *str)
5615
if (strcmp ("normal", str) == 0 || strcmp ("NORMAL", str) == 0)
5616
return GTK_STATE_NORMAL;
5617
else if (strcmp ("prelight", str) == 0 || strcmp ("PRELIGHT", str) == 0)
5618
return GTK_STATE_PRELIGHT;
5619
else if (strcmp ("active", str) == 0 || strcmp ("ACTIVE", str) == 0)
5620
return GTK_STATE_ACTIVE;
5621
else if (strcmp ("selected", str) == 0 || strcmp ("SELECTED", str) == 0)
5622
return GTK_STATE_SELECTED;
5623
else if (strcmp ("insensitive", str) == 0 || strcmp ("INSENSITIVE", str) == 0)
5624
return GTK_STATE_INSENSITIVE;
5626
return -1; /* hack */
5630
meta_gtk_state_to_string (GtkStateType state)
5634
case GTK_STATE_NORMAL:
5636
case GTK_STATE_PRELIGHT:
5638
case GTK_STATE_ACTIVE:
5640
case GTK_STATE_SELECTED:
5642
case GTK_STATE_INSENSITIVE:
5643
return "INSENSITIVE";
5650
meta_gtk_shadow_from_string (const char *str)
5652
if (strcmp ("none", str) == 0)
5653
return GTK_SHADOW_NONE;
5654
else if (strcmp ("in", str) == 0)
5655
return GTK_SHADOW_IN;
5656
else if (strcmp ("out", str) == 0)
5657
return GTK_SHADOW_OUT;
5658
else if (strcmp ("etched_in", str) == 0)
5659
return GTK_SHADOW_ETCHED_IN;
5660
else if (strcmp ("etched_out", str) == 0)
5661
return GTK_SHADOW_ETCHED_OUT;
5667
meta_gtk_shadow_to_string (GtkShadowType shadow)
5671
case GTK_SHADOW_NONE:
5675
case GTK_SHADOW_OUT:
5677
case GTK_SHADOW_ETCHED_IN:
5679
case GTK_SHADOW_ETCHED_OUT:
5680
return "etched_out";
5687
meta_gtk_arrow_from_string (const char *str)
5689
if (strcmp ("up", str) == 0)
5690
return GTK_ARROW_UP;
5691
else if (strcmp ("down", str) == 0)
5692
return GTK_ARROW_DOWN;
5693
else if (strcmp ("left", str) == 0)
5694
return GTK_ARROW_LEFT;
5695
else if (strcmp ("right", str) == 0)
5696
return GTK_ARROW_RIGHT;
5702
meta_gtk_arrow_to_string (GtkArrowType arrow)
5708
case GTK_ARROW_DOWN:
5710
case GTK_ARROW_LEFT:
5712
case GTK_ARROW_RIGHT:
5720
meta_image_fill_type_from_string (const char *str)
5722
if (strcmp ("tile", str) == 0)
5723
return META_IMAGE_FILL_TILE;
5724
else if (strcmp ("scale", str) == 0)
5725
return META_IMAGE_FILL_SCALE;
5731
meta_image_fill_type_to_string (MetaImageFillType fill_type)
5735
case META_IMAGE_FILL_TILE:
5737
case META_IMAGE_FILL_SCALE:
5744
/* gtkstyle.c cut-and-pastage */
5746
gtk_style_shade (GdkColor *a,
5754
red = (gdouble) a->red / 65535.0;
5755
green = (gdouble) a->green / 65535.0;
5756
blue = (gdouble) a->blue / 65535.0;
5758
rgb_to_hls (&red, &green, &blue);
5763
else if (green < 0.0)
5769
else if (blue < 0.0)
5772
hls_to_rgb (&red, &green, &blue);
5774
b->red = red * 65535.0;
5775
b->green = green * 65535.0;
5776
b->blue = blue * 65535.0;
5780
rgb_to_hls (gdouble *r,
5821
l = (max + min) / 2;
5828
s = (max - min) / (max + min);
5830
s = (max - min) / (2 - max - min);
5834
h = (green - blue) / delta;
5835
else if (green == max)
5836
h = 2 + (blue - red) / delta;
5837
else if (blue == max)
5838
h = 4 + (red - green) / delta;
5851
hls_to_rgb (gdouble *h,
5864
if (lightness <= 0.5)
5865
m2 = lightness * (1 + saturation);
5867
m2 = lightness + saturation - lightness * saturation;
5868
m1 = 2 * lightness - m2;
5870
if (saturation == 0)
5885
r = m1 + (m2 - m1) * hue / 60;
5889
r = m1 + (m2 - m1) * (240 - hue) / 60;
5900
g = m1 + (m2 - m1) * hue / 60;
5904
g = m1 + (m2 - m1) * (240 - hue) / 60;
5915
b = m1 + (m2 - m1) * hue / 60;
5919
b = m1 + (m2 - m1) * (240 - hue) / 60;
5930
/* These are some functions I'm saving to use in optimizing
5931
* MetaDrawOpList, namely to pre-composite pixbufs on client side
5932
* prior to rendering to the server
5935
draw_bg_solid_composite (const MetaTextureSpec *bg,
5936
const MetaTextureSpec *fg,
5939
GdkDrawable *drawable,
5940
const GdkRectangle *clip,
5941
MetaTextureDrawMode mode,
5951
g_assert (bg->type == META_TEXTURE_SOLID);
5952
g_assert (fg->type != META_TEXTURE_COMPOSITE);
5953
g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
5955
meta_color_spec_render (bg->data.solid.color_spec,
5961
case META_TEXTURE_SOLID:
5965
meta_color_spec_render (fg->data.solid.color_spec,
5969
color_composite (&bg_color, &fg_color,
5972
draw_color_rectangle (widget, drawable, &fg_color, clip,
5973
x, y, width, height);
5977
case META_TEXTURE_GRADIENT:
5978
/* FIXME I think we could just composite all the colors in
5979
* the gradient prior to generating the gradient?
5982
case META_TEXTURE_IMAGE:
5985
GdkPixbuf *composited;
5987
pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
5993
composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
5994
gdk_pixbuf_get_has_alpha (pixbuf), 8,
5995
gdk_pixbuf_get_width (pixbuf),
5996
gdk_pixbuf_get_height (pixbuf));
5998
if (composited == NULL)
6000
g_object_unref (G_OBJECT (pixbuf));
6004
gdk_pixbuf_composite_color (pixbuf,
6007
gdk_pixbuf_get_width (pixbuf),
6008
gdk_pixbuf_get_height (pixbuf),
6009
0.0, 0.0, /* offsets */
6010
1.0, 1.0, /* scale */
6011
GDK_INTERP_BILINEAR,
6013
0, 0, /* check offsets */
6015
GDK_COLOR_RGB (bg_color),
6016
GDK_COLOR_RGB (bg_color));
6018
/* Need to draw background since pixbuf is not
6019
* necessarily covering the whole thing
6021
draw_color_rectangle (widget, drawable, &bg_color, clip,
6022
x, y, width, height);
6024
render_pixbuf_aligned (drawable, clip, composited,
6026
x, y, width, height);
6028
g_object_unref (G_OBJECT (pixbuf));
6029
g_object_unref (G_OBJECT (composited));
6033
case META_TEXTURE_BLANK:
6034
case META_TEXTURE_COMPOSITE:
6035
case META_TEXTURE_SHAPE_LIST:
6036
g_assert_not_reached ();
6042
draw_bg_gradient_composite (const MetaTextureSpec *bg,
6043
const MetaTextureSpec *fg,
6046
GdkDrawable *drawable,
6047
const GdkRectangle *clip,
6048
MetaTextureDrawMode mode,
6056
g_assert (bg->type == META_TEXTURE_GRADIENT);
6057
g_assert (fg->type != META_TEXTURE_COMPOSITE);
6058
g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
6062
case META_TEXTURE_SOLID:
6063
case META_TEXTURE_GRADIENT:
6064
case META_TEXTURE_IMAGE:
6066
GdkPixbuf *bg_pixbuf;
6067
GdkPixbuf *fg_pixbuf;
6068
GdkPixbuf *composited;
6069
int fg_width, fg_height;
6071
bg_pixbuf = meta_texture_spec_render (bg, widget, mode, 255,
6074
if (bg_pixbuf == NULL)
6077
fg_pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
6080
if (fg_pixbuf == NULL)
6082
g_object_unref (G_OBJECT (bg_pixbuf));
6086
/* gradients always fill the entire target area */
6087
g_assert (gdk_pixbuf_get_width (bg_pixbuf) == width);
6088
g_assert (gdk_pixbuf_get_height (bg_pixbuf) == height);
6090
composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
6091
gdk_pixbuf_get_has_alpha (bg_pixbuf), 8,
6092
gdk_pixbuf_get_width (bg_pixbuf),
6093
gdk_pixbuf_get_height (bg_pixbuf));
6095
if (composited == NULL)
6097
g_object_unref (G_OBJECT (bg_pixbuf));
6098
g_object_unref (G_OBJECT (fg_pixbuf));
6102
fg_width = gdk_pixbuf_get_width (fg_pixbuf);
6103
fg_height = gdk_pixbuf_get_height (fg_pixbuf);
6105
/* If we wanted to be all cool we could deal with the
6106
* offsets and try to composite only in the clip rectangle,
6107
* but I just don't care enough to figure it out.
6110
gdk_pixbuf_composite (fg_pixbuf,
6112
x + (width - fg_width) * xalign,
6113
y + (height - fg_height) * yalign,
6114
gdk_pixbuf_get_width (fg_pixbuf),
6115
gdk_pixbuf_get_height (fg_pixbuf),
6116
0.0, 0.0, /* offsets */
6117
1.0, 1.0, /* scale */
6118
GDK_INTERP_BILINEAR,
6121
render_pixbuf (drawable, clip, composited, x, y);
6123
g_object_unref (G_OBJECT (bg_pixbuf));
6124
g_object_unref (G_OBJECT (fg_pixbuf));
6125
g_object_unref (G_OBJECT (composited));
6129
case META_TEXTURE_BLANK:
6130
case META_TEXTURE_SHAPE_LIST:
6131
case META_TEXTURE_COMPOSITE:
6132
g_assert_not_reached ();