1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3
/* Metacity Theme Rendering */
6
* Copyright (C) 2001 Havoc Pennington
8
* This program is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU General Public License as
10
* published by the Free Software Foundation; either version 2 of the
11
* License, or (at your option) any later version.
13
* This program is distributed in the hope that it will be useful, but
14
* WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25
* \file theme.c Making Metacity look pretty
27
* The window decorations drawn by Metacity are described by files on disk
28
* known internally as "themes" (externally as "window border themes" on
29
* http://art.gnome.org/themes/metacity/ or "Metacity themes"). This file
30
* contains most of the code necessary to support themes; it does not
31
* contain the XML parser, which is in theme-parser.c.
33
* \bug This is a big file with lots of different subsystems, which might
34
* be better split out into separate files.
38
* \defgroup tokenizer The theme expression tokenizer
40
* Themes can use a simple expression language to represent the values of
41
* things. This is the tokeniser used for that language.
43
* \bug We could remove almost all this code by using GScanner instead,
44
* but we would also have to find every expression in every existing theme
45
* we could and make sure the parse trees were the same.
49
* \defgroup parser The theme expression parser
51
* Themes can use a simple expression language to represent the values of
52
* things. This is the parser used for that language.
57
#include "theme-parser.h"
66
#define GDK_COLOR_RGBA(color) \
68
(((color).red / 256) << 24) | \
69
(((color).green / 256) << 16) | \
70
(((color).blue / 256) << 8)))
72
#define GDK_COLOR_RGB(color) \
73
((guint32) ((((color).red / 256) << 16) | \
74
(((color).green / 256) << 8) | \
75
(((color).blue / 256))))
77
#define ALPHA_TO_UCHAR(d) ((unsigned char) ((d) * 255))
79
#define DEBUG_FILL_STRUCT(s) memset ((s), 0xef, sizeof (*(s)))
80
#define CLAMP_UCHAR(v) ((guchar) (CLAMP (((int)v), (int)0, (int)255)))
81
#define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
83
static void gtk_style_shade (GdkColor *a,
86
static void rgb_to_hls (gdouble *r,
89
static void hls_to_rgb (gdouble *h,
94
* The current theme. (Themes are singleton.)
96
static MetaTheme *meta_current_theme = NULL;
99
colorize_pixbuf (GdkPixbuf *orig,
111
const guchar *src_pixels;
114
pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (orig), gdk_pixbuf_get_has_alpha (orig),
115
gdk_pixbuf_get_bits_per_sample (orig),
116
gdk_pixbuf_get_width (orig), gdk_pixbuf_get_height (orig));
121
orig_rowstride = gdk_pixbuf_get_rowstride (orig);
122
dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
123
width = gdk_pixbuf_get_width (pixbuf);
124
height = gdk_pixbuf_get_height (pixbuf);
125
has_alpha = gdk_pixbuf_get_has_alpha (orig);
126
src_pixels = gdk_pixbuf_get_pixels (orig);
127
dest_pixels = gdk_pixbuf_get_pixels (pixbuf);
129
for (y = 0; y < height; y++)
131
src = src_pixels + y * orig_rowstride;
132
dest = dest_pixels + y * dest_rowstride;
134
for (x = 0; x < width; x++)
138
intensity = INTENSITY (src[0], src[1], src[2]) / 255.0;
140
if (intensity <= 0.5)
142
/* Go from black at intensity = 0.0 to new_color at intensity = 0.5 */
143
dr = (new_color->red * intensity * 2.0) / 65535.0;
144
dg = (new_color->green * intensity * 2.0) / 65535.0;
145
db = (new_color->blue * intensity * 2.0) / 65535.0;
149
/* Go from new_color at intensity = 0.5 to white at intensity = 1.0 */
150
dr = (new_color->red + (65535 - new_color->red) * (intensity - 0.5) * 2.0) / 65535.0;
151
dg = (new_color->green + (65535 - new_color->green) * (intensity - 0.5) * 2.0) / 65535.0;
152
db = (new_color->blue + (65535 - new_color->blue) * (intensity - 0.5) * 2.0) / 65535.0;
155
dest[0] = CLAMP_UCHAR (255 * dr);
156
dest[1] = CLAMP_UCHAR (255 * dg);
157
dest[2] = CLAMP_UCHAR (255 * db);
177
color_composite (const GdkColor *bg,
185
alpha = alpha_d * 0xffff;
186
color->red = color->red + (((fg->red - color->red) * alpha + 0x8000) >> 16);
187
color->green = color->green + (((fg->green - color->green) * alpha + 0x8000) >> 16);
188
color->blue = color->blue + (((fg->blue - color->blue) * alpha + 0x8000) >> 16);
192
* Sets all the fields of a border to dummy values.
194
* \param border The border whose fields should be reset.
197
init_border (GtkBorder *border)
206
* Creates a new, empty MetaFrameLayout. The fields will be set to dummy
209
* \return The newly created MetaFrameLayout.
212
meta_frame_layout_new (void)
214
MetaFrameLayout *layout;
216
layout = g_new0 (MetaFrameLayout, 1);
218
layout->refcount = 1;
220
/* Fill with -1 values to detect invalid themes */
221
layout->left_width = -1;
222
layout->right_width = -1;
223
layout->bottom_height = -1;
225
init_border (&layout->title_border);
227
layout->title_vertical_pad = -1;
229
layout->right_titlebar_edge = -1;
230
layout->left_titlebar_edge = -1;
232
layout->button_sizing = META_BUTTON_SIZING_LAST;
233
layout->button_aspect = 1.0;
234
layout->button_width = -1;
235
layout->button_height = -1;
237
layout->has_title = TRUE;
238
layout->title_scale = 1.0;
240
init_border (&layout->button_border);
249
validate_border (const GtkBorder *border,
256
else if (border->bottom < 0)
258
else if (border->left < 0)
260
else if (border->right < 0)
267
* Ensures that the theme supplied a particular dimension. When a
268
* MetaFrameLayout is created, all its integer fields are set to -1
269
* by meta_frame_layout_new(). After an instance of this type
270
* should have been initialised, this function checks that
271
* a given field is not still at -1. It is never called directly, but
272
* rather via the CHECK_GEOMETRY_VALUE and CHECK_GEOMETRY_BORDER
275
* \param val The value to check
276
* \param name The name to use in the error message
277
* \param[out] error Set to an error if val was not initialised
280
validate_geometry_value (int val,
286
g_set_error (error, META_THEME_ERROR,
287
META_THEME_ERROR_FRAME_GEOMETRY,
288
_("frame geometry does not specify \"%s\" dimension"),
297
validate_geometry_border (const GtkBorder *border,
303
if (!validate_border (border, &bad))
305
g_set_error (error, META_THEME_ERROR,
306
META_THEME_ERROR_FRAME_GEOMETRY,
307
_("frame geometry does not specify dimension \"%s\" for border \"%s\""),
316
meta_frame_layout_validate (const MetaFrameLayout *layout,
319
g_return_val_if_fail (layout != NULL, FALSE);
321
#define CHECK_GEOMETRY_VALUE(vname) if (!validate_geometry_value (layout->vname, #vname, error)) return FALSE
323
#define CHECK_GEOMETRY_BORDER(bname) if (!validate_geometry_border (&layout->bname, #bname, error)) return FALSE
325
CHECK_GEOMETRY_VALUE (left_width);
326
CHECK_GEOMETRY_VALUE (right_width);
327
CHECK_GEOMETRY_VALUE (bottom_height);
329
CHECK_GEOMETRY_BORDER (title_border);
331
CHECK_GEOMETRY_VALUE (title_vertical_pad);
333
CHECK_GEOMETRY_VALUE (right_titlebar_edge);
334
CHECK_GEOMETRY_VALUE (left_titlebar_edge);
336
switch (layout->button_sizing)
338
case META_BUTTON_SIZING_ASPECT:
339
if (layout->button_aspect < (0.1) ||
340
layout->button_aspect > (15.0))
342
g_set_error (error, META_THEME_ERROR,
343
META_THEME_ERROR_FRAME_GEOMETRY,
344
_("Button aspect ratio %g is not reasonable"),
345
layout->button_aspect);
349
case META_BUTTON_SIZING_FIXED:
350
CHECK_GEOMETRY_VALUE (button_width);
351
CHECK_GEOMETRY_VALUE (button_height);
353
case META_BUTTON_SIZING_LAST:
354
g_set_error (error, META_THEME_ERROR,
355
META_THEME_ERROR_FRAME_GEOMETRY,
356
_("Frame geometry does not specify size of buttons"));
360
CHECK_GEOMETRY_BORDER (button_border);
366
meta_frame_layout_copy (const MetaFrameLayout *src)
368
MetaFrameLayout *layout;
370
layout = g_new0 (MetaFrameLayout, 1);
374
layout->refcount = 1;
380
meta_frame_layout_ref (MetaFrameLayout *layout)
382
g_return_if_fail (layout != NULL);
384
layout->refcount += 1;
388
meta_frame_layout_unref (MetaFrameLayout *layout)
390
g_return_if_fail (layout != NULL);
391
g_return_if_fail (layout->refcount > 0);
393
layout->refcount -= 1;
395
if (layout->refcount == 0)
397
DEBUG_FILL_STRUCT (layout);
403
meta_frame_layout_get_borders (const MetaFrameLayout *layout,
405
MetaFrameFlags flags,
411
int buttons_height, title_height;
413
g_return_if_fail (top_height != NULL);
414
g_return_if_fail (bottom_height != NULL);
415
g_return_if_fail (left_width != NULL);
416
g_return_if_fail (right_width != NULL);
418
if (!layout->has_title)
421
buttons_height = layout->button_height +
422
layout->button_border.top + layout->button_border.bottom;
423
title_height = text_height +
424
layout->title_vertical_pad +
425
layout->title_border.top + layout->title_border.bottom;
429
*top_height = MAX (buttons_height, title_height);
433
*left_width = layout->left_width;
435
*right_width = layout->right_width;
439
if (flags & META_FRAME_SHADED)
442
*bottom_height = layout->bottom_height;
445
if (flags & META_FRAME_FULLSCREEN)
458
static MetaButtonSpace*
459
rect_for_function (MetaFrameGeometry *fgeom,
460
MetaFrameFlags flags,
461
MetaButtonFunction function,
465
/* Firstly, check version-specific things. */
467
if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS))
471
case META_BUTTON_FUNCTION_SHADE:
472
if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED))
473
return &fgeom->shade_rect;
476
case META_BUTTON_FUNCTION_ABOVE:
477
if (!(flags & META_FRAME_ABOVE))
478
return &fgeom->above_rect;
481
case META_BUTTON_FUNCTION_STICK:
482
if (!(flags & META_FRAME_STUCK))
483
return &fgeom->stick_rect;
486
case META_BUTTON_FUNCTION_UNSHADE:
487
if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED))
488
return &fgeom->unshade_rect;
491
case META_BUTTON_FUNCTION_UNABOVE:
492
if (flags & META_FRAME_ABOVE)
493
return &fgeom->unabove_rect;
496
case META_BUTTON_FUNCTION_UNSTICK:
497
if (flags & META_FRAME_STUCK)
498
return &fgeom->unstick_rect;
500
/* just go on to the next switch block */;
504
/* now consider the buttons which exist in all versions */
508
case META_BUTTON_FUNCTION_MENU:
509
if (flags & META_FRAME_ALLOWS_MENU)
510
return &fgeom->menu_rect;
513
case META_BUTTON_FUNCTION_MINIMIZE:
514
if (flags & META_FRAME_ALLOWS_MINIMIZE)
515
return &fgeom->min_rect;
518
case META_BUTTON_FUNCTION_MAXIMIZE:
519
if (flags & META_FRAME_ALLOWS_MAXIMIZE)
520
return &fgeom->max_rect;
523
case META_BUTTON_FUNCTION_CLOSE:
524
if (flags & META_FRAME_ALLOWS_DELETE)
525
return &fgeom->close_rect;
528
case META_BUTTON_FUNCTION_STICK:
529
case META_BUTTON_FUNCTION_SHADE:
530
case META_BUTTON_FUNCTION_ABOVE:
531
case META_BUTTON_FUNCTION_UNSTICK:
532
case META_BUTTON_FUNCTION_UNSHADE:
533
case META_BUTTON_FUNCTION_UNABOVE:
534
/* we are being asked for a >v1 button which hasn't been handled yet,
535
* so obviously we're not in a theme which supports that version.
536
* therefore, we don't show the button. return NULL and all will
541
case META_BUTTON_FUNCTION_LAST:
549
strip_button (MetaButtonSpace *func_rects[MAX_BUTTONS_PER_CORNER],
550
GdkRectangle *bg_rects[MAX_BUTTONS_PER_CORNER],
552
MetaButtonSpace *to_strip)
559
if (func_rects[i] == to_strip)
563
/* shift the other rects back in the array */
566
func_rects[i] = func_rects[i+1];
567
bg_rects[i] = bg_rects[i+1];
572
func_rects[i] = NULL;
581
return FALSE; /* did not strip anything */
585
meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
587
MetaFrameFlags flags,
590
const MetaButtonLayout *button_layout,
591
MetaFrameGeometry *fgeom,
594
int i, n_left, n_right, n_left_spacers, n_right_spacers;
597
int title_right_edge;
599
int button_width, button_height;
600
int min_size_for_rounding;
602
/* the left/right rects in order; the max # of rects
603
* is the number of button functions
605
MetaButtonSpace *left_func_rects[MAX_BUTTONS_PER_CORNER];
606
MetaButtonSpace *right_func_rects[MAX_BUTTONS_PER_CORNER];
607
GdkRectangle *left_bg_rects[MAX_BUTTONS_PER_CORNER];
608
gboolean left_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
609
GdkRectangle *right_bg_rects[MAX_BUTTONS_PER_CORNER];
610
gboolean right_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
612
meta_frame_layout_get_borders (layout, text_height,
615
&fgeom->bottom_height,
617
&fgeom->right_width);
619
width = client_width + fgeom->left_width + fgeom->right_width;
621
height = ((flags & META_FRAME_SHADED) ? 0: client_height) +
622
fgeom->top_height + fgeom->bottom_height;
624
fgeom->width = width;
625
fgeom->height = height;
627
fgeom->top_titlebar_edge = layout->title_border.top;
628
fgeom->bottom_titlebar_edge = layout->title_border.bottom;
629
fgeom->left_titlebar_edge = layout->left_titlebar_edge;
630
fgeom->right_titlebar_edge = layout->right_titlebar_edge;
636
switch (layout->button_sizing)
638
case META_BUTTON_SIZING_ASPECT:
639
button_height = fgeom->top_height - layout->button_border.top - layout->button_border.bottom;
640
button_width = button_height / layout->button_aspect;
642
case META_BUTTON_SIZING_FIXED:
643
button_width = layout->button_width;
644
button_height = layout->button_height;
646
case META_BUTTON_SIZING_LAST:
647
g_assert_not_reached ();
651
/* FIXME all this code sort of pretends that duplicate buttons
652
* with the same function are allowed, but that breaks the
653
* code in frames.c, so isn't really allowed right now.
654
* Would need left_close_rect, right_close_rect, etc.
657
/* Init all button rects to 0, lame hack */
658
memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0',
659
LENGTH_OF_BUTTON_RECTS);
666
if (!layout->hide_buttons)
668
/* Try to fill in rects */
669
for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
671
left_func_rects[n_left] = rect_for_function (fgeom, flags,
672
button_layout->left_buttons[i],
674
if (left_func_rects[n_left] != NULL)
676
left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i];
677
if (button_layout->left_buttons_has_spacer[i])
684
for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
686
right_func_rects[n_right] = rect_for_function (fgeom, flags,
687
button_layout->right_buttons[i],
689
if (right_func_rects[n_right] != NULL)
691
right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i];
692
if (button_layout->right_buttons_has_spacer[i])
700
for (i = 0; i < MAX_BUTTONS_PER_CORNER; i++)
702
left_bg_rects[i] = NULL;
703
right_bg_rects[i] = NULL;
706
for (i = 0; i < n_left; i++)
708
if (i == 0) /* For the first button (From left to right) */
710
if (n_left > 1) /* Set left_left_background
711
if we have more than one button */
712
left_bg_rects[i] = &fgeom->left_left_background;
713
else /* No background if we have only one single button */
714
left_bg_rects[i] = &fgeom->left_single_background;
716
else if (i == (n_left - 1))
717
left_bg_rects[i] = &fgeom->left_right_background;
719
left_bg_rects[i] = &fgeom->left_middle_backgrounds[i - 1];
722
for (i = 0; i < n_right; i++)
724
if (i == (n_right - 1)) /* For the first button (From right to left) */
726
if (n_right > 1) /* Set right_right_background
727
if we have more than one button */
728
right_bg_rects[i] = &fgeom->right_right_background;
729
else /* No background if we have only one single button */
730
right_bg_rects[i] = &fgeom->right_single_background;
733
right_bg_rects[i] = &fgeom->right_left_background;
735
right_bg_rects[i] = &fgeom->right_middle_backgrounds[i - 1];
738
/* Be sure buttons fit */
739
while (n_left > 0 || n_right > 0)
741
int space_used_by_buttons;
744
space_available = fgeom->width - layout->left_titlebar_edge - layout->right_titlebar_edge;
746
space_used_by_buttons = 0;
748
space_used_by_buttons += button_width * n_left;
749
space_used_by_buttons += (button_width * 0.75) * n_left_spacers;
750
space_used_by_buttons += layout->button_border.left * n_left;
751
space_used_by_buttons += layout->button_border.right * n_left;
753
space_used_by_buttons += button_width * n_right;
754
space_used_by_buttons += (button_width * 0.75) * n_right_spacers;
755
space_used_by_buttons += layout->button_border.left * n_right;
756
space_used_by_buttons += layout->button_border.right * n_right;
758
if (space_used_by_buttons <= space_available)
759
break; /* Everything fits, bail out */
761
/* First try to remove separators */
762
if (n_left_spacers > 0)
764
left_buttons_has_spacer[--n_left_spacers] = FALSE;
767
else if (n_right_spacers > 0)
769
right_buttons_has_spacer[--n_right_spacers] = FALSE;
773
/* Otherwise we need to shave out a button. Shave
774
* above, stick, shade, min, max, close, then menu (menu is most useful);
775
* prefer the default button locations.
777
if (strip_button (left_func_rects, left_bg_rects,
778
&n_left, &fgeom->above_rect))
780
else if (strip_button (right_func_rects, right_bg_rects,
781
&n_right, &fgeom->above_rect))
783
else if (strip_button (left_func_rects, left_bg_rects,
784
&n_left, &fgeom->stick_rect))
786
else if (strip_button (right_func_rects, right_bg_rects,
787
&n_right, &fgeom->stick_rect))
789
else if (strip_button (left_func_rects, left_bg_rects,
790
&n_left, &fgeom->shade_rect))
792
else if (strip_button (right_func_rects, right_bg_rects,
793
&n_right, &fgeom->shade_rect))
795
else if (strip_button (left_func_rects, left_bg_rects,
796
&n_left, &fgeom->min_rect))
798
else if (strip_button (right_func_rects, right_bg_rects,
799
&n_right, &fgeom->min_rect))
801
else if (strip_button (left_func_rects, left_bg_rects,
802
&n_left, &fgeom->max_rect))
804
else if (strip_button (right_func_rects, right_bg_rects,
805
&n_right, &fgeom->max_rect))
807
else if (strip_button (left_func_rects, left_bg_rects,
808
&n_left, &fgeom->close_rect))
810
else if (strip_button (right_func_rects, right_bg_rects,
811
&n_right, &fgeom->close_rect))
813
else if (strip_button (right_func_rects, right_bg_rects,
814
&n_right, &fgeom->menu_rect))
816
else if (strip_button (left_func_rects, left_bg_rects,
817
&n_left, &fgeom->menu_rect))
821
meta_bug ("Could not find a button to strip. n_left = %d n_right = %d\n",
826
/* center buttons vertically */
827
button_y = (fgeom->top_height -
828
(button_height + layout->button_border.top + layout->button_border.bottom)) / 2 + layout->button_border.top;
830
/* right edge of farthest-right button */
831
x = width - layout->right_titlebar_edge;
836
MetaButtonSpace *rect;
838
if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */
841
rect = right_func_rects[i];
842
rect->visible.x = x - layout->button_border.right - button_width;
843
if (right_buttons_has_spacer[i])
844
rect->visible.x -= (button_width * 0.75);
846
rect->visible.y = button_y;
847
rect->visible.width = button_width;
848
rect->visible.height = button_height;
850
if (flags & META_FRAME_MAXIMIZED)
852
rect->clickable.x = rect->visible.x;
853
rect->clickable.y = rect->visible.y;
854
rect->clickable.width = button_width;
855
rect->clickable.height = button_height;
857
if (i == n_right - 1)
858
rect->clickable.width += layout->right_titlebar_edge + layout->right_width + layout->button_border.right;
862
g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
864
*(right_bg_rects[i]) = rect->visible;
866
x = rect->visible.x - layout->button_border.left;
871
/* save right edge of titlebar for later use */
872
title_right_edge = x - layout->title_border.right;
874
/* Now x changes to be position from the left and we go through
875
* the left-side buttons
877
x = layout->left_titlebar_edge;
878
for (i = 0; i < n_left; i++)
880
MetaButtonSpace *rect;
882
rect = left_func_rects[i];
884
rect->visible.x = x + layout->button_border.left;
885
rect->visible.y = button_y;
886
rect->visible.width = button_width;
887
rect->visible.height = button_height;
889
if (flags & META_FRAME_MAXIMIZED)
891
rect->clickable.x = rect->visible.x;
892
rect->clickable.y = rect->visible.y;
893
rect->clickable.width = button_width;
894
rect->clickable.height = button_height;
897
g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
899
x = rect->visible.x + rect->visible.width + layout->button_border.right;
900
if (left_buttons_has_spacer[i])
901
x += (button_width * 0.75);
903
*(left_bg_rects[i]) = rect->visible;
906
/* We always fill as much vertical space as possible with title rect,
907
* rather than centering it like the buttons
909
fgeom->title_rect.x = x + layout->title_border.left;
910
fgeom->title_rect.y = layout->title_border.top;
911
fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x;
912
fgeom->title_rect.height = fgeom->top_height - layout->title_border.top - layout->title_border.bottom;
914
/* Nuke title if it won't fit */
915
if (fgeom->title_rect.width < 0 ||
916
fgeom->title_rect.height < 0)
918
fgeom->title_rect.width = 0;
919
fgeom->title_rect.height = 0;
922
if (flags & META_FRAME_SHADED)
923
min_size_for_rounding = 0;
925
min_size_for_rounding = 5;
927
fgeom->top_left_corner_rounded_radius = 0;
928
fgeom->top_right_corner_rounded_radius = 0;
929
fgeom->bottom_left_corner_rounded_radius = 0;
930
fgeom->bottom_right_corner_rounded_radius = 0;
932
if (fgeom->top_height + fgeom->left_width >= min_size_for_rounding)
933
fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius;
934
if (fgeom->top_height + fgeom->right_width >= min_size_for_rounding)
935
fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius;
937
if (fgeom->bottom_height + fgeom->left_width >= min_size_for_rounding)
938
fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius;
939
if (fgeom->bottom_height + fgeom->right_width >= min_size_for_rounding)
940
fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius;
944
meta_gradient_spec_new (MetaGradientType type)
946
MetaGradientSpec *spec;
948
spec = g_new (MetaGradientSpec, 1);
951
spec->color_specs = NULL;
957
free_color_spec (gpointer spec, gpointer user_data)
959
meta_color_spec_free (spec);
963
meta_gradient_spec_free (MetaGradientSpec *spec)
965
g_return_if_fail (spec != NULL);
967
g_slist_foreach (spec->color_specs, free_color_spec, NULL);
968
g_slist_free (spec->color_specs);
970
DEBUG_FILL_STRUCT (spec);
975
meta_gradient_spec_render (const MetaGradientSpec *spec,
986
n_colors = g_slist_length (spec->color_specs);
991
colors = g_new (GdkColor, n_colors);
994
tmp = spec->color_specs;
997
meta_color_spec_render (tmp->data, widget, &colors[i]);
1003
pixbuf = meta_gradient_create_multi (width, height,
1013
meta_gradient_spec_validate (MetaGradientSpec *spec,
1016
g_return_val_if_fail (spec != NULL, FALSE);
1018
if (g_slist_length (spec->color_specs) < 2)
1020
g_set_error (error, META_THEME_ERROR,
1021
META_THEME_ERROR_FAILED,
1022
_("Gradients should have at least two colors"));
1029
MetaAlphaGradientSpec*
1030
meta_alpha_gradient_spec_new (MetaGradientType type,
1033
MetaAlphaGradientSpec *spec;
1035
g_return_val_if_fail (n_alphas > 0, NULL);
1037
spec = g_new0 (MetaAlphaGradientSpec, 1);
1040
spec->alphas = g_new0 (unsigned char, n_alphas);
1041
spec->n_alphas = n_alphas;
1047
meta_alpha_gradient_spec_free (MetaAlphaGradientSpec *spec)
1049
g_return_if_fail (spec != NULL);
1051
g_free (spec->alphas);
1055
MetaShadowProperties*
1056
meta_shadow_properties_new (void)
1058
MetaShadowProperties *properties;
1060
properties = g_new0 (MetaShadowProperties, 1);
1064
properties->unity_shadow_radius = 0.0f;
1065
properties->unity_shadow_x_offset = 0;
1066
properties->unity_shadow_y_offset = 0;
1067
properties->unity_shadow_color = NULL;
1074
meta_shadow_properties_free (MetaShadowProperties *properties)
1076
g_return_if_fail (properties != NULL);
1078
meta_color_spec_free (properties->unity_shadow_color);
1079
g_free (properties);
1082
MetaInvisibleGrabAreaProperties*
1083
meta_invisible_grab_area_properties_new (void)
1085
MetaInvisibleGrabAreaProperties *properties;
1087
properties = g_new0 (MetaInvisibleGrabAreaProperties, 1);
1093
meta_invisible_grab_area_properties_free (MetaInvisibleGrabAreaProperties *properties)
1095
g_return_if_fail (properties != NULL);
1097
g_free (properties);
1101
meta_color_spec_new (MetaColorSpecType type)
1103
MetaColorSpec *spec;
1104
MetaColorSpec dummy;
1107
size = G_STRUCT_OFFSET (MetaColorSpec, data);
1111
case META_COLOR_SPEC_BASIC:
1112
size += sizeof (dummy.data.basic);
1115
case META_COLOR_SPEC_GTK:
1116
size += sizeof (dummy.data.gtk);
1119
case META_COLOR_SPEC_BLEND:
1120
size += sizeof (dummy.data.blend);
1123
case META_COLOR_SPEC_SHADE:
1124
size += sizeof (dummy.data.shade);
1128
spec = g_malloc0 (size);
1136
meta_color_spec_free (MetaColorSpec *spec)
1138
g_return_if_fail (spec != NULL);
1142
case META_COLOR_SPEC_BASIC:
1143
DEBUG_FILL_STRUCT (&spec->data.basic);
1146
case META_COLOR_SPEC_GTK:
1147
DEBUG_FILL_STRUCT (&spec->data.gtk);
1150
case META_COLOR_SPEC_BLEND:
1151
if (spec->data.blend.foreground)
1152
meta_color_spec_free (spec->data.blend.foreground);
1153
if (spec->data.blend.background)
1154
meta_color_spec_free (spec->data.blend.background);
1155
DEBUG_FILL_STRUCT (&spec->data.blend);
1158
case META_COLOR_SPEC_SHADE:
1159
if (spec->data.shade.base)
1160
meta_color_spec_free (spec->data.shade.base);
1161
DEBUG_FILL_STRUCT (&spec->data.shade);
1169
meta_color_spec_new_from_string (const char *str,
1172
MetaColorSpec *spec;
1176
if (str[0] == 'g' && str[1] == 't' && str[2] == 'k' && str[3] == ':')
1179
const char *bracket;
1180
const char *end_bracket;
1183
MetaGtkColorComponent component;
1186
while (*bracket && *bracket != '[')
1189
if (*bracket == '\0')
1191
g_set_error (err, META_THEME_ERROR,
1192
META_THEME_ERROR_FAILED,
1193
_("GTK color specification must have the state in brackets, e.g. gtk:fg[NORMAL] where NORMAL is the state; could not parse \"%s\""),
1198
end_bracket = bracket;
1200
while (*end_bracket && *end_bracket != ']')
1203
if (*end_bracket == '\0')
1205
g_set_error (err, META_THEME_ERROR,
1206
META_THEME_ERROR_FAILED,
1207
_("GTK color specification must have a close bracket after the state, e.g. gtk:fg[NORMAL] where NORMAL is the state; could not parse \"%s\""),
1212
tmp = g_strndup (bracket + 1, end_bracket - bracket - 1);
1213
state = meta_gtk_state_from_string (tmp);
1214
if (((int) state) == -1)
1216
g_set_error (err, META_THEME_ERROR,
1217
META_THEME_ERROR_FAILED,
1218
_("Did not understand state \"%s\" in color specification"),
1225
tmp = g_strndup (str + 4, bracket - str - 4);
1226
component = meta_color_component_from_string (tmp);
1227
if (component == META_GTK_COLOR_LAST)
1229
g_set_error (err, META_THEME_ERROR,
1230
META_THEME_ERROR_FAILED,
1231
_("Did not understand color component \"%s\" in color specification"),
1238
spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
1239
spec->data.gtk.state = state;
1240
spec->data.gtk.component = component;
1241
g_assert (spec->data.gtk.state < N_GTK_STATES);
1242
g_assert (spec->data.gtk.component < META_GTK_COLOR_LAST);
1244
else if (str[0] == 'b' && str[1] == 'l' && str[2] == 'e' && str[3] == 'n' &&
1245
str[4] == 'd' && str[5] == '/')
1254
split = g_strsplit (str, "/", 4);
1256
if (split[0] == NULL || split[1] == NULL ||
1257
split[2] == NULL || split[3] == NULL)
1259
g_set_error (err, META_THEME_ERROR,
1260
META_THEME_ERROR_FAILED,
1261
_("Blend format is \"blend/bg_color/fg_color/alpha\", \"%s\" does not fit the format"),
1267
alpha = g_ascii_strtod (split[3], &end);
1268
if (end == split[3])
1270
g_set_error (err, META_THEME_ERROR,
1271
META_THEME_ERROR_FAILED,
1272
_("Could not parse alpha value \"%s\" in blended color"),
1278
if (alpha < (0.0 - 1e6) || alpha > (1.0 + 1e6))
1280
g_set_error (err, META_THEME_ERROR,
1281
META_THEME_ERROR_FAILED,
1282
_("Alpha value \"%s\" in blended color is not between 0.0 and 1.0"),
1291
bg = meta_color_spec_new_from_string (split[1], err);
1298
fg = meta_color_spec_new_from_string (split[2], err);
1301
meta_color_spec_free (bg);
1308
spec = meta_color_spec_new (META_COLOR_SPEC_BLEND);
1309
spec->data.blend.alpha = alpha;
1310
spec->data.blend.background = bg;
1311
spec->data.blend.foreground = fg;
1313
else if (str[0] == 's' && str[1] == 'h' && str[2] == 'a' && str[3] == 'd' &&
1314
str[4] == 'e' && str[5] == '/')
1320
MetaColorSpec *base;
1322
split = g_strsplit (str, "/", 3);
1324
if (split[0] == NULL || split[1] == NULL ||
1327
g_set_error (err, META_THEME_ERROR,
1328
META_THEME_ERROR_FAILED,
1329
_("Shade format is \"shade/base_color/factor\", \"%s\" does not fit the format"),
1335
factor = g_ascii_strtod (split[2], &end);
1336
if (end == split[2])
1338
g_set_error (err, META_THEME_ERROR,
1339
META_THEME_ERROR_FAILED,
1340
_("Could not parse shade factor \"%s\" in shaded color"),
1346
if (factor < (0.0 - 1e6))
1348
g_set_error (err, META_THEME_ERROR,
1349
META_THEME_ERROR_FAILED,
1350
_("Shade factor \"%s\" in shaded color is negative"),
1358
base = meta_color_spec_new_from_string (split[1], err);
1367
spec = meta_color_spec_new (META_COLOR_SPEC_SHADE);
1368
spec->data.shade.factor = factor;
1369
spec->data.shade.base = base;
1373
spec = meta_color_spec_new (META_COLOR_SPEC_BASIC);
1375
if (!gdk_color_parse (str, &spec->data.basic.color))
1377
g_set_error (err, META_THEME_ERROR,
1378
META_THEME_ERROR_FAILED,
1379
_("Could not parse color \"%s\""),
1381
meta_color_spec_free (spec);
1392
meta_color_spec_new_gtk (MetaGtkColorComponent component,
1395
MetaColorSpec *spec;
1397
spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
1399
spec->data.gtk.component = component;
1400
spec->data.gtk.state = state;
1406
meta_color_spec_render (MetaColorSpec *spec,
1410
GtkStyle *widget_style;
1412
g_return_if_fail (spec != NULL);
1413
g_return_if_fail (GTK_IS_WIDGET (widget));
1415
widget_style = gtk_widget_get_style (widget);
1416
g_return_if_fail (widget_style != NULL);
1420
case META_COLOR_SPEC_BASIC:
1421
*color = spec->data.basic.color;
1424
case META_COLOR_SPEC_GTK:
1425
switch (spec->data.gtk.component)
1427
case META_GTK_COLOR_BG:
1428
*color = widget_style->bg[spec->data.gtk.state];
1430
case META_GTK_COLOR_FG:
1431
*color = widget_style->fg[spec->data.gtk.state];
1433
case META_GTK_COLOR_BASE:
1434
*color = widget_style->base[spec->data.gtk.state];
1436
case META_GTK_COLOR_TEXT:
1437
*color = widget_style->text[spec->data.gtk.state];
1439
case META_GTK_COLOR_LIGHT:
1440
*color = widget_style->light[spec->data.gtk.state];
1442
case META_GTK_COLOR_DARK:
1443
*color = widget_style->dark[spec->data.gtk.state];
1445
case META_GTK_COLOR_MID:
1446
*color = widget_style->mid[spec->data.gtk.state];
1448
case META_GTK_COLOR_TEXT_AA:
1449
*color = widget_style->text_aa[spec->data.gtk.state];
1451
case META_GTK_COLOR_LAST:
1452
g_assert_not_reached ();
1457
case META_COLOR_SPEC_BLEND:
1461
meta_color_spec_render (spec->data.blend.background, widget, &bg);
1462
meta_color_spec_render (spec->data.blend.foreground, widget, &fg);
1464
color_composite (&bg, &fg, spec->data.blend.alpha,
1465
&spec->data.blend.color);
1467
*color = spec->data.blend.color;
1471
case META_COLOR_SPEC_SHADE:
1473
meta_color_spec_render (spec->data.shade.base, widget,
1474
&spec->data.shade.color);
1476
gtk_style_shade (&spec->data.shade.color,
1477
&spec->data.shade.color, spec->data.shade.factor);
1479
*color = spec->data.shade.color;
1486
* Represents an operation as a string.
1488
* \param type an operation, such as addition
1489
* \return a string, such as "+"
1492
op_name (PosOperatorType type)
1498
case POS_OP_SUBTRACT:
1500
case POS_OP_MULTIPLY:
1518
* Parses a string and returns an operation.
1520
* \param p a pointer into a string representing an operation; part of an
1521
* expression somewhere, so not null-terminated
1522
* \param len set to the length of the string found. Set to 0 if none is.
1523
* \return the operation found. If none was, returns POS_OP_NONE.
1525
static PosOperatorType
1526
op_from_string (const char *p,
1538
return POS_OP_SUBTRACT;
1541
return POS_OP_MULTIPLY;
1544
return POS_OP_DIVIDE;
1559
else if (p[0] == '`' &&
1574
* Frees an array of tokens. All the tokens and their associated memory
1577
* \param tokens an array of tokens to be freed
1578
* \param n_tokens how many tokens are in the array.
1581
free_tokens (PosToken *tokens,
1586
/* n_tokens can be 0 since tokens may have been allocated more than
1587
* it was initialized
1590
for (i = 0; i < n_tokens; i++)
1591
if (tokens[i].type == POS_TOKEN_VARIABLE)
1592
g_free (tokens[i].d.v.name);
1598
* Tokenises a number in an expression.
1600
* \param p a pointer into a string representing an operation; part of an
1601
* expression somewhere, so not null-terminated
1602
* \param end_return set to a pointer to the end of the number found; but
1603
* not updated if no number was found at all
1604
* \param next set to either an integer or a float token
1605
* \param[out] err set to the problem if there was a problem
1606
* \return TRUE if a valid number was found, FALSE otherwise (and "err" will
1609
* \bug The "while (*start)..." part: what's wrong with strchr-ish things?
1610
* \bug The name is wrong: it doesn't parse anything.
1611
* \ingroup tokenizer
1614
parse_number (const char *p,
1615
const char **end_return,
1619
const char *start = p;
1624
while (*p && (*p == '.' || g_ascii_isdigit (*p)))
1629
char buf[7] = { '\0' };
1630
buf[g_unichar_to_utf8 (g_utf8_get_char (p), buf)] = '\0';
1631
g_set_error (err, META_THEME_ERROR,
1632
META_THEME_ERROR_BAD_CHARACTER,
1633
_("Coordinate expression contains character '%s' which is not allowed"),
1640
/* we need this to exclude floats like "1e6" */
1641
num_str = g_strndup (start, p - start);
1653
next->type = POS_TOKEN_DOUBLE;
1654
next->d.d.val = g_ascii_strtod (num_str, &end);
1658
g_set_error (err, META_THEME_ERROR,
1659
META_THEME_ERROR_FAILED,
1660
_("Coordinate expression contains floating point number '%s' which could not be parsed"),
1668
next->type = POS_TOKEN_INT;
1669
next->d.i.val = strtol (num_str, &end, 10);
1672
g_set_error (err, META_THEME_ERROR,
1673
META_THEME_ERROR_FAILED,
1674
_("Coordinate expression contains integer '%s' which could not be parsed"),
1683
g_assert (next->type == POS_TOKEN_INT || next->type == POS_TOKEN_DOUBLE);
1689
* Whether a variable can validly appear as part of the name of a variable.
1691
#define IS_VARIABLE_CHAR(c) (g_ascii_isalpha ((c)) || (c) == '_')
1695
debug_print_tokens (PosToken *tokens,
1700
for (i = 0; i < n_tokens; i++)
1702
PosToken *t = &tokens[i];
1709
g_print ("\"%d\"", t->d.i.val);
1711
case POS_TOKEN_DOUBLE:
1712
g_print ("\"%g\"", t->d.d.val);
1714
case POS_TOKEN_OPEN_PAREN:
1717
case POS_TOKEN_CLOSE_PAREN:
1720
case POS_TOKEN_VARIABLE:
1721
g_print ("\"%s\"", t->d.v.name);
1723
case POS_TOKEN_OPERATOR:
1724
g_print ("\"%s\"", op_name (t->d.o.op));
1734
* Tokenises an expression.
1736
* \param expr The expression
1737
* \param[out] tokens_p The resulting tokens
1738
* \param[out] n_tokens_p The number of resulting tokens
1739
* \param[out] err set to the problem if there was a problem
1741
* \return True if the expression was successfully tokenised; false otherwise.
1743
* \ingroup tokenizer
1746
pos_tokenize (const char *expr,
1747
PosToken **tokens_p,
1761
tokens = g_new (PosToken, allocated);
1769
if (n_tokens == allocated)
1772
tokens = g_renew (PosToken, tokens, allocated);
1775
next = &tokens[n_tokens];
1782
case '-': /* negative numbers aren't allowed so this is easy */
1785
next->type = POS_TOKEN_OPERATOR;
1786
next->d.o.op = op_from_string (p, &len);
1787
if (next->d.o.op != POS_OP_NONE)
1790
p = p + (len - 1); /* -1 since we ++p later */
1794
g_set_error (err, META_THEME_ERROR,
1795
META_THEME_ERROR_FAILED,
1796
_("Coordinate expression contained unknown operator at the start of this text: \"%s\""),
1804
next->type = POS_TOKEN_OPEN_PAREN;
1809
next->type = POS_TOKEN_CLOSE_PAREN;
1819
if (IS_VARIABLE_CHAR (*p))
1821
/* Assume variable */
1822
const char *start = p;
1823
while (*p && IS_VARIABLE_CHAR (*p))
1825
g_assert (p != start);
1826
next->type = POS_TOKEN_VARIABLE;
1827
next->d.v.name = g_strndup (start, p - start);
1829
--p; /* since we ++p again at the end of while loop */
1836
if (!parse_number (p, &end, next, err))
1840
p = end - 1; /* -1 since we ++p again at the end of while loop */
1851
g_set_error (err, META_THEME_ERROR,
1852
META_THEME_ERROR_FAILED,
1853
_("Coordinate expression was empty or not understood"));
1859
*n_tokens_p = n_tokens;
1864
g_assert (err == NULL || *err != NULL);
1866
free_tokens (tokens, n_tokens);
1871
* The type of a PosExpr: either integer, double, or an operation.
1882
* Type and value of an expression in a parsed sequence. We don't
1883
* keep expressions in a tree; if this is of type POS_EXPR_OPERATOR,
1884
* the arguments of the operator will be in the array positions
1885
* immediately preceding and following this operator; they cannot
1886
* themselves be operators.
1888
* \bug operator is char; it should really be of PosOperatorType.
1904
debug_print_exprs (PosExpr *exprs,
1909
for (i = 0; i < n_exprs; i++)
1911
switch (exprs[i].type)
1914
g_print (" %d", exprs[i].d.int_val);
1916
case POS_EXPR_DOUBLE:
1917
g_print (" %g", exprs[i].d.double_val);
1919
case POS_EXPR_OPERATOR:
1920
g_print (" %s", op_name (exprs[i].d.operator));
1929
do_operation (PosExpr *a,
1934
/* Promote types to double if required */
1935
if (a->type == POS_EXPR_DOUBLE ||
1936
b->type == POS_EXPR_DOUBLE)
1938
if (a->type != POS_EXPR_DOUBLE)
1940
a->type = POS_EXPR_DOUBLE;
1941
a->d.double_val = a->d.int_val;
1943
if (b->type != POS_EXPR_DOUBLE)
1945
b->type = POS_EXPR_DOUBLE;
1946
b->d.double_val = b->d.int_val;
1950
g_assert (a->type == b->type);
1952
if (a->type == POS_EXPR_INT)
1956
case POS_OP_MULTIPLY:
1957
a->d.int_val = a->d.int_val * b->d.int_val;
1960
if (b->d.int_val == 0)
1962
g_set_error (err, META_THEME_ERROR,
1963
META_THEME_ERROR_DIVIDE_BY_ZERO,
1964
_("Coordinate expression results in division by zero"));
1967
a->d.int_val = a->d.int_val / b->d.int_val;
1970
if (b->d.int_val == 0)
1972
g_set_error (err, META_THEME_ERROR,
1973
META_THEME_ERROR_DIVIDE_BY_ZERO,
1974
_("Coordinate expression results in division by zero"));
1977
a->d.int_val = a->d.int_val % b->d.int_val;
1980
a->d.int_val = a->d.int_val + b->d.int_val;
1982
case POS_OP_SUBTRACT:
1983
a->d.int_val = a->d.int_val - b->d.int_val;
1986
a->d.int_val = MAX (a->d.int_val, b->d.int_val);
1989
a->d.int_val = MIN (a->d.int_val, b->d.int_val);
1992
g_assert_not_reached ();
1996
else if (a->type == POS_EXPR_DOUBLE)
2000
case POS_OP_MULTIPLY:
2001
a->d.double_val = a->d.double_val * b->d.double_val;
2004
if (b->d.double_val == 0.0)
2006
g_set_error (err, META_THEME_ERROR,
2007
META_THEME_ERROR_DIVIDE_BY_ZERO,
2008
_("Coordinate expression results in division by zero"));
2011
a->d.double_val = a->d.double_val / b->d.double_val;
2014
g_set_error (err, META_THEME_ERROR,
2015
META_THEME_ERROR_MOD_ON_FLOAT,
2016
_("Coordinate expression tries to use mod operator on a floating-point number"));
2019
a->d.double_val = a->d.double_val + b->d.double_val;
2021
case POS_OP_SUBTRACT:
2022
a->d.double_val = a->d.double_val - b->d.double_val;
2025
a->d.double_val = MAX (a->d.double_val, b->d.double_val);
2028
a->d.double_val = MIN (a->d.double_val, b->d.double_val);
2031
g_assert_not_reached ();
2036
g_assert_not_reached ();
2042
do_operations (PosExpr *exprs,
2050
g_print ("Doing prec %d ops on %d exprs\n", precedence, *n_exprs);
2051
debug_print_exprs (exprs, *n_exprs);
2055
while (i < *n_exprs)
2059
/* exprs[i-1] first operand
2061
* exprs[i+1] second operand
2063
* we replace first operand with result of mul/div/mod,
2064
* or skip over operator and second operand if we have
2068
if (exprs[i-1].type == POS_EXPR_OPERATOR)
2070
g_set_error (err, META_THEME_ERROR,
2071
META_THEME_ERROR_FAILED,
2072
_("Coordinate expression has an operator \"%s\" where an operand was expected"),
2073
op_name (exprs[i-1].d.operator));
2077
if (exprs[i].type != POS_EXPR_OPERATOR)
2079
g_set_error (err, META_THEME_ERROR,
2080
META_THEME_ERROR_FAILED,
2081
_("Coordinate expression had an operand where an operator was expected"));
2085
if (i == (*n_exprs - 1))
2087
g_set_error (err, META_THEME_ERROR,
2088
META_THEME_ERROR_FAILED,
2089
_("Coordinate expression ended with an operator instead of an operand"));
2093
g_assert ((i+1) < *n_exprs);
2095
if (exprs[i+1].type == POS_EXPR_OPERATOR)
2097
g_set_error (err, META_THEME_ERROR,
2098
META_THEME_ERROR_FAILED,
2099
_("Coordinate expression has operator \"%c\" following operator \"%c\" with no operand in between"),
2100
exprs[i+1].d.operator,
2101
exprs[i].d.operator);
2110
switch (exprs[i].d.operator)
2114
case POS_OP_MULTIPLY:
2116
if (!do_operation (&exprs[i-1], &exprs[i+1],
2117
exprs[i].d.operator,
2124
switch (exprs[i].d.operator)
2127
case POS_OP_SUBTRACT:
2129
if (!do_operation (&exprs[i-1], &exprs[i+1],
2130
exprs[i].d.operator,
2136
/* I have no rationale at all for making these low-precedence */
2138
switch (exprs[i].d.operator)
2143
if (!do_operation (&exprs[i-1], &exprs[i+1],
2144
exprs[i].d.operator,
2154
/* exprs[i-1] first operand (now result)
2156
* exprs[i+1] second operand
2157
* exprs[i+2] new operator
2159
* we move new operator just after first operand
2161
if ((i+2) < *n_exprs)
2163
g_memmove (&exprs[i], &exprs[i+2],
2164
sizeof (PosExpr) * (*n_exprs - i - 2));
2171
/* Skip operator and next operand */
2180
* There is a predefined set of variables which can appear in an expression.
2181
* Here we take a token representing a variable, and return the current value
2182
* of that variable in a particular environment.
2183
* (The value is always an integer.)
2185
* There are supposedly some circumstances in which this function can be
2186
* called from outside Metacity, in which case env->theme will be NULL, and
2187
* therefore we can't use it to find out quark values, so we do the comparison
2188
* using strcmp, which is slower.
2190
* \param t The token representing a variable
2191
* \param[out] result The value of that variable; not set if the token did
2192
* not represent a known variable
2193
* \param env The environment within which t should be evaluated
2194
* \param[out] err set to the problem if there was a problem
2196
* \return true if we found the variable asked for, false if we didn't
2198
* \bug shouldn't t be const?
2199
* \bug we should perhaps consider some sort of lookup arrangement into an
2200
* array; also, the duplication of code is unlovely; perhaps using glib
2201
* string hashes instead of quarks would fix both problems?
2205
pos_eval_get_variable (PosToken *t,
2207
const MetaPositionExprEnv *env,
2212
if (t->d.v.name_quark == env->theme->quark_width)
2213
*result = env->rect.width;
2214
else if (t->d.v.name_quark == env->theme->quark_height)
2215
*result = env->rect.height;
2216
else if (env->object_width >= 0 &&
2217
t->d.v.name_quark == env->theme->quark_object_width)
2218
*result = env->object_width;
2219
else if (env->object_height >= 0 &&
2220
t->d.v.name_quark == env->theme->quark_object_height)
2221
*result = env->object_height;
2222
else if (t->d.v.name_quark == env->theme->quark_left_width)
2223
*result = env->left_width;
2224
else if (t->d.v.name_quark == env->theme->quark_right_width)
2225
*result = env->right_width;
2226
else if (t->d.v.name_quark == env->theme->quark_top_height)
2227
*result = env->top_height;
2228
else if (t->d.v.name_quark == env->theme->quark_bottom_height)
2229
*result = env->bottom_height;
2230
else if (t->d.v.name_quark == env->theme->quark_mini_icon_width)
2231
*result = env->mini_icon_width;
2232
else if (t->d.v.name_quark == env->theme->quark_mini_icon_height)
2233
*result = env->mini_icon_height;
2234
else if (t->d.v.name_quark == env->theme->quark_icon_width)
2235
*result = env->icon_width;
2236
else if (t->d.v.name_quark == env->theme->quark_icon_height)
2237
*result = env->icon_height;
2238
else if (t->d.v.name_quark == env->theme->quark_title_width)
2239
*result = env->title_width;
2240
else if (t->d.v.name_quark == env->theme->quark_title_height)
2241
*result = env->title_height;
2244
g_set_error (err, META_THEME_ERROR,
2245
META_THEME_ERROR_UNKNOWN_VARIABLE,
2246
_("Coordinate expression had unknown variable or constant \"%s\""),
2253
if (strcmp (t->d.v.name, "width") == 0)
2254
*result = env->rect.width;
2255
else if (strcmp (t->d.v.name, "height") == 0)
2256
*result = env->rect.height;
2257
else if (env->object_width >= 0 &&
2258
strcmp (t->d.v.name, "object_width") == 0)
2259
*result = env->object_width;
2260
else if (env->object_height >= 0 &&
2261
strcmp (t->d.v.name, "object_height") == 0)
2262
*result = env->object_height;
2263
else if (strcmp (t->d.v.name, "left_width") == 0)
2264
*result = env->left_width;
2265
else if (strcmp (t->d.v.name, "right_width") == 0)
2266
*result = env->right_width;
2267
else if (strcmp (t->d.v.name, "top_height") == 0)
2268
*result = env->top_height;
2269
else if (strcmp (t->d.v.name, "bottom_height") == 0)
2270
*result = env->bottom_height;
2271
else if (strcmp (t->d.v.name, "mini_icon_width") == 0)
2272
*result = env->mini_icon_width;
2273
else if (strcmp (t->d.v.name, "mini_icon_height") == 0)
2274
*result = env->mini_icon_height;
2275
else if (strcmp (t->d.v.name, "icon_width") == 0)
2276
*result = env->icon_width;
2277
else if (strcmp (t->d.v.name, "icon_height") == 0)
2278
*result = env->icon_height;
2279
else if (strcmp (t->d.v.name, "title_width") == 0)
2280
*result = env->title_width;
2281
else if (strcmp (t->d.v.name, "title_height") == 0)
2282
*result = env->title_height;
2285
g_set_error (err, META_THEME_ERROR,
2286
META_THEME_ERROR_UNKNOWN_VARIABLE,
2287
_("Coordinate expression had unknown variable or constant \"%s\""),
2297
* Evaluates a sequence of tokens within a particular environment context,
2298
* and returns the current value. May recur if parantheses are found.
2300
* \param tokens A list of tokens to evaluate.
2301
* \param n_tokens How many tokens are in the list.
2302
* \param env The environment context in which to evaluate the expression.
2303
* \param[out] result The current value of the expression
2305
* \bug Yes, we really do reparse the expression every time it's evaluated.
2306
* We should keep the parse tree around all the time and just
2307
* run the new values through it.
2311
pos_eval_helper (PosToken *tokens,
2313
const MetaPositionExprEnv *env,
2317
/* Lazy-ass hardcoded limit on number of terms in expression */
2318
#define MAX_EXPRS 32
2322
PosExpr exprs[MAX_EXPRS];
2326
/* Our first goal is to get a list of PosExpr, essentially
2327
* substituting variables and handling parentheses.
2333
for (i = 0; i < n_tokens; i++)
2335
PosToken *t = &tokens[i];
2337
if (n_exprs >= MAX_EXPRS)
2339
g_set_error (err, META_THEME_ERROR,
2340
META_THEME_ERROR_FAILED,
2341
_("Coordinate expression parser overflowed its buffer."));
2345
if (paren_level == 0)
2350
exprs[n_exprs].type = POS_EXPR_INT;
2351
exprs[n_exprs].d.int_val = t->d.i.val;
2355
case POS_TOKEN_DOUBLE:
2356
exprs[n_exprs].type = POS_EXPR_DOUBLE;
2357
exprs[n_exprs].d.double_val = t->d.d.val;
2361
case POS_TOKEN_OPEN_PAREN:
2363
if (paren_level == 1)
2367
case POS_TOKEN_CLOSE_PAREN:
2368
g_set_error (err, META_THEME_ERROR,
2369
META_THEME_ERROR_BAD_PARENS,
2370
_("Coordinate expression had a close parenthesis with no open parenthesis"));
2373
case POS_TOKEN_VARIABLE:
2374
exprs[n_exprs].type = POS_EXPR_INT;
2376
/* FIXME we should just dump all this crap
2377
* in a hash, maybe keep width/height out
2378
* for optimization purposes
2380
if (!pos_eval_get_variable (t, &exprs[n_exprs].d.int_val, env, err))
2386
case POS_TOKEN_OPERATOR:
2387
exprs[n_exprs].type = POS_EXPR_OPERATOR;
2388
exprs[n_exprs].d.operator = t->d.o.op;
2395
g_assert (paren_level > 0);
2400
case POS_TOKEN_DOUBLE:
2401
case POS_TOKEN_VARIABLE:
2402
case POS_TOKEN_OPERATOR:
2405
case POS_TOKEN_OPEN_PAREN:
2409
case POS_TOKEN_CLOSE_PAREN:
2410
if (paren_level == 1)
2412
/* We closed a toplevel paren group, so recurse */
2413
if (!pos_eval_helper (&tokens[first_paren+1],
2414
i - first_paren - 1,
2430
if (paren_level > 0)
2432
g_set_error (err, META_THEME_ERROR,
2433
META_THEME_ERROR_BAD_PARENS,
2434
_("Coordinate expression had an open parenthesis with no close parenthesis"));
2438
/* Now we have no parens and no vars; so we just do all the multiplies
2439
* and divides, then all the add and subtract.
2443
g_set_error (err, META_THEME_ERROR,
2444
META_THEME_ERROR_FAILED,
2445
_("Coordinate expression doesn't seem to have any operators or operands"));
2449
/* precedence 1 ops */
2451
while (precedence >= 0)
2453
if (!do_operations (exprs, &n_exprs, precedence, err))
2458
g_assert (n_exprs == 1);
2466
* expr = int | double | expr * expr | expr / expr |
2467
* expr + expr | expr - expr | (expr)
2469
* so very not worth fooling with bison, yet so very painful by hand.
2472
* Evaluates an expression.
2474
* \param spec The expression to evaluate.
2475
* \param env The environment context to evaluate the expression in.
2476
* \param[out] val_p The integer value of the expression; if the expression
2477
* is of type float, this will be rounded. If we return
2478
* FALSE because the expression is invalid, this will be
2480
* \param[out] err The error, if anything went wrong.
2482
* \return True if we evaluated the expression successfully; false otherwise.
2484
* \bug Shouldn't spec be const?
2488
pos_eval (MetaDrawSpec *spec,
2489
const MetaPositionExprEnv *env,
2497
if (pos_eval_helper (spec->tokens, spec->n_tokens, env, &expr, err))
2502
*val_p = expr.d.int_val;
2504
case POS_EXPR_DOUBLE:
2505
*val_p = expr.d.double_val;
2507
case POS_EXPR_OPERATOR:
2508
g_assert_not_reached ();
2519
/* We always return both X and Y, but only one will be meaningful in
2524
meta_parse_position_expression (MetaDrawSpec *spec,
2525
const MetaPositionExprEnv *env,
2530
/* All positions are in a coordinate system with x, y at the origin.
2531
* The expression can have -, +, *, / as operators, floating point
2532
* or integer constants, and the variables "width" and "height" and
2533
* optionally "object_width" and object_height". Negative numbers
2542
if (pos_eval (spec, env, &spec->value, err) == FALSE)
2544
g_assert (err == NULL || *err != NULL);
2552
*x_return = env->rect.x + val;
2554
*y_return = env->rect.y + val;
2561
meta_parse_size_expression (MetaDrawSpec *spec,
2562
const MetaPositionExprEnv *env,
2572
if (pos_eval (spec, env, &spec->value, err) == FALSE)
2574
g_assert (err == NULL || *err != NULL);
2582
*val_return = MAX (val, 1); /* require that sizes be at least 1x1 */
2587
/* To do this we tokenize, replace variable tokens
2588
* that are constants, then reassemble. The purpose
2589
* here is to optimize expressions so we don't do hash
2590
* lookups to eval them. Obviously it's a tradeoff that
2591
* slows down theme load times.
2594
meta_theme_replace_constants (MetaTheme *theme,
2602
gboolean is_constant = TRUE;
2604
/* Loop through tokenized string looking for variables to replace */
2605
for (i = 0; i < n_tokens; i++)
2607
PosToken *t = &tokens[i];
2609
if (t->type == POS_TOKEN_VARIABLE)
2611
if (meta_theme_lookup_int_constant (theme, t->d.v.name, &ival))
2613
g_free (t->d.v.name);
2614
t->type = POS_TOKEN_INT;
2617
else if (meta_theme_lookup_float_constant (theme, t->d.v.name, &dval))
2619
g_free (t->d.v.name);
2620
t->type = POS_TOKEN_DOUBLE;
2625
/* If we've found a variable that cannot be replaced then the
2626
expression is not a constant expression and we want to
2627
replace it with a GQuark */
2629
t->d.v.name_quark = g_quark_from_string (t->d.v.name);
2630
is_constant = FALSE;
2639
parse_x_position_unchecked (MetaDrawSpec *spec,
2640
const MetaPositionExprEnv *env)
2647
if (!meta_parse_position_expression (spec, env, &retval, NULL, &error))
2649
meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
2652
g_error_free (error);
2659
parse_y_position_unchecked (MetaDrawSpec *spec,
2660
const MetaPositionExprEnv *env)
2667
if (!meta_parse_position_expression (spec, env, NULL, &retval, &error))
2669
meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
2672
g_error_free (error);
2679
parse_size_unchecked (MetaDrawSpec *spec,
2680
MetaPositionExprEnv *env)
2687
if (!meta_parse_size_expression (spec, env, &retval, &error))
2689
meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
2692
g_error_free (error);
2699
meta_draw_spec_free (MetaDrawSpec *spec)
2702
free_tokens (spec->tokens, spec->n_tokens);
2703
g_slice_free (MetaDrawSpec, spec);
2707
meta_draw_spec_new (MetaTheme *theme,
2713
spec = g_slice_new0 (MetaDrawSpec);
2715
pos_tokenize (expr, &spec->tokens, &spec->n_tokens, NULL);
2717
spec->constant = meta_theme_replace_constants (theme, spec->tokens,
2718
spec->n_tokens, NULL);
2723
result = pos_eval (spec, NULL, &spec->value, error);
2724
if (result == FALSE)
2726
meta_draw_spec_free (spec);
2735
meta_draw_op_new (MetaDrawType type)
2741
size = G_STRUCT_OFFSET (MetaDrawOp, data);
2745
case META_DRAW_LINE:
2746
size += sizeof (dummy.data.line);
2749
case META_DRAW_RECTANGLE:
2750
size += sizeof (dummy.data.rectangle);
2754
size += sizeof (dummy.data.arc);
2757
case META_DRAW_CLIP:
2758
size += sizeof (dummy.data.clip);
2761
case META_DRAW_TINT:
2762
size += sizeof (dummy.data.tint);
2765
case META_DRAW_GRADIENT:
2766
size += sizeof (dummy.data.gradient);
2769
case META_DRAW_IMAGE:
2770
size += sizeof (dummy.data.image);
2773
case META_DRAW_GTK_ARROW:
2774
size += sizeof (dummy.data.gtk_arrow);
2777
case META_DRAW_GTK_BOX:
2778
size += sizeof (dummy.data.gtk_box);
2781
case META_DRAW_GTK_VLINE:
2782
size += sizeof (dummy.data.gtk_vline);
2785
case META_DRAW_ICON:
2786
size += sizeof (dummy.data.icon);
2789
case META_DRAW_TITLE:
2790
size += sizeof (dummy.data.title);
2792
case META_DRAW_OP_LIST:
2793
size += sizeof (dummy.data.op_list);
2795
case META_DRAW_TILE:
2796
size += sizeof (dummy.data.tile);
2800
op = g_malloc0 (size);
2808
meta_draw_op_free (MetaDrawOp *op)
2810
g_return_if_fail (op != NULL);
2814
case META_DRAW_LINE:
2815
if (op->data.line.color_spec)
2816
meta_color_spec_free (op->data.line.color_spec);
2818
meta_draw_spec_free (op->data.line.x1);
2819
meta_draw_spec_free (op->data.line.y1);
2820
meta_draw_spec_free (op->data.line.x2);
2821
meta_draw_spec_free (op->data.line.y2);
2824
case META_DRAW_RECTANGLE:
2825
if (op->data.rectangle.color_spec)
2826
g_free (op->data.rectangle.color_spec);
2828
meta_draw_spec_free (op->data.rectangle.x);
2829
meta_draw_spec_free (op->data.rectangle.y);
2830
meta_draw_spec_free (op->data.rectangle.width);
2831
meta_draw_spec_free (op->data.rectangle.height);
2835
if (op->data.arc.color_spec)
2836
g_free (op->data.arc.color_spec);
2838
meta_draw_spec_free (op->data.arc.x);
2839
meta_draw_spec_free (op->data.arc.y);
2840
meta_draw_spec_free (op->data.arc.width);
2841
meta_draw_spec_free (op->data.arc.height);
2844
case META_DRAW_CLIP:
2845
meta_draw_spec_free (op->data.clip.x);
2846
meta_draw_spec_free (op->data.clip.y);
2847
meta_draw_spec_free (op->data.clip.width);
2848
meta_draw_spec_free (op->data.clip.height);
2851
case META_DRAW_TINT:
2852
if (op->data.tint.color_spec)
2853
meta_color_spec_free (op->data.tint.color_spec);
2855
if (op->data.tint.alpha_spec)
2856
meta_alpha_gradient_spec_free (op->data.tint.alpha_spec);
2858
meta_draw_spec_free (op->data.tint.x);
2859
meta_draw_spec_free (op->data.tint.y);
2860
meta_draw_spec_free (op->data.tint.width);
2861
meta_draw_spec_free (op->data.tint.height);
2864
case META_DRAW_GRADIENT:
2865
if (op->data.gradient.gradient_spec)
2866
meta_gradient_spec_free (op->data.gradient.gradient_spec);
2868
if (op->data.gradient.alpha_spec)
2869
meta_alpha_gradient_spec_free (op->data.gradient.alpha_spec);
2871
meta_draw_spec_free (op->data.gradient.x);
2872
meta_draw_spec_free (op->data.gradient.y);
2873
meta_draw_spec_free (op->data.gradient.width);
2874
meta_draw_spec_free (op->data.gradient.height);
2877
case META_DRAW_IMAGE:
2878
if (op->data.image.alpha_spec)
2879
meta_alpha_gradient_spec_free (op->data.image.alpha_spec);
2881
if (op->data.image.pixbuf)
2882
g_object_unref (G_OBJECT (op->data.image.pixbuf));
2884
if (op->data.image.colorize_spec)
2885
meta_color_spec_free (op->data.image.colorize_spec);
2887
if (op->data.image.colorize_cache_pixbuf)
2888
g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
2890
meta_draw_spec_free (op->data.image.x);
2891
meta_draw_spec_free (op->data.image.y);
2892
meta_draw_spec_free (op->data.image.width);
2893
meta_draw_spec_free (op->data.image.height);
2896
case META_DRAW_GTK_ARROW:
2897
meta_draw_spec_free (op->data.gtk_arrow.x);
2898
meta_draw_spec_free (op->data.gtk_arrow.y);
2899
meta_draw_spec_free (op->data.gtk_arrow.width);
2900
meta_draw_spec_free (op->data.gtk_arrow.height);
2903
case META_DRAW_GTK_BOX:
2904
meta_draw_spec_free (op->data.gtk_box.x);
2905
meta_draw_spec_free (op->data.gtk_box.y);
2906
meta_draw_spec_free (op->data.gtk_box.width);
2907
meta_draw_spec_free (op->data.gtk_box.height);
2910
case META_DRAW_GTK_VLINE:
2911
meta_draw_spec_free (op->data.gtk_vline.x);
2912
meta_draw_spec_free (op->data.gtk_vline.y1);
2913
meta_draw_spec_free (op->data.gtk_vline.y2);
2916
case META_DRAW_ICON:
2917
if (op->data.icon.alpha_spec)
2918
meta_alpha_gradient_spec_free (op->data.icon.alpha_spec);
2920
meta_draw_spec_free (op->data.icon.x);
2921
meta_draw_spec_free (op->data.icon.y);
2922
meta_draw_spec_free (op->data.icon.width);
2923
meta_draw_spec_free (op->data.icon.height);
2926
case META_DRAW_TITLE:
2927
if (op->data.title.color_spec)
2928
meta_color_spec_free (op->data.title.color_spec);
2930
meta_draw_spec_free (op->data.title.x);
2931
meta_draw_spec_free (op->data.title.y);
2934
case META_DRAW_OP_LIST:
2935
if (op->data.op_list.op_list)
2936
meta_draw_op_list_unref (op->data.op_list.op_list);
2938
meta_draw_spec_free (op->data.op_list.x);
2939
meta_draw_spec_free (op->data.op_list.y);
2940
meta_draw_spec_free (op->data.op_list.width);
2941
meta_draw_spec_free (op->data.op_list.height);
2944
case META_DRAW_TILE:
2945
if (op->data.tile.op_list)
2946
meta_draw_op_list_unref (op->data.tile.op_list);
2948
meta_draw_spec_free (op->data.tile.x);
2949
meta_draw_spec_free (op->data.tile.y);
2950
meta_draw_spec_free (op->data.tile.width);
2951
meta_draw_spec_free (op->data.tile.height);
2952
meta_draw_spec_free (op->data.tile.tile_xoffset);
2953
meta_draw_spec_free (op->data.tile.tile_yoffset);
2954
meta_draw_spec_free (op->data.tile.tile_width);
2955
meta_draw_spec_free (op->data.tile.tile_height);
2963
apply_alpha (GdkPixbuf *pixbuf,
2964
MetaAlphaGradientSpec *spec,
2965
gboolean force_copy)
2967
GdkPixbuf *new_pixbuf;
2968
gboolean needs_alpha;
2970
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
2972
needs_alpha = spec && (spec->n_alphas > 1 ||
2973
spec->alphas[0] != 0xff);
2978
if (!gdk_pixbuf_get_has_alpha (pixbuf))
2980
new_pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
2981
g_object_unref (G_OBJECT (pixbuf));
2982
pixbuf = new_pixbuf;
2984
else if (force_copy)
2986
new_pixbuf = gdk_pixbuf_copy (pixbuf);
2987
g_object_unref (G_OBJECT (pixbuf));
2988
pixbuf = new_pixbuf;
2991
g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
2993
meta_gradient_add_alpha (pixbuf, spec->alphas, spec->n_alphas, spec->type);
2999
pixbuf_tile (GdkPixbuf *tile,
3008
tile_width = gdk_pixbuf_get_width (tile);
3009
tile_height = gdk_pixbuf_get_height (tile);
3011
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
3012
gdk_pixbuf_get_has_alpha (tile),
3023
w = MIN (tile_width, width - i);
3024
h = MIN (tile_height, height - j);
3026
gdk_pixbuf_copy_area (tile,
3042
replicate_rows (GdkPixbuf *src,
3048
unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
3049
unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
3050
unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
3052
unsigned char *dest_pixels;
3054
unsigned int dest_rowstride;
3057
result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
3059
dest_rowstride = gdk_pixbuf_get_rowstride (result);
3060
dest_pixels = gdk_pixbuf_get_pixels (result);
3062
for (i = 0; i < height; i++)
3063
memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
3069
replicate_cols (GdkPixbuf *src,
3075
unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
3076
unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
3077
unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
3079
unsigned char *dest_pixels;
3081
unsigned int dest_rowstride;
3084
result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
3086
dest_rowstride = gdk_pixbuf_get_rowstride (result);
3087
dest_pixels = gdk_pixbuf_get_pixels (result);
3089
for (i = 0; i < height; i++)
3091
unsigned char *p = dest_pixels + dest_rowstride * i;
3092
unsigned char *q = pixels + src_rowstride * i;
3094
unsigned char r = *(q++);
3095
unsigned char g = *(q++);
3096
unsigned char b = *(q++);
3098
if (n_channels == 4)
3104
for (j = 0; j < width; j++)
3114
for (j = 0; j < width; j++)
3127
scale_and_alpha_pixbuf (GdkPixbuf *src,
3128
MetaAlphaGradientSpec *alpha_spec,
3129
MetaImageFillType fill_type,
3132
gboolean vertical_stripes,
3133
gboolean horizontal_stripes)
3136
GdkPixbuf *temp_pixbuf;
3142
if (gdk_pixbuf_get_width (pixbuf) == width &&
3143
gdk_pixbuf_get_height (pixbuf) == height)
3145
g_object_ref (G_OBJECT (pixbuf));
3149
if (fill_type == META_IMAGE_FILL_TILE)
3151
pixbuf = pixbuf_tile (pixbuf, width, height);
3155
int src_h, src_w, dest_h, dest_w;
3156
src_h = gdk_pixbuf_get_height (src);
3157
src_w = gdk_pixbuf_get_width (src);
3159
/* prefer to replicate_cols if possible, as that
3160
* is faster (no memory reads)
3162
if (horizontal_stripes)
3164
dest_w = gdk_pixbuf_get_width (src);
3167
else if (vertical_stripes)
3170
dest_h = gdk_pixbuf_get_height (src);
3179
if (dest_w == src_w && dest_h == src_h)
3182
g_object_ref (G_OBJECT (temp_pixbuf));
3186
temp_pixbuf = gdk_pixbuf_scale_simple (src,
3188
GDK_INTERP_BILINEAR);
3191
/* prefer to replicate_cols if possible, as that
3192
* is faster (no memory reads)
3194
if (horizontal_stripes)
3196
pixbuf = replicate_cols (temp_pixbuf, 0, 0, width, height);
3197
g_object_unref (G_OBJECT (temp_pixbuf));
3199
else if (vertical_stripes)
3201
pixbuf = replicate_rows (temp_pixbuf, 0, 0, width, height);
3202
g_object_unref (G_OBJECT (temp_pixbuf));
3206
pixbuf = temp_pixbuf;
3212
pixbuf = apply_alpha (pixbuf, alpha_spec, pixbuf == src);
3218
draw_op_as_pixbuf (const MetaDrawOp *op,
3220
const MetaDrawInfo *info,
3224
/* Try to get the op as a pixbuf, assuming w/h in the op
3225
* matches the width/height passed in. return NULL
3226
* if the op can't be converted to an equivalent pixbuf.
3234
case META_DRAW_LINE:
3237
case META_DRAW_RECTANGLE:
3238
if (op->data.rectangle.filled)
3242
meta_color_spec_render (op->data.rectangle.color_spec,
3246
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
3250
gdk_pixbuf_fill (pixbuf, GDK_COLOR_RGBA (color));
3257
case META_DRAW_CLIP:
3260
case META_DRAW_TINT:
3266
meta_color_spec_render (op->data.rectangle.color_spec,
3271
op->data.tint.alpha_spec &&
3272
(op->data.tint.alpha_spec->n_alphas > 1 ||
3273
op->data.tint.alpha_spec->alphas[0] != 0xff);
3275
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
3281
rgba = GDK_COLOR_RGBA (color);
3283
gdk_pixbuf_fill (pixbuf, rgba);
3285
else if (op->data.tint.alpha_spec->n_alphas == 1)
3287
rgba = GDK_COLOR_RGBA (color);
3289
rgba |= op->data.tint.alpha_spec->alphas[0];
3291
gdk_pixbuf_fill (pixbuf, rgba);
3295
rgba = GDK_COLOR_RGBA (color);
3297
gdk_pixbuf_fill (pixbuf, rgba);
3299
meta_gradient_add_alpha (pixbuf,
3300
op->data.tint.alpha_spec->alphas,
3301
op->data.tint.alpha_spec->n_alphas,
3302
op->data.tint.alpha_spec->type);
3307
case META_DRAW_GRADIENT:
3309
pixbuf = meta_gradient_spec_render (op->data.gradient.gradient_spec,
3310
widget, width, height);
3312
pixbuf = apply_alpha (pixbuf,
3313
op->data.gradient.alpha_spec,
3319
case META_DRAW_IMAGE:
3321
if (op->data.image.colorize_spec)
3325
meta_color_spec_render (op->data.image.colorize_spec,
3328
if (op->data.image.colorize_cache_pixbuf == NULL ||
3329
op->data.image.colorize_cache_pixel != GDK_COLOR_RGB (color))
3331
if (op->data.image.colorize_cache_pixbuf)
3332
g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
3334
/* const cast here */
3335
((MetaDrawOp*)op)->data.image.colorize_cache_pixbuf =
3336
colorize_pixbuf (op->data.image.pixbuf,
3338
((MetaDrawOp*)op)->data.image.colorize_cache_pixel =
3339
GDK_COLOR_RGB (color);
3342
if (op->data.image.colorize_cache_pixbuf)
3344
pixbuf = scale_and_alpha_pixbuf (op->data.image.colorize_cache_pixbuf,
3345
op->data.image.alpha_spec,
3346
op->data.image.fill_type,
3348
op->data.image.vertical_stripes,
3349
op->data.image.horizontal_stripes);
3354
pixbuf = scale_and_alpha_pixbuf (op->data.image.pixbuf,
3355
op->data.image.alpha_spec,
3356
op->data.image.fill_type,
3358
op->data.image.vertical_stripes,
3359
op->data.image.horizontal_stripes);
3364
case META_DRAW_GTK_ARROW:
3365
case META_DRAW_GTK_BOX:
3366
case META_DRAW_GTK_VLINE:
3369
case META_DRAW_ICON:
3370
if (info->mini_icon &&
3371
width <= gdk_pixbuf_get_width (info->mini_icon) &&
3372
height <= gdk_pixbuf_get_height (info->mini_icon))
3373
pixbuf = scale_and_alpha_pixbuf (info->mini_icon,
3374
op->data.icon.alpha_spec,
3375
op->data.icon.fill_type,
3378
else if (info->icon)
3379
pixbuf = scale_and_alpha_pixbuf (info->icon,
3380
op->data.icon.alpha_spec,
3381
op->data.icon.fill_type,
3386
case META_DRAW_TITLE:
3389
case META_DRAW_OP_LIST:
3392
case META_DRAW_TILE:
3400
fill_env (MetaPositionExprEnv *env,
3401
const MetaDrawInfo *info,
3402
MetaRectangle logical_region)
3404
/* FIXME this stuff could be raised into draw_op_list_draw() probably
3406
env->rect = logical_region;
3407
env->object_width = -1;
3408
env->object_height = -1;
3411
env->left_width = info->fgeom->left_width;
3412
env->right_width = info->fgeom->right_width;
3413
env->top_height = info->fgeom->top_height;
3414
env->bottom_height = info->fgeom->bottom_height;
3418
env->left_width = 0;
3419
env->right_width = 0;
3420
env->top_height = 0;
3421
env->bottom_height = 0;
3424
env->mini_icon_width = info->mini_icon ? gdk_pixbuf_get_width (info->mini_icon) : 0;
3425
env->mini_icon_height = info->mini_icon ? gdk_pixbuf_get_height (info->mini_icon) : 0;
3426
env->icon_width = info->icon ? gdk_pixbuf_get_width (info->icon) : 0;
3427
env->icon_height = info->icon ? gdk_pixbuf_get_height (info->icon) : 0;
3429
env->title_width = info->title_layout_width;
3430
env->title_height = info->title_layout_height;
3431
env->theme = meta_current_theme;
3434
/* This code was originally rendering anti-aliased using X primitives, and
3435
* now has been switched to draw anti-aliased using cairo. In general, the
3436
* closest correspondence between X rendering and cairo rendering is given
3437
* by offsetting the geometry by 0.5 pixels in both directions before rendering
3438
* with cairo. This is because X samples at the upper left corner of the
3439
* pixel while cairo averages over the entire pixel. However, in the cases
3440
* where the X rendering was an exact rectangle with no "jaggies"
3441
* we need to be a bit careful about applying the offset. We want to produce
3442
* the exact same pixel-aligned rectangle, rather than a rectangle with
3443
* fuzz around the edges.
3446
meta_draw_op_draw_with_env (const MetaDrawOp *op,
3447
GtkStyle *style_gtk,
3449
GdkDrawable *drawable,
3450
const GdkRectangle *clip,
3451
const MetaDrawInfo *info,
3453
MetaPositionExprEnv *env)
3458
cr = gdk_cairo_create (drawable);
3460
cairo_set_line_width (cr, 1.0);
3464
gdk_cairo_rectangle (cr, clip);
3470
case META_DRAW_LINE:
3474
meta_color_spec_render (op->data.line.color_spec, widget, &color);
3475
gdk_cairo_set_source_color (cr, &color);
3477
if (op->data.line.width > 0)
3478
cairo_set_line_width (cr, op->data.line.width);
3480
if (op->data.line.dash_on_length > 0 &&
3481
op->data.line.dash_off_length > 0)
3483
double dash_list[2];
3484
dash_list[0] = op->data.line.dash_on_length;
3485
dash_list[1] = op->data.line.dash_off_length;
3486
cairo_set_dash (cr, dash_list, 2, 0);
3489
x1 = parse_x_position_unchecked (op->data.line.x1, env);
3490
y1 = parse_y_position_unchecked (op->data.line.y1, env);
3492
if (!op->data.line.x2 &&
3493
!op->data.line.y2 &&
3494
op->data.line.width==0)
3496
cairo_rectangle (cr, x1, y1, 1, 1);
3501
if (op->data.line.x2)
3502
x2 = parse_x_position_unchecked (op->data.line.x2, env);
3506
if (op->data.line.y2)
3507
y2 = parse_y_position_unchecked (op->data.line.y2, env);
3511
/* This is one of the cases where we are matching the exact
3512
* pixel aligned rectangle produced by X.
3514
if (y1 == y2 || x1 == x2)
3516
double offset = (op->data.line.width == 0 ||
3517
op->data.line.width % 2) ? .5 : 0;
3518
/* X includes end points for lines of width 0 */
3519
double line_extend = op->data.line.width == 0 ? 1. : 0.;
3529
cairo_move_to (cr, x1, y1 + offset);
3530
cairo_line_to (cr, x2 + line_extend, y2 + offset);
3540
cairo_move_to (cr, x1 + offset, y1);
3541
cairo_line_to (cr, x2 + offset, y2 + line_extend);
3546
if (op->data.line.width <= 0)
3548
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3550
cairo_move_to (cr, x1 + .5, y1 + .5);
3551
cairo_line_to (cr, x2 + .5, y2 + .5);
3558
case META_DRAW_RECTANGLE:
3560
int rx, ry, rwidth, rheight;
3562
meta_color_spec_render (op->data.rectangle.color_spec, widget, &color);
3563
gdk_cairo_set_source_color (cr, &color);
3565
rx = parse_x_position_unchecked (op->data.rectangle.x, env);
3566
ry = parse_y_position_unchecked (op->data.rectangle.y, env);
3567
rwidth = parse_size_unchecked (op->data.rectangle.width, env);
3568
rheight = parse_size_unchecked (op->data.rectangle.height, env);
3570
/* Filled and stroked rectangles are the other cases
3571
* we pixel-align to X rasterization
3573
if (op->data.rectangle.filled)
3575
cairo_rectangle (cr, rx, ry, rwidth, rheight);
3580
cairo_rectangle (cr, rx + .5, ry + .5, rwidth, rheight);
3588
int rx, ry, rwidth, rheight;
3589
double start_angle, end_angle;
3590
double center_x, center_y;
3592
meta_color_spec_render (op->data.arc.color_spec, widget, &color);
3593
gdk_cairo_set_source_color (cr, &color);
3595
rx = parse_x_position_unchecked (op->data.arc.x, env);
3596
ry = parse_y_position_unchecked (op->data.arc.y, env);
3597
rwidth = parse_size_unchecked (op->data.arc.width, env);
3598
rheight = parse_size_unchecked (op->data.arc.height, env);
3600
start_angle = op->data.arc.start_angle * (M_PI / 180.)
3601
- (.25 * M_PI); /* start at 12 instead of 3 oclock */
3602
end_angle = start_angle + op->data.arc.extent_angle * (M_PI / 180.);
3603
center_x = rx + (double)rwidth / 2. + .5;
3604
center_y = ry + (double)rheight / 2. + .5;
3608
cairo_translate (cr, center_x, center_y);
3609
cairo_scale (cr, (double)rwidth / 2., (double)rheight / 2.);
3611
if (op->data.arc.extent_angle >= 0)
3612
cairo_arc (cr, 0, 0, 1, start_angle, end_angle);
3614
cairo_arc_negative (cr, 0, 0, 1, start_angle, end_angle);
3618
if (op->data.arc.filled)
3620
cairo_line_to (cr, center_x, center_y);
3628
case META_DRAW_CLIP:
3631
case META_DRAW_TINT:
3633
int rx, ry, rwidth, rheight;
3634
gboolean needs_alpha;
3636
needs_alpha = op->data.tint.alpha_spec &&
3637
(op->data.tint.alpha_spec->n_alphas > 1 ||
3638
op->data.tint.alpha_spec->alphas[0] != 0xff);
3640
rx = parse_x_position_unchecked (op->data.tint.x, env);
3641
ry = parse_y_position_unchecked (op->data.tint.y, env);
3642
rwidth = parse_size_unchecked (op->data.tint.width, env);
3643
rheight = parse_size_unchecked (op->data.tint.height, env);
3647
meta_color_spec_render (op->data.tint.color_spec, widget, &color);
3648
gdk_cairo_set_source_color (cr, &color);
3650
cairo_rectangle (cr, rx, ry, rwidth, rheight);
3657
pixbuf = draw_op_as_pixbuf (op, widget, info,
3662
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
3665
g_object_unref (G_OBJECT (pixbuf));
3671
case META_DRAW_GRADIENT:
3673
int rx, ry, rwidth, rheight;
3676
rx = parse_x_position_unchecked (op->data.gradient.x, env);
3677
ry = parse_y_position_unchecked (op->data.gradient.y, env);
3678
rwidth = parse_size_unchecked (op->data.gradient.width, env);
3679
rheight = parse_size_unchecked (op->data.gradient.height, env);
3681
pixbuf = draw_op_as_pixbuf (op, widget, info,
3686
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
3689
g_object_unref (G_OBJECT (pixbuf));
3694
case META_DRAW_IMAGE:
3696
int rx, ry, rwidth, rheight;
3699
if (op->data.image.pixbuf)
3701
env->object_width = gdk_pixbuf_get_width (op->data.image.pixbuf);
3702
env->object_height = gdk_pixbuf_get_height (op->data.image.pixbuf);
3705
rwidth = parse_size_unchecked (op->data.image.width, env);
3706
rheight = parse_size_unchecked (op->data.image.height, env);
3708
pixbuf = draw_op_as_pixbuf (op, widget, info,
3713
rx = parse_x_position_unchecked (op->data.image.x, env);
3714
ry = parse_y_position_unchecked (op->data.image.y, env);
3716
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
3719
g_object_unref (G_OBJECT (pixbuf));
3724
case META_DRAW_GTK_ARROW:
3726
int rx, ry, rwidth, rheight;
3728
rx = parse_x_position_unchecked (op->data.gtk_arrow.x, env);
3729
ry = parse_y_position_unchecked (op->data.gtk_arrow.y, env);
3730
rwidth = parse_size_unchecked (op->data.gtk_arrow.width, env);
3731
rheight = parse_size_unchecked (op->data.gtk_arrow.height, env);
3733
gtk_paint_arrow (style_gtk,
3735
op->data.gtk_arrow.state,
3736
op->data.gtk_arrow.shadow,
3737
(GdkRectangle*) clip,
3740
op->data.gtk_arrow.arrow,
3741
op->data.gtk_arrow.filled,
3742
rx, ry, rwidth, rheight);
3746
case META_DRAW_GTK_BOX:
3748
int rx, ry, rwidth, rheight;
3750
rx = parse_x_position_unchecked (op->data.gtk_box.x, env);
3751
ry = parse_y_position_unchecked (op->data.gtk_box.y, env);
3752
rwidth = parse_size_unchecked (op->data.gtk_box.width, env);
3753
rheight = parse_size_unchecked (op->data.gtk_box.height, env);
3755
gtk_paint_box (style_gtk,
3757
op->data.gtk_box.state,
3758
op->data.gtk_box.shadow,
3759
(GdkRectangle*) clip,
3762
rx, ry, rwidth, rheight);
3766
case META_DRAW_GTK_VLINE:
3770
rx = parse_x_position_unchecked (op->data.gtk_vline.x, env);
3771
ry1 = parse_y_position_unchecked (op->data.gtk_vline.y1, env);
3772
ry2 = parse_y_position_unchecked (op->data.gtk_vline.y2, env);
3774
gtk_paint_vline (style_gtk,
3776
op->data.gtk_vline.state,
3777
(GdkRectangle*) clip,
3784
case META_DRAW_ICON:
3786
int rx, ry, rwidth, rheight;
3789
rwidth = parse_size_unchecked (op->data.icon.width, env);
3790
rheight = parse_size_unchecked (op->data.icon.height, env);
3792
pixbuf = draw_op_as_pixbuf (op, widget, info,
3797
rx = parse_x_position_unchecked (op->data.icon.x, env);
3798
ry = parse_y_position_unchecked (op->data.icon.y, env);
3800
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
3803
g_object_unref (G_OBJECT (pixbuf));
3808
case META_DRAW_TITLE:
3809
if (info->title_layout)
3813
meta_color_spec_render (op->data.title.color_spec, widget, &color);
3814
gdk_cairo_set_source_color (cr, &color);
3816
rx = parse_x_position_unchecked (op->data.title.x, env);
3817
ry = parse_y_position_unchecked (op->data.title.y, env);
3819
cairo_move_to (cr, rx, ry);
3820
pango_cairo_show_layout (cr, info->title_layout);
3824
case META_DRAW_OP_LIST:
3826
MetaRectangle d_rect;
3828
d_rect.x = parse_x_position_unchecked (op->data.op_list.x, env);
3829
d_rect.y = parse_y_position_unchecked (op->data.op_list.y, env);
3830
d_rect.width = parse_size_unchecked (op->data.op_list.width, env);
3831
d_rect.height = parse_size_unchecked (op->data.op_list.height, env);
3833
meta_draw_op_list_draw_with_style (op->data.op_list.op_list,
3834
style_gtk, widget, drawable, clip, info,
3839
case META_DRAW_TILE:
3841
int rx, ry, rwidth, rheight;
3842
int tile_xoffset, tile_yoffset;
3843
GdkRectangle new_clip;
3846
rx = parse_x_position_unchecked (op->data.tile.x, env);
3847
ry = parse_y_position_unchecked (op->data.tile.y, env);
3848
rwidth = parse_size_unchecked (op->data.tile.width, env);
3849
rheight = parse_size_unchecked (op->data.tile.height, env);
3853
new_clip.width = rwidth;
3854
new_clip.height = rheight;
3856
if (clip == NULL || gdk_rectangle_intersect ((GdkRectangle*)clip, &new_clip,
3859
tile_xoffset = parse_x_position_unchecked (op->data.tile.tile_xoffset, env);
3860
tile_yoffset = parse_y_position_unchecked (op->data.tile.tile_yoffset, env);
3861
/* tile offset should not include x/y */
3862
tile_xoffset -= rect.x;
3863
tile_yoffset -= rect.y;
3865
tile.width = parse_size_unchecked (op->data.tile.tile_width, env);
3866
tile.height = parse_size_unchecked (op->data.tile.tile_height, env);
3868
tile.x = rx - tile_xoffset;
3870
while (tile.x < (rx + rwidth))
3872
tile.y = ry - tile_yoffset;
3873
while (tile.y < (ry + rheight))
3875
meta_draw_op_list_draw_with_style (op->data.tile.op_list,
3876
style_gtk, widget, drawable, &new_clip, info,
3879
tile.y += tile.height;
3882
tile.x += tile.width;
3893
meta_draw_op_draw_with_style (const MetaDrawOp *op,
3894
GtkStyle *style_gtk,
3896
GdkDrawable *drawable,
3897
const GdkRectangle *clip,
3898
const MetaDrawInfo *info,
3899
MetaRectangle logical_region)
3901
MetaPositionExprEnv env;
3903
g_return_if_fail (style_gtk->colormap == gdk_drawable_get_colormap (drawable));
3905
fill_env (&env, info, logical_region);
3907
meta_draw_op_draw_with_env (op, style_gtk, widget, drawable, clip,
3908
info, logical_region,
3914
meta_draw_op_draw (const MetaDrawOp *op,
3916
GdkDrawable *drawable,
3917
const GdkRectangle *clip,
3918
const MetaDrawInfo *info,
3919
MetaRectangle logical_region)
3921
meta_draw_op_draw_with_style (op, gtk_widget_get_style (widget), widget,
3922
drawable, clip, info, logical_region);
3926
meta_draw_op_list_new (int n_preallocs)
3928
MetaDrawOpList *op_list;
3930
g_return_val_if_fail (n_preallocs >= 0, NULL);
3932
op_list = g_new (MetaDrawOpList, 1);
3934
op_list->refcount = 1;
3935
op_list->n_allocated = n_preallocs;
3936
op_list->ops = g_new (MetaDrawOp*, op_list->n_allocated);
3943
meta_draw_op_list_ref (MetaDrawOpList *op_list)
3945
g_return_if_fail (op_list != NULL);
3947
op_list->refcount += 1;
3951
meta_draw_op_list_unref (MetaDrawOpList *op_list)
3953
g_return_if_fail (op_list != NULL);
3954
g_return_if_fail (op_list->refcount > 0);
3956
op_list->refcount -= 1;
3958
if (op_list->refcount == 0)
3962
for (i = 0; i < op_list->n_ops; i++)
3963
meta_draw_op_free (op_list->ops[i]);
3965
g_free (op_list->ops);
3967
DEBUG_FILL_STRUCT (op_list);
3973
meta_draw_op_list_draw_with_style (const MetaDrawOpList *op_list,
3974
GtkStyle *style_gtk,
3976
GdkDrawable *drawable,
3977
const GdkRectangle *clip,
3978
const MetaDrawInfo *info,
3982
GdkRectangle active_clip;
3983
GdkRectangle orig_clip;
3984
MetaPositionExprEnv env;
3986
g_return_if_fail (style_gtk->colormap == gdk_drawable_get_colormap (drawable));
3988
if (op_list->n_ops == 0)
3991
fill_env (&env, info, rect);
3993
/* FIXME this can be optimized, potentially a lot, by
3994
* compressing multiple ops when possible. For example,
3995
* anything convertible to a pixbuf can be composited
3996
* client-side, and putting a color tint over a pixbuf
3997
* can be done without creating the solid-color pixbuf.
3999
* To implement this my plan is to have the idea of a
4000
* compiled draw op (with the string expressions already
4001
* evaluated), we make an array of those, and then fold
4002
* adjacent items when possible.
4010
orig_clip.x = rect.x;
4011
orig_clip.y = rect.y;
4012
orig_clip.width = rect.width;
4013
orig_clip.height = rect.height;
4016
active_clip = orig_clip;
4018
for (i = 0; i < op_list->n_ops; i++)
4020
MetaDrawOp *op = op_list->ops[i];
4022
if (op->type == META_DRAW_CLIP)
4024
active_clip.x = parse_x_position_unchecked (op->data.clip.x, &env);
4025
active_clip.y = parse_y_position_unchecked (op->data.clip.y, &env);
4026
active_clip.width = parse_size_unchecked (op->data.clip.width, &env);
4027
active_clip.height = parse_size_unchecked (op->data.clip.height, &env);
4029
gdk_rectangle_intersect (&orig_clip, &active_clip, &active_clip);
4031
else if (active_clip.width > 0 &&
4032
active_clip.height > 0)
4034
meta_draw_op_draw_with_env (op,
4035
style_gtk, widget, drawable, &active_clip, info,
4043
meta_draw_op_list_draw (const MetaDrawOpList *op_list,
4045
GdkDrawable *drawable,
4046
const GdkRectangle *clip,
4047
const MetaDrawInfo *info,
4051
meta_draw_op_list_draw_with_style (op_list, gtk_widget_get_style (widget), widget,
4052
drawable, clip, info, rect);
4056
meta_draw_op_list_append (MetaDrawOpList *op_list,
4059
if (op_list->n_ops == op_list->n_allocated)
4061
op_list->n_allocated *= 2;
4062
op_list->ops = g_renew (MetaDrawOp*, op_list->ops, op_list->n_allocated);
4065
op_list->ops[op_list->n_ops] = op;
4066
op_list->n_ops += 1;
4070
meta_draw_op_list_validate (MetaDrawOpList *op_list,
4073
g_return_val_if_fail (op_list != NULL, FALSE);
4075
/* empty lists are OK, nothing else to check really */
4080
/* This is not done in validate, since we wouldn't know the name
4081
* of the list to report the error. It might be nice to
4082
* store names inside the list sometime.
4085
meta_draw_op_list_contains (MetaDrawOpList *op_list,
4086
MetaDrawOpList *child)
4090
/* mmm, huge tree recursion */
4092
for (i = 0; i < op_list->n_ops; i++)
4094
if (op_list->ops[i]->type == META_DRAW_OP_LIST)
4096
if (op_list->ops[i]->data.op_list.op_list == child)
4099
if (meta_draw_op_list_contains (op_list->ops[i]->data.op_list.op_list,
4103
else if (op_list->ops[i]->type == META_DRAW_TILE)
4105
if (op_list->ops[i]->data.tile.op_list == child)
4108
if (meta_draw_op_list_contains (op_list->ops[i]->data.tile.op_list,
4118
* Constructor for a MetaFrameStyle.
4120
* \param parent The parent style. Data not filled in here will be
4121
* looked for in the parent style, and in its parent
4124
* \return The newly-constructed style.
4127
meta_frame_style_new (MetaFrameStyle *parent)
4129
MetaFrameStyle *style;
4131
style = g_new0 (MetaFrameStyle, 1);
4133
style->refcount = 1;
4135
/* Default alpha is fully opaque */
4136
style->window_background_alpha = 255;
4138
style->parent = parent;
4140
meta_frame_style_ref (parent);
4146
* Increases the reference count of a frame style.
4147
* If the style is NULL, this is a no-op.
4149
* \param style The style.
4152
meta_frame_style_ref (MetaFrameStyle *style)
4154
g_return_if_fail (style != NULL);
4156
style->refcount += 1;
4160
free_button_ops (MetaDrawOpList *op_lists[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST])
4164
for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
4165
for (j = 0; j < META_BUTTON_STATE_LAST; j++)
4167
meta_draw_op_list_unref (op_lists[i][j]);
4171
meta_frame_style_unref (MetaFrameStyle *style)
4173
g_return_if_fail (style != NULL);
4174
g_return_if_fail (style->refcount > 0);
4176
style->refcount -= 1;
4178
if (style->refcount == 0)
4182
free_button_ops (style->buttons);
4184
for (i = 0; i < META_FRAME_PIECE_LAST; i++)
4185
if (style->pieces[i])
4186
meta_draw_op_list_unref (style->pieces[i]);
4189
meta_frame_layout_unref (style->layout);
4191
if (style->window_background_color)
4192
meta_color_spec_free (style->window_background_color);
4194
/* we hold a reference to any parent style */
4196
meta_frame_style_unref (style->parent);
4198
if (style->shadow_properties)
4199
meta_shadow_properties_free (style->shadow_properties);
4201
if (style->invisible_grab_area_properties)
4202
meta_invisible_grab_area_properties_free (style->invisible_grab_area_properties);
4204
DEBUG_FILL_STRUCT (style);
4209
static MetaDrawOpList*
4210
get_button (MetaFrameStyle *style,
4211
MetaButtonType type,
4212
MetaButtonState state)
4214
MetaDrawOpList *op_list;
4215
MetaFrameStyle *parent;
4219
while (parent && op_list == NULL)
4221
op_list = parent->buttons[type][state];
4222
parent = parent->parent;
4225
/* We fall back to middle button backgrounds if we don't
4226
* have the ones on the sides
4229
if (op_list == NULL &&
4230
(type == META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND ||
4231
type == META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND))
4232
return get_button (style, META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND,
4235
if (op_list == NULL &&
4236
(type == META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND ||
4237
type == META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND))
4238
return get_button (style, META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND,
4241
/* We fall back to normal if no prelight */
4242
if (op_list == NULL &&
4243
state == META_BUTTON_STATE_PRELIGHT)
4244
return get_button (style, type, META_BUTTON_STATE_NORMAL);
4250
meta_frame_style_validate (MetaFrameStyle *style,
4251
guint current_theme_version,
4256
g_return_val_if_fail (style != NULL, FALSE);
4257
g_return_val_if_fail (style->layout != NULL, FALSE);
4259
for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
4261
/* for now the "positional" buttons are optional */
4262
if (i >= META_BUTTON_TYPE_CLOSE)
4264
for (j = 0; j < META_BUTTON_STATE_LAST; j++)
4266
if (get_button (style, i, j) == NULL &&
4267
meta_theme_earliest_version_with_button (i) <= current_theme_version
4270
g_set_error (error, META_THEME_ERROR,
4271
META_THEME_ERROR_FAILED,
4272
_("<button function=\"%s\" state=\"%s\" draw_ops=\"whatever\"/> must be specified for this frame style"),
4273
meta_button_type_to_string (i),
4274
meta_button_state_to_string (j));
4285
button_rect (MetaButtonType type,
4286
const MetaFrameGeometry *fgeom,
4287
int middle_background_offset,
4292
case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
4293
*rect = fgeom->left_left_background;
4296
case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
4297
*rect = fgeom->left_middle_backgrounds[middle_background_offset];
4300
case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
4301
*rect = fgeom->left_right_background;
4304
case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
4305
*rect = fgeom->right_left_background;
4308
case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
4309
*rect = fgeom->right_middle_backgrounds[middle_background_offset];
4312
case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
4313
*rect = fgeom->right_right_background;
4316
case META_BUTTON_TYPE_CLOSE:
4317
*rect = fgeom->close_rect.visible;
4320
case META_BUTTON_TYPE_SHADE:
4321
*rect = fgeom->shade_rect.visible;
4324
case META_BUTTON_TYPE_UNSHADE:
4325
*rect = fgeom->unshade_rect.visible;
4328
case META_BUTTON_TYPE_ABOVE:
4329
*rect = fgeom->above_rect.visible;
4332
case META_BUTTON_TYPE_UNABOVE:
4333
*rect = fgeom->unabove_rect.visible;
4336
case META_BUTTON_TYPE_STICK:
4337
*rect = fgeom->stick_rect.visible;
4340
case META_BUTTON_TYPE_UNSTICK:
4341
*rect = fgeom->unstick_rect.visible;
4344
case META_BUTTON_TYPE_MAXIMIZE:
4345
*rect = fgeom->max_rect.visible;
4348
case META_BUTTON_TYPE_MINIMIZE:
4349
*rect = fgeom->min_rect.visible;
4352
case META_BUTTON_TYPE_MENU:
4353
*rect = fgeom->menu_rect.visible;
4356
case META_BUTTON_TYPE_LAST:
4357
g_assert_not_reached ();
4363
meta_frame_style_draw_with_style (MetaFrameStyle *style,
4364
GtkStyle *style_gtk,
4366
GdkDrawable *drawable,
4369
const GdkRectangle *clip,
4370
const MetaFrameGeometry *fgeom,
4373
PangoLayout *title_layout,
4375
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
4376
GdkPixbuf *mini_icon,
4380
GdkRectangle titlebar_rect;
4381
GdkRectangle left_titlebar_edge;
4382
GdkRectangle right_titlebar_edge;
4383
GdkRectangle bottom_titlebar_edge;
4384
GdkRectangle top_titlebar_edge;
4385
GdkRectangle left_edge, right_edge, bottom_edge;
4386
PangoRectangle extents;
4387
MetaDrawInfo draw_info;
4389
g_return_if_fail (style_gtk->colormap == gdk_drawable_get_colormap (drawable));
4391
titlebar_rect.x = 0;
4392
titlebar_rect.y = 0;
4393
titlebar_rect.width = fgeom->width;
4394
titlebar_rect.height = fgeom->top_height;
4396
left_titlebar_edge.x = titlebar_rect.x;
4397
left_titlebar_edge.y = titlebar_rect.y + fgeom->top_titlebar_edge;
4398
left_titlebar_edge.width = fgeom->left_titlebar_edge;
4399
left_titlebar_edge.height = titlebar_rect.height - fgeom->top_titlebar_edge - fgeom->bottom_titlebar_edge;
4401
right_titlebar_edge.y = left_titlebar_edge.y;
4402
right_titlebar_edge.height = left_titlebar_edge.height;
4403
right_titlebar_edge.width = fgeom->right_titlebar_edge;
4404
right_titlebar_edge.x = titlebar_rect.x + titlebar_rect.width - right_titlebar_edge.width;
4406
top_titlebar_edge.x = titlebar_rect.x;
4407
top_titlebar_edge.y = titlebar_rect.y;
4408
top_titlebar_edge.width = titlebar_rect.width;
4409
top_titlebar_edge.height = fgeom->top_titlebar_edge;
4411
bottom_titlebar_edge.x = titlebar_rect.x;
4412
bottom_titlebar_edge.width = titlebar_rect.width;
4413
bottom_titlebar_edge.height = fgeom->bottom_titlebar_edge;
4414
bottom_titlebar_edge.y = titlebar_rect.y + titlebar_rect.height - bottom_titlebar_edge.height;
4417
left_edge.y = fgeom->top_height;
4418
left_edge.width = fgeom->left_width;
4419
left_edge.height = fgeom->height - fgeom->top_height - fgeom->bottom_height;
4421
right_edge.x = fgeom->width - fgeom->right_width;
4422
right_edge.y = fgeom->top_height;
4423
right_edge.width = fgeom->right_width;
4424
right_edge.height = fgeom->height - fgeom->top_height - fgeom->bottom_height;
4427
bottom_edge.y = fgeom->height - fgeom->bottom_height;
4428
bottom_edge.width = fgeom->width;
4429
bottom_edge.height = fgeom->bottom_height;
4432
pango_layout_get_pixel_extents (title_layout,
4435
draw_info.mini_icon = mini_icon;
4436
draw_info.icon = icon;
4437
draw_info.title_layout = title_layout;
4438
draw_info.title_layout_width = title_layout ? extents.width : 0;
4439
draw_info.title_layout_height = title_layout ? extents.height : 0;
4440
draw_info.fgeom = fgeom;
4442
/* The enum is in the order the pieces should be rendered. */
4444
while (i < META_FRAME_PIECE_LAST)
4447
GdkRectangle combined_clip;
4449
switch ((MetaFramePiece) i)
4451
case META_FRAME_PIECE_ENTIRE_BACKGROUND:
4454
rect.width = fgeom->width;
4455
rect.height = fgeom->height;
4458
case META_FRAME_PIECE_TITLEBAR:
4459
rect = titlebar_rect;
4462
case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE:
4463
rect = left_titlebar_edge;
4466
case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE:
4467
rect = right_titlebar_edge;
4470
case META_FRAME_PIECE_TOP_TITLEBAR_EDGE:
4471
rect = top_titlebar_edge;
4474
case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE:
4475
rect = bottom_titlebar_edge;
4478
case META_FRAME_PIECE_TITLEBAR_MIDDLE:
4479
rect.x = left_titlebar_edge.x + left_titlebar_edge.width;
4480
rect.y = top_titlebar_edge.y + top_titlebar_edge.height;
4481
rect.width = titlebar_rect.width - left_titlebar_edge.width -
4482
right_titlebar_edge.width;
4483
rect.height = titlebar_rect.height - top_titlebar_edge.height - bottom_titlebar_edge.height;
4486
case META_FRAME_PIECE_TITLE:
4487
rect = fgeom->title_rect;
4490
case META_FRAME_PIECE_LEFT_EDGE:
4494
case META_FRAME_PIECE_RIGHT_EDGE:
4498
case META_FRAME_PIECE_BOTTOM_EDGE:
4502
case META_FRAME_PIECE_OVERLAY:
4505
rect.width = fgeom->width;
4506
rect.height = fgeom->height;
4509
case META_FRAME_PIECE_LAST:
4510
g_assert_not_reached ();
4518
combined_clip = rect;
4520
gdk_rectangle_intersect ((GdkRectangle*) clip, /* const cast */
4524
if (combined_clip.width > 0 && combined_clip.height > 0)
4526
MetaDrawOpList *op_list;
4527
MetaFrameStyle *parent;
4531
while (parent && op_list == NULL)
4533
op_list = parent->pieces[i];
4534
parent = parent->parent;
4539
MetaRectangle m_rect;
4540
m_rect = meta_rect (rect.x, rect.y, rect.width, rect.height);
4541
meta_draw_op_list_draw_with_style (op_list,
4552
/* Draw buttons just before overlay */
4553
if ((i + 1) == META_FRAME_PIECE_OVERLAY)
4555
int middle_bg_offset;
4557
middle_bg_offset = 0;
4559
while (j < META_BUTTON_TYPE_LAST)
4561
button_rect (j, fgeom, middle_bg_offset, &rect);
4567
combined_clip = rect;
4569
gdk_rectangle_intersect ((GdkRectangle*) clip, /* const cast */
4573
if (combined_clip.width > 0 && combined_clip.height > 0)
4575
MetaDrawOpList *op_list;
4577
op_list = get_button (style, j, button_states[j]);
4581
MetaRectangle m_rect;
4582
m_rect = meta_rect (rect.x, rect.y,
4583
rect.width, rect.height);
4584
meta_draw_op_list_draw_with_style (op_list,
4594
/* MIDDLE_BACKGROUND type may get drawn more than once */
4595
if ((j == META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND ||
4596
j == META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND) &&
4597
(middle_bg_offset < (MAX_MIDDLE_BACKGROUNDS - 1)))
4603
middle_bg_offset = 0;
4614
meta_frame_style_draw (MetaFrameStyle *style,
4616
GdkDrawable *drawable,
4619
const GdkRectangle *clip,
4620
const MetaFrameGeometry *fgeom,
4623
PangoLayout *title_layout,
4625
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
4626
GdkPixbuf *mini_icon,
4629
meta_frame_style_draw_with_style (style, gtk_widget_get_style (widget), widget,
4630
drawable, x_offset, y_offset,
4631
clip, fgeom, client_width, client_height,
4632
title_layout, text_height,
4633
button_states, mini_icon, icon);
4636
MetaShadowProperties *
4637
meta_frame_style_get_shadow_properties (MetaFrameStyle *style)
4639
return style->shadow_properties;
4643
MetaInvisibleGrabAreaProperties * meta_frame_style_get_invisible_grab_area_properties (MetaFrameStyle *style)
4645
return style->invisible_grab_area_properties;
4649
meta_frame_style_set_new (MetaFrameStyleSet *parent)
4651
MetaFrameStyleSet *style_set;
4653
style_set = g_new0 (MetaFrameStyleSet, 1);
4655
style_set->parent = parent;
4657
meta_frame_style_set_ref (parent);
4659
style_set->refcount = 1;
4665
free_focus_styles (MetaFrameStyle *focus_styles[META_FRAME_FOCUS_LAST])
4669
for (i = 0; i < META_FRAME_FOCUS_LAST; i++)
4670
if (focus_styles[i])
4671
meta_frame_style_unref (focus_styles[i]);
4675
meta_frame_style_set_ref (MetaFrameStyleSet *style_set)
4677
g_return_if_fail (style_set != NULL);
4679
style_set->refcount += 1;
4683
meta_frame_style_set_unref (MetaFrameStyleSet *style_set)
4685
g_return_if_fail (style_set != NULL);
4686
g_return_if_fail (style_set->refcount > 0);
4688
style_set->refcount -= 1;
4690
if (style_set->refcount == 0)
4694
for (i = 0; i < META_FRAME_RESIZE_LAST; i++)
4696
free_focus_styles (style_set->normal_styles[i]);
4697
free_focus_styles (style_set->shaded_styles[i]);
4700
free_focus_styles (style_set->maximized_styles);
4701
free_focus_styles (style_set->maximized_and_shaded_styles);
4703
if (style_set->parent)
4704
meta_frame_style_set_unref (style_set->parent);
4706
DEBUG_FILL_STRUCT (style_set);
4712
static MetaFrameStyle*
4713
get_style (MetaFrameStyleSet *style_set,
4714
MetaFrameState state,
4715
MetaFrameResize resize,
4716
MetaFrameFocus focus)
4718
MetaFrameStyle *style;
4724
case META_FRAME_STATE_NORMAL:
4725
case META_FRAME_STATE_SHADED:
4727
if (state == META_FRAME_STATE_SHADED)
4728
style = style_set->shaded_styles[resize][focus];
4730
style = style_set->normal_styles[resize][focus];
4732
/* Try parent if we failed here */
4733
if (style == NULL && style_set->parent)
4734
style = get_style (style_set->parent, state, resize, focus);
4736
/* Allow people to omit the vert/horz/none resize modes */
4737
if (style == NULL &&
4738
resize != META_FRAME_RESIZE_BOTH)
4739
style = get_style (style_set, state, META_FRAME_RESIZE_BOTH, focus);
4744
MetaFrameStyle **styles;
4750
case META_FRAME_STATE_MAXIMIZED:
4751
styles = style_set->maximized_styles;
4753
case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
4754
styles = style_set->maximized_and_shaded_styles;
4756
case META_FRAME_STATE_NORMAL:
4757
case META_FRAME_STATE_SHADED:
4758
case META_FRAME_STATE_LAST:
4759
g_assert_not_reached ();
4763
style = styles[focus];
4765
/* Try parent if we failed here */
4766
if (style == NULL && style_set->parent)
4767
style = get_style (style_set->parent, state, resize, focus);
4775
check_state (MetaFrameStyleSet *style_set,
4776
MetaFrameState state,
4781
for (i = 0; i < META_FRAME_FOCUS_LAST; i++)
4783
if (get_style (style_set, state,
4784
META_FRAME_RESIZE_NONE, i) == NULL)
4786
/* Translators: This error occurs when a <frame> tag is missing
4787
* in theme XML. The "<frame ...>" is intended as a noun phrase,
4788
* and the "missing" qualifies it. You should translate "whatever".
4790
g_set_error (error, META_THEME_ERROR,
4791
META_THEME_ERROR_FAILED,
4792
_("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
4793
meta_frame_state_to_string (state),
4794
meta_frame_resize_to_string (META_FRAME_RESIZE_NONE),
4795
meta_frame_focus_to_string (i));
4804
meta_frame_style_set_validate (MetaFrameStyleSet *style_set,
4809
g_return_val_if_fail (style_set != NULL, FALSE);
4811
for (i = 0; i < META_FRAME_RESIZE_LAST; i++)
4812
for (j = 0; j < META_FRAME_FOCUS_LAST; j++)
4813
if (get_style (style_set, META_FRAME_STATE_NORMAL, i, j) == NULL)
4815
g_set_error (error, META_THEME_ERROR,
4816
META_THEME_ERROR_FAILED,
4817
_("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
4818
meta_frame_state_to_string (META_FRAME_STATE_NORMAL),
4819
meta_frame_resize_to_string (i),
4820
meta_frame_focus_to_string (j));
4824
if (!check_state (style_set, META_FRAME_STATE_SHADED, error))
4827
if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED, error))
4830
if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED_AND_SHADED, error))
4837
meta_theme_get_current (void)
4839
return meta_current_theme;
4843
meta_theme_set_current (const char *name,
4844
gboolean force_reload)
4846
MetaTheme *new_theme;
4849
meta_topic (META_DEBUG_THEMES, "Setting current theme to \"%s\"\n", name);
4851
if (!force_reload &&
4852
meta_current_theme &&
4853
strcmp (name, meta_current_theme->name) == 0)
4857
new_theme = meta_theme_load (name, &err);
4859
if (new_theme == NULL)
4861
meta_warning (_("Failed to load theme \"%s\": %s\n"),
4862
name, err->message);
4867
if (meta_current_theme)
4868
meta_theme_free (meta_current_theme);
4870
meta_current_theme = new_theme;
4872
meta_topic (META_DEBUG_THEMES, "New theme is \"%s\"\n", meta_current_theme->name);
4877
meta_theme_new (void)
4881
theme = g_new0 (MetaTheme, 1);
4883
theme->images_by_filename =
4884
g_hash_table_new_full (g_str_hash,
4887
(GDestroyNotify) g_object_unref);
4889
theme->layouts_by_name =
4890
g_hash_table_new_full (g_str_hash,
4893
(GDestroyNotify) meta_frame_layout_unref);
4895
theme->draw_op_lists_by_name =
4896
g_hash_table_new_full (g_str_hash,
4899
(GDestroyNotify) meta_draw_op_list_unref);
4901
theme->styles_by_name =
4902
g_hash_table_new_full (g_str_hash,
4905
(GDestroyNotify) meta_frame_style_unref);
4907
theme->style_sets_by_name =
4908
g_hash_table_new_full (g_str_hash,
4911
(GDestroyNotify) meta_frame_style_set_unref);
4913
/* Create our variable quarks so we can look up variables without
4914
having to strcmp for the names */
4915
theme->quark_width = g_quark_from_static_string ("width");
4916
theme->quark_height = g_quark_from_static_string ("height");
4917
theme->quark_object_width = g_quark_from_static_string ("object_width");
4918
theme->quark_object_height = g_quark_from_static_string ("object_height");
4919
theme->quark_left_width = g_quark_from_static_string ("left_width");
4920
theme->quark_right_width = g_quark_from_static_string ("right_width");
4921
theme->quark_top_height = g_quark_from_static_string ("top_height");
4922
theme->quark_bottom_height = g_quark_from_static_string ("bottom_height");
4923
theme->quark_mini_icon_width = g_quark_from_static_string ("mini_icon_width");
4924
theme->quark_mini_icon_height = g_quark_from_static_string ("mini_icon_height");
4925
theme->quark_icon_width = g_quark_from_static_string ("icon_width");
4926
theme->quark_icon_height = g_quark_from_static_string ("icon_height");
4927
theme->quark_title_width = g_quark_from_static_string ("title_width");
4928
theme->quark_title_height = g_quark_from_static_string ("title_height");
4934
meta_theme_free (MetaTheme *theme)
4938
g_return_if_fail (theme != NULL);
4940
g_free (theme->name);
4941
g_free (theme->dirname);
4942
g_free (theme->filename);
4943
g_free (theme->readable_name);
4944
g_free (theme->date);
4945
g_free (theme->description);
4946
g_free (theme->author);
4947
g_free (theme->copyright);
4949
/* be more careful when destroying the theme hash tables,
4950
since they are only constructed as needed, and may be NULL. */
4951
if (theme->integer_constants)
4952
g_hash_table_destroy (theme->integer_constants);
4953
if (theme->images_by_filename)
4954
g_hash_table_destroy (theme->images_by_filename);
4955
if (theme->layouts_by_name)
4956
g_hash_table_destroy (theme->layouts_by_name);
4957
if (theme->draw_op_lists_by_name)
4958
g_hash_table_destroy (theme->draw_op_lists_by_name);
4959
if (theme->styles_by_name)
4960
g_hash_table_destroy (theme->styles_by_name);
4961
if (theme->style_sets_by_name)
4962
g_hash_table_destroy (theme->style_sets_by_name);
4964
for (i = 0; i < META_FRAME_TYPE_LAST; i++)
4965
if (theme->style_sets_by_type[i])
4966
meta_frame_style_set_unref (theme->style_sets_by_type[i]);
4968
DEBUG_FILL_STRUCT (theme);
4973
meta_theme_validate (MetaTheme *theme,
4978
g_return_val_if_fail (theme != NULL, FALSE);
4980
/* FIXME what else should be checked? */
4982
g_assert (theme->name);
4984
if (theme->readable_name == NULL)
4986
/* Translators: This error means that a necessary XML tag (whose name
4987
* is given in angle brackets) was not found in a given theme (whose
4988
* name is given second, in quotation marks).
4990
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4991
_("No <%s> set for theme \"%s\""), "name", theme->name);
4995
if (theme->author == NULL)
4997
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4998
_("No <%s> set for theme \"%s\""), "author", theme->name);
5002
if (theme->date == NULL)
5004
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5005
_("No <%s> set for theme \"%s\""), "date", theme->name);
5009
if (theme->description == NULL)
5011
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5012
_("No <%s> set for theme \"%s\""), "description", theme->name);
5016
if (theme->copyright == NULL)
5018
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5019
_("No <%s> set for theme \"%s\""), "copyright", theme->name);
5023
for (i = 0; i < (int)META_FRAME_TYPE_LAST; i++)
5024
if (theme->style_sets_by_type[i] == NULL)
5026
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5027
_("No frame style set for window type \"%s\" in theme \"%s\", add a <window type=\"%s\" style_set=\"whatever\"/> element"),
5028
meta_frame_type_to_string (i),
5030
meta_frame_type_to_string (i));
5039
meta_theme_load_image (MetaTheme *theme,
5040
const char *filename,
5041
guint size_of_theme_icons,
5046
pixbuf = g_hash_table_lookup (theme->images_by_filename,
5052
if (g_str_has_prefix (filename, "theme:") &&
5053
META_THEME_ALLOWS (theme, META_THEME_IMAGES_FROM_ICON_THEMES))
5055
pixbuf = gtk_icon_theme_load_icon (
5056
gtk_icon_theme_get_default (),
5058
size_of_theme_icons,
5061
if (pixbuf == NULL) return NULL;
5066
full_path = g_build_filename (theme->dirname, filename, NULL);
5068
pixbuf = gdk_pixbuf_new_from_file (full_path, error);
5077
g_hash_table_replace (theme->images_by_filename,
5078
g_strdup (filename),
5084
g_object_ref (G_OBJECT (pixbuf));
5089
static MetaFrameStyle*
5090
theme_get_style (MetaTheme *theme,
5092
MetaFrameFlags flags)
5094
MetaFrameState state;
5095
MetaFrameResize resize;
5096
MetaFrameFocus focus;
5097
MetaFrameStyle *style;
5098
MetaFrameStyleSet *style_set;
5100
style_set = theme->style_sets_by_type[type];
5102
/* Right now the parser forces a style set for all types,
5103
* but this fallback code is here in case I take that out.
5105
if (style_set == NULL)
5106
style_set = theme->style_sets_by_type[META_FRAME_TYPE_NORMAL];
5107
if (style_set == NULL)
5110
switch (flags & (META_FRAME_MAXIMIZED | META_FRAME_SHADED))
5113
state = META_FRAME_STATE_NORMAL;
5115
case META_FRAME_MAXIMIZED:
5116
state = META_FRAME_STATE_MAXIMIZED;
5118
case META_FRAME_SHADED:
5119
state = META_FRAME_STATE_SHADED;
5121
case (META_FRAME_MAXIMIZED | META_FRAME_SHADED):
5122
state = META_FRAME_STATE_MAXIMIZED_AND_SHADED;
5125
g_assert_not_reached ();
5126
state = META_FRAME_STATE_LAST; /* compiler */
5130
switch (flags & (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE))
5133
resize = META_FRAME_RESIZE_NONE;
5135
case META_FRAME_ALLOWS_VERTICAL_RESIZE:
5136
resize = META_FRAME_RESIZE_VERTICAL;
5138
case META_FRAME_ALLOWS_HORIZONTAL_RESIZE:
5139
resize = META_FRAME_RESIZE_HORIZONTAL;
5141
case (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE):
5142
resize = META_FRAME_RESIZE_BOTH;
5145
g_assert_not_reached ();
5146
resize = META_FRAME_RESIZE_LAST; /* compiler */
5150
/* re invert the styles used for focus/unfocussed while flashing a frame */
5151
if (((flags & META_FRAME_HAS_FOCUS) && !(flags & META_FRAME_IS_FLASHING))
5152
|| (!(flags & META_FRAME_HAS_FOCUS) && (flags & META_FRAME_IS_FLASHING)))
5153
focus = META_FRAME_FOCUS_YES;
5155
focus = META_FRAME_FOCUS_NO;
5157
style = get_style (style_set, state, resize, focus);
5163
meta_theme_get_frame_style (MetaTheme *theme,
5165
MetaFrameFlags flags)
5167
MetaFrameStyle *style;
5169
g_return_val_if_fail (type < META_FRAME_TYPE_LAST, NULL);
5171
style = theme_get_style (theme, type, flags);
5177
meta_theme_get_title_scale (MetaTheme *theme,
5179
MetaFrameFlags flags)
5181
MetaFrameStyle *style;
5183
g_return_val_if_fail (type < META_FRAME_TYPE_LAST, 1.0);
5185
style = theme_get_style (theme, type, flags);
5187
/* Parser is not supposed to allow this currently */
5191
return style->layout->title_scale;
5195
meta_theme_draw_frame_with_style (MetaTheme *theme,
5196
GtkStyle *style_gtk,
5198
GdkDrawable *drawable,
5199
const GdkRectangle *clip,
5203
MetaFrameFlags flags,
5206
PangoLayout *title_layout,
5208
const MetaButtonLayout *button_layout,
5209
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
5210
GdkPixbuf *mini_icon,
5213
MetaFrameGeometry fgeom;
5214
MetaFrameStyle *style;
5216
g_return_if_fail (type < META_FRAME_TYPE_LAST);
5218
style = theme_get_style (theme, type, flags);
5220
/* Parser is not supposed to allow this currently */
5224
meta_frame_layout_calc_geometry (style->layout,
5227
client_width, client_height,
5232
meta_frame_style_draw_with_style (style,
5239
client_width, client_height,
5247
meta_theme_draw_frame (MetaTheme *theme,
5249
GdkDrawable *drawable,
5250
const GdkRectangle *clip,
5254
MetaFrameFlags flags,
5257
PangoLayout *title_layout,
5259
const MetaButtonLayout *button_layout,
5260
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
5261
GdkPixbuf *mini_icon,
5264
meta_theme_draw_frame_with_style (theme, gtk_widget_get_style (widget), widget,
5265
drawable, clip, x_offset, y_offset, type,flags,
5266
client_width, client_height,
5267
title_layout, text_height,
5268
button_layout, button_states,
5273
meta_theme_draw_frame_by_name (MetaTheme *theme,
5275
GdkDrawable *drawable,
5276
const GdkRectangle *clip,
5279
const gchar *style_name,
5280
MetaFrameFlags flags,
5283
PangoLayout *title_layout,
5285
const MetaButtonLayout *button_layout,
5286
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
5287
GdkPixbuf *mini_icon,
5290
MetaFrameGeometry fgeom;
5291
MetaFrameStyle *style;
5293
style = meta_theme_lookup_style (theme, style_name);
5295
/* Parser is not supposed to allow this currently */
5299
meta_frame_layout_calc_geometry (style->layout,
5302
client_width, client_height,
5307
meta_frame_style_draw (style,
5313
client_width, client_height,
5321
meta_theme_get_frame_borders (MetaTheme *theme,
5324
MetaFrameFlags flags,
5330
MetaFrameStyle *style;
5332
g_return_if_fail (type < META_FRAME_TYPE_LAST);
5343
style = theme_get_style (theme, type, flags);
5345
/* Parser is not supposed to allow this currently */
5349
meta_frame_layout_get_borders (style->layout,
5352
top_height, bottom_height,
5353
left_width, right_width);
5357
meta_theme_calc_geometry (MetaTheme *theme,
5360
MetaFrameFlags flags,
5363
const MetaButtonLayout *button_layout,
5364
MetaFrameGeometry *fgeom)
5366
MetaFrameStyle *style;
5368
g_return_if_fail (type < META_FRAME_TYPE_LAST);
5370
style = theme_get_style (theme, type, flags);
5372
/* Parser is not supposed to allow this currently */
5376
meta_frame_layout_calc_geometry (style->layout,
5379
client_width, client_height,
5386
meta_theme_lookup_layout (MetaTheme *theme,
5389
return g_hash_table_lookup (theme->layouts_by_name, name);
5393
meta_theme_insert_layout (MetaTheme *theme,
5395
MetaFrameLayout *layout)
5397
meta_frame_layout_ref (layout);
5398
g_hash_table_replace (theme->layouts_by_name, g_strdup (name), layout);
5402
meta_theme_lookup_draw_op_list (MetaTheme *theme,
5405
return g_hash_table_lookup (theme->draw_op_lists_by_name, name);
5409
meta_theme_insert_draw_op_list (MetaTheme *theme,
5411
MetaDrawOpList *op_list)
5413
meta_draw_op_list_ref (op_list);
5414
g_hash_table_replace (theme->draw_op_lists_by_name, g_strdup (name), op_list);
5418
meta_theme_lookup_style (MetaTheme *theme,
5421
return g_hash_table_lookup (theme->styles_by_name, name);
5425
meta_theme_insert_style (MetaTheme *theme,
5427
MetaFrameStyle *style)
5429
meta_frame_style_ref (style);
5430
g_hash_table_replace (theme->styles_by_name, g_strdup (name), style);
5434
meta_theme_lookup_style_set (MetaTheme *theme,
5437
return g_hash_table_lookup (theme->style_sets_by_name, name);
5441
meta_theme_insert_style_set (MetaTheme *theme,
5443
MetaFrameStyleSet *style_set)
5445
meta_frame_style_set_ref (style_set);
5446
g_hash_table_replace (theme->style_sets_by_name, g_strdup (name), style_set);
5450
first_uppercase (const char *str)
5452
return g_ascii_isupper (*str);
5456
meta_theme_define_int_constant (MetaTheme *theme,
5461
if (theme->integer_constants == NULL)
5462
theme->integer_constants = g_hash_table_new_full (g_str_hash,
5467
if (!first_uppercase (name))
5469
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5470
_("User-defined constants must begin with a capital letter; \"%s\" does not"),
5475
if (g_hash_table_lookup_extended (theme->integer_constants, name, NULL, NULL))
5477
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5478
_("Constant \"%s\" has already been defined"),
5484
g_hash_table_insert (theme->integer_constants,
5486
GINT_TO_POINTER (value));
5492
meta_theme_lookup_int_constant (MetaTheme *theme,
5500
if (theme->integer_constants == NULL)
5503
if (g_hash_table_lookup_extended (theme->integer_constants,
5504
name, NULL, &old_value))
5506
*value = GPOINTER_TO_INT (old_value);
5516
meta_theme_define_float_constant (MetaTheme *theme,
5523
if (theme->float_constants == NULL)
5524
theme->float_constants = g_hash_table_new_full (g_str_hash,
5529
if (!first_uppercase (name))
5531
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5532
_("User-defined constants must begin with a capital letter; \"%s\" does not"),
5537
if (g_hash_table_lookup_extended (theme->float_constants, name, NULL, NULL))
5539
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5540
_("Constant \"%s\" has already been defined"),
5546
d = g_new (double, 1);
5549
g_hash_table_insert (theme->float_constants,
5550
g_strdup (name), d);
5556
meta_theme_lookup_float_constant (MetaTheme *theme,
5564
if (theme->float_constants == NULL)
5567
d = g_hash_table_lookup (theme->float_constants, name);
5581
meta_theme_define_color_constant (MetaTheme *theme,
5586
if (theme->color_constants == NULL)
5587
theme->color_constants = g_hash_table_new_full (g_str_hash,
5592
if (!first_uppercase (name))
5594
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5595
_("User-defined constants must begin with a capital letter; \"%s\" does not"),
5600
if (g_hash_table_lookup_extended (theme->color_constants, name, NULL, NULL))
5602
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5603
_("Constant \"%s\" has already been defined"),
5609
g_hash_table_insert (theme->color_constants,
5617
* Looks up a colour constant.
5619
* \param theme the theme containing the constant
5620
* \param name the name of the constant
5621
* \param value [out] the string representation of the colour, or NULL if it
5623
* \return TRUE if it exists, FALSE otherwise
5626
meta_theme_lookup_color_constant (MetaTheme *theme,
5634
if (theme->color_constants == NULL)
5637
result = g_hash_table_lookup (theme->color_constants, name);
5651
PangoFontDescription*
5652
meta_gtk_widget_get_font_desc (GtkWidget *widget,
5654
const PangoFontDescription *override)
5656
PangoFontDescription *font_desc;
5658
g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
5660
font_desc = pango_font_description_copy (gtk_widget_get_style (widget)->font_desc);
5663
pango_font_description_merge (font_desc, override, TRUE);
5665
pango_font_description_set_size (font_desc,
5666
MAX (pango_font_description_get_size (font_desc) * scale, 1));
5672
* Returns the height of the letters in a particular font.
5674
* \param font_desc the font
5675
* \param context the context of the font
5676
* \return the height of the letters
5679
meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc,
5680
PangoContext *context)
5682
PangoFontMetrics *metrics;
5683
PangoLanguage *lang;
5686
lang = pango_context_get_language (context);
5687
metrics = pango_context_get_metrics (context, font_desc, lang);
5689
retval = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
5690
pango_font_metrics_get_descent (metrics));
5692
pango_font_metrics_unref (metrics);
5697
MetaGtkColorComponent
5698
meta_color_component_from_string (const char *str)
5700
if (strcmp ("fg", str) == 0)
5701
return META_GTK_COLOR_FG;
5702
else if (strcmp ("bg", str) == 0)
5703
return META_GTK_COLOR_BG;
5704
else if (strcmp ("light", str) == 0)
5705
return META_GTK_COLOR_LIGHT;
5706
else if (strcmp ("dark", str) == 0)
5707
return META_GTK_COLOR_DARK;
5708
else if (strcmp ("mid", str) == 0)
5709
return META_GTK_COLOR_MID;
5710
else if (strcmp ("text", str) == 0)
5711
return META_GTK_COLOR_TEXT;
5712
else if (strcmp ("base", str) == 0)
5713
return META_GTK_COLOR_BASE;
5714
else if (strcmp ("text_aa", str) == 0)
5715
return META_GTK_COLOR_TEXT_AA;
5717
return META_GTK_COLOR_LAST;
5721
meta_color_component_to_string (MetaGtkColorComponent component)
5725
case META_GTK_COLOR_FG:
5727
case META_GTK_COLOR_BG:
5729
case META_GTK_COLOR_LIGHT:
5731
case META_GTK_COLOR_DARK:
5733
case META_GTK_COLOR_MID:
5735
case META_GTK_COLOR_TEXT:
5737
case META_GTK_COLOR_BASE:
5739
case META_GTK_COLOR_TEXT_AA:
5741
case META_GTK_COLOR_LAST:
5749
meta_button_state_from_string (const char *str)
5751
if (strcmp ("normal", str) == 0)
5752
return META_BUTTON_STATE_NORMAL;
5753
else if (strcmp ("pressed", str) == 0)
5754
return META_BUTTON_STATE_PRESSED;
5755
else if (strcmp ("prelight", str) == 0)
5756
return META_BUTTON_STATE_PRELIGHT;
5758
return META_BUTTON_STATE_LAST;
5762
meta_button_state_to_string (MetaButtonState state)
5766
case META_BUTTON_STATE_NORMAL:
5768
case META_BUTTON_STATE_PRESSED:
5770
case META_BUTTON_STATE_PRELIGHT:
5772
case META_BUTTON_STATE_LAST:
5780
meta_button_type_from_string (const char *str, MetaTheme *theme)
5782
if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS))
5784
if (strcmp ("shade", str) == 0)
5785
return META_BUTTON_TYPE_SHADE;
5786
else if (strcmp ("above", str) == 0)
5787
return META_BUTTON_TYPE_ABOVE;
5788
else if (strcmp ("stick", str) == 0)
5789
return META_BUTTON_TYPE_STICK;
5790
else if (strcmp ("unshade", str) == 0)
5791
return META_BUTTON_TYPE_UNSHADE;
5792
else if (strcmp ("unabove", str) == 0)
5793
return META_BUTTON_TYPE_UNABOVE;
5794
else if (strcmp ("unstick", str) == 0)
5795
return META_BUTTON_TYPE_UNSTICK;
5798
if (strcmp ("close", str) == 0)
5799
return META_BUTTON_TYPE_CLOSE;
5800
else if (strcmp ("maximize", str) == 0)
5801
return META_BUTTON_TYPE_MAXIMIZE;
5802
else if (strcmp ("minimize", str) == 0)
5803
return META_BUTTON_TYPE_MINIMIZE;
5804
else if (strcmp ("menu", str) == 0)
5805
return META_BUTTON_TYPE_MENU;
5806
else if (strcmp ("left_left_background", str) == 0)
5807
return META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND;
5808
else if (strcmp ("left_middle_background", str) == 0)
5809
return META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND;
5810
else if (strcmp ("left_right_background", str) == 0)
5811
return META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND;
5812
else if (strcmp ("right_left_background", str) == 0)
5813
return META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND;
5814
else if (strcmp ("right_middle_background", str) == 0)
5815
return META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND;
5816
else if (strcmp ("right_right_background", str) == 0)
5817
return META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND;
5819
return META_BUTTON_TYPE_LAST;
5823
meta_button_type_to_string (MetaButtonType type)
5827
case META_BUTTON_TYPE_CLOSE:
5829
case META_BUTTON_TYPE_MAXIMIZE:
5831
case META_BUTTON_TYPE_MINIMIZE:
5833
case META_BUTTON_TYPE_SHADE:
5835
case META_BUTTON_TYPE_ABOVE:
5837
case META_BUTTON_TYPE_STICK:
5839
case META_BUTTON_TYPE_UNSHADE:
5841
case META_BUTTON_TYPE_UNABOVE:
5843
case META_BUTTON_TYPE_UNSTICK:
5845
case META_BUTTON_TYPE_MENU:
5847
case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
5848
return "left_left_background";
5849
case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
5850
return "left_middle_background";
5851
case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
5852
return "left_right_background";
5853
case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
5854
return "right_left_background";
5855
case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
5856
return "right_middle_background";
5857
case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
5858
return "right_right_background";
5859
case META_BUTTON_TYPE_LAST:
5867
meta_frame_piece_from_string (const char *str)
5869
if (strcmp ("entire_background", str) == 0)
5870
return META_FRAME_PIECE_ENTIRE_BACKGROUND;
5871
else if (strcmp ("titlebar", str) == 0)
5872
return META_FRAME_PIECE_TITLEBAR;
5873
else if (strcmp ("titlebar_middle", str) == 0)
5874
return META_FRAME_PIECE_TITLEBAR_MIDDLE;
5875
else if (strcmp ("left_titlebar_edge", str) == 0)
5876
return META_FRAME_PIECE_LEFT_TITLEBAR_EDGE;
5877
else if (strcmp ("right_titlebar_edge", str) == 0)
5878
return META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE;
5879
else if (strcmp ("top_titlebar_edge", str) == 0)
5880
return META_FRAME_PIECE_TOP_TITLEBAR_EDGE;
5881
else if (strcmp ("bottom_titlebar_edge", str) == 0)
5882
return META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE;
5883
else if (strcmp ("title", str) == 0)
5884
return META_FRAME_PIECE_TITLE;
5885
else if (strcmp ("left_edge", str) == 0)
5886
return META_FRAME_PIECE_LEFT_EDGE;
5887
else if (strcmp ("right_edge", str) == 0)
5888
return META_FRAME_PIECE_RIGHT_EDGE;
5889
else if (strcmp ("bottom_edge", str) == 0)
5890
return META_FRAME_PIECE_BOTTOM_EDGE;
5891
else if (strcmp ("overlay", str) == 0)
5892
return META_FRAME_PIECE_OVERLAY;
5894
return META_FRAME_PIECE_LAST;
5898
meta_frame_piece_to_string (MetaFramePiece piece)
5902
case META_FRAME_PIECE_ENTIRE_BACKGROUND:
5903
return "entire_background";
5904
case META_FRAME_PIECE_TITLEBAR:
5906
case META_FRAME_PIECE_TITLEBAR_MIDDLE:
5907
return "titlebar_middle";
5908
case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE:
5909
return "left_titlebar_edge";
5910
case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE:
5911
return "right_titlebar_edge";
5912
case META_FRAME_PIECE_TOP_TITLEBAR_EDGE:
5913
return "top_titlebar_edge";
5914
case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE:
5915
return "bottom_titlebar_edge";
5916
case META_FRAME_PIECE_TITLE:
5918
case META_FRAME_PIECE_LEFT_EDGE:
5920
case META_FRAME_PIECE_RIGHT_EDGE:
5921
return "right_edge";
5922
case META_FRAME_PIECE_BOTTOM_EDGE:
5923
return "bottom_edge";
5924
case META_FRAME_PIECE_OVERLAY:
5926
case META_FRAME_PIECE_LAST:
5934
meta_frame_state_from_string (const char *str)
5936
if (strcmp ("normal", str) == 0)
5937
return META_FRAME_STATE_NORMAL;
5938
else if (strcmp ("maximized", str) == 0)
5939
return META_FRAME_STATE_MAXIMIZED;
5940
else if (strcmp ("shaded", str) == 0)
5941
return META_FRAME_STATE_SHADED;
5942
else if (strcmp ("maximized_and_shaded", str) == 0)
5943
return META_FRAME_STATE_MAXIMIZED_AND_SHADED;
5945
return META_FRAME_STATE_LAST;
5949
meta_frame_state_to_string (MetaFrameState state)
5953
case META_FRAME_STATE_NORMAL:
5955
case META_FRAME_STATE_MAXIMIZED:
5957
case META_FRAME_STATE_SHADED:
5959
case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
5960
return "maximized_and_shaded";
5961
case META_FRAME_STATE_LAST:
5969
meta_frame_resize_from_string (const char *str)
5971
if (strcmp ("none", str) == 0)
5972
return META_FRAME_RESIZE_NONE;
5973
else if (strcmp ("vertical", str) == 0)
5974
return META_FRAME_RESIZE_VERTICAL;
5975
else if (strcmp ("horizontal", str) == 0)
5976
return META_FRAME_RESIZE_HORIZONTAL;
5977
else if (strcmp ("both", str) == 0)
5978
return META_FRAME_RESIZE_BOTH;
5980
return META_FRAME_RESIZE_LAST;
5984
meta_frame_resize_to_string (MetaFrameResize resize)
5988
case META_FRAME_RESIZE_NONE:
5990
case META_FRAME_RESIZE_VERTICAL:
5992
case META_FRAME_RESIZE_HORIZONTAL:
5993
return "horizontal";
5994
case META_FRAME_RESIZE_BOTH:
5996
case META_FRAME_RESIZE_LAST:
6004
meta_frame_focus_from_string (const char *str)
6006
if (strcmp ("no", str) == 0)
6007
return META_FRAME_FOCUS_NO;
6008
else if (strcmp ("yes", str) == 0)
6009
return META_FRAME_FOCUS_YES;
6011
return META_FRAME_FOCUS_LAST;
6015
meta_frame_focus_to_string (MetaFrameFocus focus)
6019
case META_FRAME_FOCUS_NO:
6021
case META_FRAME_FOCUS_YES:
6023
case META_FRAME_FOCUS_LAST:
6031
meta_frame_type_from_string (const char *str)
6033
if (strcmp ("normal", str) == 0)
6034
return META_FRAME_TYPE_NORMAL;
6035
else if (strcmp ("dialog", str) == 0)
6036
return META_FRAME_TYPE_DIALOG;
6037
else if (strcmp ("modal_dialog", str) == 0)
6038
return META_FRAME_TYPE_MODAL_DIALOG;
6039
else if (strcmp ("utility", str) == 0)
6040
return META_FRAME_TYPE_UTILITY;
6041
else if (strcmp ("menu", str) == 0)
6042
return META_FRAME_TYPE_MENU;
6043
else if (strcmp ("border", str) == 0)
6044
return META_FRAME_TYPE_BORDER;
6046
else if (strcmp ("toolbar", str) == 0)
6047
return META_FRAME_TYPE_TOOLBAR;
6050
return META_FRAME_TYPE_LAST;
6054
meta_frame_type_to_string (MetaFrameType type)
6058
case META_FRAME_TYPE_NORMAL:
6060
case META_FRAME_TYPE_DIALOG:
6062
case META_FRAME_TYPE_MODAL_DIALOG:
6063
return "modal_dialog";
6064
case META_FRAME_TYPE_UTILITY:
6066
case META_FRAME_TYPE_MENU:
6068
case META_FRAME_TYPE_BORDER:
6071
case META_FRAME_TYPE_TOOLBAR:
6074
case META_FRAME_TYPE_LAST:
6082
meta_gradient_type_from_string (const char *str)
6084
if (strcmp ("vertical", str) == 0)
6085
return META_GRADIENT_VERTICAL;
6086
else if (strcmp ("horizontal", str) == 0)
6087
return META_GRADIENT_HORIZONTAL;
6088
else if (strcmp ("diagonal", str) == 0)
6089
return META_GRADIENT_DIAGONAL;
6091
return META_GRADIENT_LAST;
6095
meta_gradient_type_to_string (MetaGradientType type)
6099
case META_GRADIENT_VERTICAL:
6101
case META_GRADIENT_HORIZONTAL:
6102
return "horizontal";
6103
case META_GRADIENT_DIAGONAL:
6105
case META_GRADIENT_LAST:
6113
meta_gtk_state_from_string (const char *str)
6115
if (strcmp ("normal", str) == 0 || strcmp ("NORMAL", str) == 0)
6116
return GTK_STATE_NORMAL;
6117
else if (strcmp ("prelight", str) == 0 || strcmp ("PRELIGHT", str) == 0)
6118
return GTK_STATE_PRELIGHT;
6119
else if (strcmp ("active", str) == 0 || strcmp ("ACTIVE", str) == 0)
6120
return GTK_STATE_ACTIVE;
6121
else if (strcmp ("selected", str) == 0 || strcmp ("SELECTED", str) == 0)
6122
return GTK_STATE_SELECTED;
6123
else if (strcmp ("insensitive", str) == 0 || strcmp ("INSENSITIVE", str) == 0)
6124
return GTK_STATE_INSENSITIVE;
6126
return -1; /* hack */
6130
meta_gtk_state_to_string (GtkStateType state)
6134
case GTK_STATE_NORMAL:
6136
case GTK_STATE_PRELIGHT:
6138
case GTK_STATE_ACTIVE:
6140
case GTK_STATE_SELECTED:
6142
case GTK_STATE_INSENSITIVE:
6143
return "INSENSITIVE";
6150
meta_gtk_shadow_from_string (const char *str)
6152
if (strcmp ("none", str) == 0)
6153
return GTK_SHADOW_NONE;
6154
else if (strcmp ("in", str) == 0)
6155
return GTK_SHADOW_IN;
6156
else if (strcmp ("out", str) == 0)
6157
return GTK_SHADOW_OUT;
6158
else if (strcmp ("etched_in", str) == 0)
6159
return GTK_SHADOW_ETCHED_IN;
6160
else if (strcmp ("etched_out", str) == 0)
6161
return GTK_SHADOW_ETCHED_OUT;
6167
meta_gtk_shadow_to_string (GtkShadowType shadow)
6171
case GTK_SHADOW_NONE:
6175
case GTK_SHADOW_OUT:
6177
case GTK_SHADOW_ETCHED_IN:
6179
case GTK_SHADOW_ETCHED_OUT:
6180
return "etched_out";
6187
meta_gtk_arrow_from_string (const char *str)
6189
if (strcmp ("up", str) == 0)
6190
return GTK_ARROW_UP;
6191
else if (strcmp ("down", str) == 0)
6192
return GTK_ARROW_DOWN;
6193
else if (strcmp ("left", str) == 0)
6194
return GTK_ARROW_LEFT;
6195
else if (strcmp ("right", str) == 0)
6196
return GTK_ARROW_RIGHT;
6197
else if (strcmp ("none", str) == 0)
6198
return GTK_ARROW_NONE;
6204
meta_gtk_arrow_to_string (GtkArrowType arrow)
6210
case GTK_ARROW_DOWN:
6212
case GTK_ARROW_LEFT:
6214
case GTK_ARROW_RIGHT:
6216
case GTK_ARROW_NONE:
6224
* Returns a fill_type from a string. The inverse of
6225
* meta_image_fill_type_to_string().
6227
* \param str a string representing a fill_type
6228
* \result the fill_type, or -1 if it represents no fill_type.
6231
meta_image_fill_type_from_string (const char *str)
6233
if (strcmp ("tile", str) == 0)
6234
return META_IMAGE_FILL_TILE;
6235
else if (strcmp ("scale", str) == 0)
6236
return META_IMAGE_FILL_SCALE;
6242
* Returns a string representation of a fill_type. The inverse of
6243
* meta_image_fill_type_from_string().
6245
* \param fill_type the fill type
6246
* \result a string representing that type
6249
meta_image_fill_type_to_string (MetaImageFillType fill_type)
6253
case META_IMAGE_FILL_TILE:
6255
case META_IMAGE_FILL_SCALE:
6263
* Takes a colour "a", scales the lightness and saturation by a certain amount,
6264
* and sets "b" to the resulting colour.
6265
* gtkstyle.c cut-and-pastage.
6267
* \param a the starting colour
6268
* \param b [out] the resulting colour
6269
* \param k amount to scale lightness and saturation by
6272
gtk_style_shade (GdkColor *a,
6280
red = (gdouble) a->red / 65535.0;
6281
green = (gdouble) a->green / 65535.0;
6282
blue = (gdouble) a->blue / 65535.0;
6284
rgb_to_hls (&red, &green, &blue);
6289
else if (green < 0.0)
6295
else if (blue < 0.0)
6298
hls_to_rgb (&red, &green, &blue);
6300
b->red = red * 65535.0;
6301
b->green = green * 65535.0;
6302
b->blue = blue * 65535.0;
6306
* Converts a red/green/blue triplet to a hue/lightness/saturation triplet.
6308
* \param r on input, red; on output, hue
6309
* \param g on input, green; on output, lightness
6310
* \param b on input, blue; on output, saturation
6313
rgb_to_hls (gdouble *r,
6354
l = (max + min) / 2;
6361
s = (max - min) / (max + min);
6363
s = (max - min) / (2 - max - min);
6367
h = (green - blue) / delta;
6368
else if (green == max)
6369
h = 2 + (blue - red) / delta;
6370
else if (blue == max)
6371
h = 4 + (red - green) / delta;
6384
* Converts a hue/lightness/saturation triplet to a red/green/blue triplet.
6386
* \param h on input, hue; on output, red
6387
* \param l on input, lightness; on output, green
6388
* \param s on input, saturation; on output, blue
6391
hls_to_rgb (gdouble *h,
6404
if (lightness <= 0.5)
6405
m2 = lightness * (1 + saturation);
6407
m2 = lightness + saturation - lightness * saturation;
6408
m1 = 2 * lightness - m2;
6410
if (saturation == 0)
6425
r = m1 + (m2 - m1) * hue / 60;
6429
r = m1 + (m2 - m1) * (240 - hue) / 60;
6440
g = m1 + (m2 - m1) * hue / 60;
6444
g = m1 + (m2 - m1) * (240 - hue) / 60;
6455
b = m1 + (m2 - m1) * hue / 60;
6459
b = m1 + (m2 - m1) * (240 - hue) / 60;
6470
/* These are some functions I'm saving to use in optimizing
6471
* MetaDrawOpList, namely to pre-composite pixbufs on client side
6472
* prior to rendering to the server
6475
draw_bg_solid_composite (const MetaTextureSpec *bg,
6476
const MetaTextureSpec *fg,
6479
GdkDrawable *drawable,
6480
const GdkRectangle *clip,
6481
MetaTextureDrawMode mode,
6491
g_assert (bg->type == META_TEXTURE_SOLID);
6492
g_assert (fg->type != META_TEXTURE_COMPOSITE);
6493
g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
6495
meta_color_spec_render (bg->data.solid.color_spec,
6501
case META_TEXTURE_SOLID:
6505
meta_color_spec_render (fg->data.solid.color_spec,
6509
color_composite (&bg_color, &fg_color,
6512
draw_color_rectangle (widget, drawable, &fg_color, clip,
6513
x, y, width, height);
6517
case META_TEXTURE_GRADIENT:
6518
/* FIXME I think we could just composite all the colors in
6519
* the gradient prior to generating the gradient?
6522
case META_TEXTURE_IMAGE:
6525
GdkPixbuf *composited;
6527
pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
6533
composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
6534
gdk_pixbuf_get_has_alpha (pixbuf), 8,
6535
gdk_pixbuf_get_width (pixbuf),
6536
gdk_pixbuf_get_height (pixbuf));
6538
if (composited == NULL)
6540
g_object_unref (G_OBJECT (pixbuf));
6544
gdk_pixbuf_composite_color (pixbuf,
6547
gdk_pixbuf_get_width (pixbuf),
6548
gdk_pixbuf_get_height (pixbuf),
6549
0.0, 0.0, /* offsets */
6550
1.0, 1.0, /* scale */
6551
GDK_INTERP_BILINEAR,
6553
0, 0, /* check offsets */
6555
GDK_COLOR_RGB (bg_color),
6556
GDK_COLOR_RGB (bg_color));
6558
/* Need to draw background since pixbuf is not
6559
* necessarily covering the whole thing
6561
draw_color_rectangle (widget, drawable, &bg_color, clip,
6562
x, y, width, height);
6564
render_pixbuf_aligned (drawable, clip, composited,
6566
x, y, width, height);
6568
g_object_unref (G_OBJECT (pixbuf));
6569
g_object_unref (G_OBJECT (composited));
6573
case META_TEXTURE_BLANK:
6574
case META_TEXTURE_COMPOSITE:
6575
case META_TEXTURE_SHAPE_LIST:
6576
g_assert_not_reached ();
6582
draw_bg_gradient_composite (const MetaTextureSpec *bg,
6583
const MetaTextureSpec *fg,
6586
GdkDrawable *drawable,
6587
const GdkRectangle *clip,
6588
MetaTextureDrawMode mode,
6596
g_assert (bg->type == META_TEXTURE_GRADIENT);
6597
g_assert (fg->type != META_TEXTURE_COMPOSITE);
6598
g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
6602
case META_TEXTURE_SOLID:
6603
case META_TEXTURE_GRADIENT:
6604
case META_TEXTURE_IMAGE:
6606
GdkPixbuf *bg_pixbuf;
6607
GdkPixbuf *fg_pixbuf;
6608
GdkPixbuf *composited;
6609
int fg_width, fg_height;
6611
bg_pixbuf = meta_texture_spec_render (bg, widget, mode, 255,
6614
if (bg_pixbuf == NULL)
6617
fg_pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
6620
if (fg_pixbuf == NULL)
6622
g_object_unref (G_OBJECT (bg_pixbuf));
6626
/* gradients always fill the entire target area */
6627
g_assert (gdk_pixbuf_get_width (bg_pixbuf) == width);
6628
g_assert (gdk_pixbuf_get_height (bg_pixbuf) == height);
6630
composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
6631
gdk_pixbuf_get_has_alpha (bg_pixbuf), 8,
6632
gdk_pixbuf_get_width (bg_pixbuf),
6633
gdk_pixbuf_get_height (bg_pixbuf));
6635
if (composited == NULL)
6637
g_object_unref (G_OBJECT (bg_pixbuf));
6638
g_object_unref (G_OBJECT (fg_pixbuf));
6642
fg_width = gdk_pixbuf_get_width (fg_pixbuf);
6643
fg_height = gdk_pixbuf_get_height (fg_pixbuf);
6645
/* If we wanted to be all cool we could deal with the
6646
* offsets and try to composite only in the clip rectangle,
6647
* but I just don't care enough to figure it out.
6650
gdk_pixbuf_composite (fg_pixbuf,
6652
x + (width - fg_width) * xalign,
6653
y + (height - fg_height) * yalign,
6654
gdk_pixbuf_get_width (fg_pixbuf),
6655
gdk_pixbuf_get_height (fg_pixbuf),
6656
0.0, 0.0, /* offsets */
6657
1.0, 1.0, /* scale */
6658
GDK_INTERP_BILINEAR,
6661
gdk_cairo_set_source_pixbuf (cr, composited, x, y);
6664
g_object_unref (G_OBJECT (bg_pixbuf));
6665
g_object_unref (G_OBJECT (fg_pixbuf));
6666
g_object_unref (G_OBJECT (composited));
6670
case META_TEXTURE_BLANK:
6671
case META_TEXTURE_SHAPE_LIST:
6672
case META_TEXTURE_COMPOSITE:
6673
g_assert_not_reached ();
6680
* Returns the earliest version of the theme format which required support
6681
* for a particular button. (For example, "shade" first appeared in v2, and
6684
* \param type the button type
6685
* \return the number of the theme format
6688
meta_theme_earliest_version_with_button (MetaButtonType type)
6692
case META_BUTTON_TYPE_CLOSE:
6693
case META_BUTTON_TYPE_MAXIMIZE:
6694
case META_BUTTON_TYPE_MINIMIZE:
6695
case META_BUTTON_TYPE_MENU:
6696
case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
6697
case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
6698
case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
6699
case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
6700
case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
6701
case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
6704
case META_BUTTON_TYPE_SHADE:
6705
case META_BUTTON_TYPE_ABOVE:
6706
case META_BUTTON_TYPE_STICK:
6707
case META_BUTTON_TYPE_UNSHADE:
6708
case META_BUTTON_TYPE_UNABOVE:
6709
case META_BUTTON_TYPE_UNSTICK:
6713
meta_warning("Unknown button %d\n", type);