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.
56
#include "theme-private.h"
64
#define GDK_COLOR_RGBA(color) \
66
(((color).red / 256) << 24) | \
67
(((color).green / 256) << 16) | \
68
(((color).blue / 256) << 8)))
70
#define GDK_COLOR_RGB(color) \
71
((guint32) ((((color).red / 256) << 16) | \
72
(((color).green / 256) << 8) | \
73
(((color).blue / 256))))
75
#define ALPHA_TO_UCHAR(d) ((unsigned char) ((d) * 255))
77
#define DEBUG_FILL_STRUCT(s) memset ((s), 0xef, sizeof (*(s)))
78
#define CLAMP_UCHAR(v) ((guchar) (CLAMP (((int)v), (int)0, (int)255)))
79
#define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
81
static void gtk_style_shade (GdkColor *a,
84
static void rgb_to_hls (gdouble *r,
87
static void hls_to_rgb (gdouble *h,
92
* The current theme. (Themes are singleton.)
94
static MetaTheme *meta_current_theme = NULL;
97
colorize_pixbuf (GdkPixbuf *orig,
109
const guchar *src_pixels;
112
pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (orig), gdk_pixbuf_get_has_alpha (orig),
113
gdk_pixbuf_get_bits_per_sample (orig),
114
gdk_pixbuf_get_width (orig), gdk_pixbuf_get_height (orig));
119
orig_rowstride = gdk_pixbuf_get_rowstride (orig);
120
dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
121
width = gdk_pixbuf_get_width (pixbuf);
122
height = gdk_pixbuf_get_height (pixbuf);
123
has_alpha = gdk_pixbuf_get_has_alpha (orig);
124
src_pixels = gdk_pixbuf_get_pixels (orig);
125
dest_pixels = gdk_pixbuf_get_pixels (pixbuf);
127
for (y = 0; y < height; y++)
129
src = src_pixels + y * orig_rowstride;
130
dest = dest_pixels + y * dest_rowstride;
132
for (x = 0; x < width; x++)
136
intensity = INTENSITY (src[0], src[1], src[2]) / 255.0;
138
if (intensity <= 0.5)
140
/* Go from black at intensity = 0.0 to new_color at intensity = 0.5 */
141
dr = (new_color->red * intensity * 2.0) / 65535.0;
142
dg = (new_color->green * intensity * 2.0) / 65535.0;
143
db = (new_color->blue * intensity * 2.0) / 65535.0;
147
/* Go from new_color at intensity = 0.5 to white at intensity = 1.0 */
148
dr = (new_color->red + (65535 - new_color->red) * (intensity - 0.5) * 2.0) / 65535.0;
149
dg = (new_color->green + (65535 - new_color->green) * (intensity - 0.5) * 2.0) / 65535.0;
150
db = (new_color->blue + (65535 - new_color->blue) * (intensity - 0.5) * 2.0) / 65535.0;
153
dest[0] = CLAMP_UCHAR (255 * dr);
154
dest[1] = CLAMP_UCHAR (255 * dg);
155
dest[2] = CLAMP_UCHAR (255 * db);
175
color_composite (const GdkColor *bg,
183
alpha = alpha_d * 0xffff;
184
color->red = color->red + (((fg->red - color->red) * alpha + 0x8000) >> 16);
185
color->green = color->green + (((fg->green - color->green) * alpha + 0x8000) >> 16);
186
color->blue = color->blue + (((fg->blue - color->blue) * alpha + 0x8000) >> 16);
190
* Sets all the fields of a border to dummy values.
192
* \param border The border whose fields should be reset.
195
init_border (GtkBorder *border)
204
* meta_frame_layout_new: (skip)
206
* Creates a new, empty MetaFrameLayout. The fields will be set to dummy
209
* Returns: 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 (layout != NULL);
415
if (!layout->has_title)
418
buttons_height = layout->button_height +
419
layout->button_border.top + layout->button_border.bottom;
420
title_height = text_height +
421
layout->title_vertical_pad +
422
layout->title_border.top + layout->title_border.bottom;
426
*top_height = MAX (buttons_height, title_height);
430
*left_width = layout->left_width;
432
*right_width = layout->right_width;
436
if (flags & META_FRAME_SHADED)
439
*bottom_height = layout->bottom_height;
442
if (flags & META_FRAME_FULLSCREEN)
455
static MetaButtonSpace*
456
rect_for_function (MetaFrameGeometry *fgeom,
457
MetaFrameFlags flags,
458
MetaButtonFunction function,
462
/* Firstly, check version-specific things. */
464
if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS))
468
case META_BUTTON_FUNCTION_SHADE:
469
if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED))
470
return &fgeom->shade_rect;
473
case META_BUTTON_FUNCTION_ABOVE:
474
if (!(flags & META_FRAME_ABOVE))
475
return &fgeom->above_rect;
478
case META_BUTTON_FUNCTION_STICK:
479
if (!(flags & META_FRAME_STUCK))
480
return &fgeom->stick_rect;
483
case META_BUTTON_FUNCTION_UNSHADE:
484
if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED))
485
return &fgeom->unshade_rect;
488
case META_BUTTON_FUNCTION_UNABOVE:
489
if (flags & META_FRAME_ABOVE)
490
return &fgeom->unabove_rect;
493
case META_BUTTON_FUNCTION_UNSTICK:
494
if (flags & META_FRAME_STUCK)
495
return &fgeom->unstick_rect;
497
/* just go on to the next switch block */;
501
/* now consider the buttons which exist in all versions */
505
case META_BUTTON_FUNCTION_MENU:
506
if (flags & META_FRAME_ALLOWS_MENU)
507
return &fgeom->menu_rect;
510
case META_BUTTON_FUNCTION_MINIMIZE:
511
if (flags & META_FRAME_ALLOWS_MINIMIZE)
512
return &fgeom->min_rect;
515
case META_BUTTON_FUNCTION_MAXIMIZE:
516
if (flags & META_FRAME_ALLOWS_MAXIMIZE)
517
return &fgeom->max_rect;
520
case META_BUTTON_FUNCTION_CLOSE:
521
if (flags & META_FRAME_ALLOWS_DELETE)
522
return &fgeom->close_rect;
525
case META_BUTTON_FUNCTION_STICK:
526
case META_BUTTON_FUNCTION_SHADE:
527
case META_BUTTON_FUNCTION_ABOVE:
528
case META_BUTTON_FUNCTION_UNSTICK:
529
case META_BUTTON_FUNCTION_UNSHADE:
530
case META_BUTTON_FUNCTION_UNABOVE:
531
/* we are being asked for a >v1 button which hasn't been handled yet,
532
* so obviously we're not in a theme which supports that version.
533
* therefore, we don't show the button. return NULL and all will
538
case META_BUTTON_FUNCTION_LAST:
546
strip_button (MetaButtonSpace *func_rects[MAX_BUTTONS_PER_CORNER],
547
GdkRectangle *bg_rects[MAX_BUTTONS_PER_CORNER],
549
MetaButtonSpace *to_strip)
556
if (func_rects[i] == to_strip)
560
/* shift the other rects back in the array */
563
func_rects[i] = func_rects[i+1];
564
bg_rects[i] = bg_rects[i+1];
569
func_rects[i] = NULL;
578
return FALSE; /* did not strip anything */
582
meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
584
MetaFrameFlags flags,
587
const MetaButtonLayout *button_layout,
588
MetaFrameGeometry *fgeom,
591
int i, n_left, n_right, n_left_spacers, n_right_spacers;
594
int title_right_edge;
596
int button_width, button_height;
597
int min_size_for_rounding;
599
/* the left/right rects in order; the max # of rects
600
* is the number of button functions
602
MetaButtonSpace *left_func_rects[MAX_BUTTONS_PER_CORNER];
603
MetaButtonSpace *right_func_rects[MAX_BUTTONS_PER_CORNER];
604
GdkRectangle *left_bg_rects[MAX_BUTTONS_PER_CORNER];
605
gboolean left_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
606
GdkRectangle *right_bg_rects[MAX_BUTTONS_PER_CORNER];
607
gboolean right_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
609
meta_frame_layout_get_borders (layout, text_height,
612
&fgeom->bottom_height,
614
&fgeom->right_width);
616
width = client_width + fgeom->left_width + fgeom->right_width;
618
height = ((flags & META_FRAME_SHADED) ? 0: client_height) +
619
fgeom->top_height + fgeom->bottom_height;
621
fgeom->width = width;
622
fgeom->height = height;
624
fgeom->top_titlebar_edge = layout->title_border.top;
625
fgeom->bottom_titlebar_edge = layout->title_border.bottom;
626
fgeom->left_titlebar_edge = layout->left_titlebar_edge;
627
fgeom->right_titlebar_edge = layout->right_titlebar_edge;
633
switch (layout->button_sizing)
635
case META_BUTTON_SIZING_ASPECT:
636
button_height = fgeom->top_height - layout->button_border.top - layout->button_border.bottom;
637
button_width = button_height / layout->button_aspect;
639
case META_BUTTON_SIZING_FIXED:
640
button_width = layout->button_width;
641
button_height = layout->button_height;
643
case META_BUTTON_SIZING_LAST:
644
g_assert_not_reached ();
648
/* FIXME all this code sort of pretends that duplicate buttons
649
* with the same function are allowed, but that breaks the
650
* code in frames.c, so isn't really allowed right now.
651
* Would need left_close_rect, right_close_rect, etc.
654
/* Init all button rects to 0, lame hack */
655
memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0',
656
LENGTH_OF_BUTTON_RECTS);
663
if (!layout->hide_buttons)
665
/* Try to fill in rects */
666
for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
668
left_func_rects[n_left] = rect_for_function (fgeom, flags,
669
button_layout->left_buttons[i],
671
if (left_func_rects[n_left] != NULL)
673
left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i];
674
if (button_layout->left_buttons_has_spacer[i])
681
for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
683
right_func_rects[n_right] = rect_for_function (fgeom, flags,
684
button_layout->right_buttons[i],
686
if (right_func_rects[n_right] != NULL)
688
right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i];
689
if (button_layout->right_buttons_has_spacer[i])
697
for (i = 0; i < MAX_BUTTONS_PER_CORNER; i++)
699
left_bg_rects[i] = NULL;
700
right_bg_rects[i] = NULL;
703
for (i = 0; i < n_left; i++)
705
if (i == 0) /* For the first button (From left to right) */
707
if (n_left > 1) /* Set left_left_background
708
if we have more than one button */
709
left_bg_rects[i] = &fgeom->left_left_background;
710
else /* No background if we have only one single button */
711
left_bg_rects[i] = &fgeom->left_single_background;
713
else if (i == (n_left - 1))
714
left_bg_rects[i] = &fgeom->left_right_background;
716
left_bg_rects[i] = &fgeom->left_middle_backgrounds[i - 1];
719
for (i = 0; i < n_right; i++)
721
if (i == (n_right - 1)) /* For the first button (From right to left) */
723
if (n_right > 1) /* Set right_right_background
724
if we have more than one button */
725
right_bg_rects[i] = &fgeom->right_right_background;
726
else /* No background if we have only one single button */
727
right_bg_rects[i] = &fgeom->right_single_background;
730
right_bg_rects[i] = &fgeom->right_left_background;
732
right_bg_rects[i] = &fgeom->right_middle_backgrounds[i - 1];
735
/* Be sure buttons fit */
736
while (n_left > 0 || n_right > 0)
738
int space_used_by_buttons;
741
space_available = fgeom->width - layout->left_titlebar_edge - layout->right_titlebar_edge;
743
space_used_by_buttons = 0;
745
space_used_by_buttons += button_width * n_left;
746
space_used_by_buttons += (button_width * 0.75) * n_left_spacers;
747
space_used_by_buttons += layout->button_border.left * n_left;
748
space_used_by_buttons += layout->button_border.right * n_left;
750
space_used_by_buttons += button_width * n_right;
751
space_used_by_buttons += (button_width * 0.75) * n_right_spacers;
752
space_used_by_buttons += layout->button_border.left * n_right;
753
space_used_by_buttons += layout->button_border.right * n_right;
755
if (space_used_by_buttons <= space_available)
756
break; /* Everything fits, bail out */
758
/* First try to remove separators */
759
if (n_left_spacers > 0)
761
left_buttons_has_spacer[--n_left_spacers] = FALSE;
764
else if (n_right_spacers > 0)
766
right_buttons_has_spacer[--n_right_spacers] = FALSE;
770
/* Otherwise we need to shave out a button. Shave
771
* above, stick, shade, min, max, close, then menu (menu is most useful);
772
* prefer the default button locations.
774
if (strip_button (left_func_rects, left_bg_rects,
775
&n_left, &fgeom->above_rect))
777
else if (strip_button (right_func_rects, right_bg_rects,
778
&n_right, &fgeom->above_rect))
780
else if (strip_button (left_func_rects, left_bg_rects,
781
&n_left, &fgeom->stick_rect))
783
else if (strip_button (right_func_rects, right_bg_rects,
784
&n_right, &fgeom->stick_rect))
786
else if (strip_button (left_func_rects, left_bg_rects,
787
&n_left, &fgeom->shade_rect))
789
else if (strip_button (right_func_rects, right_bg_rects,
790
&n_right, &fgeom->shade_rect))
792
else if (strip_button (left_func_rects, left_bg_rects,
793
&n_left, &fgeom->min_rect))
795
else if (strip_button (right_func_rects, right_bg_rects,
796
&n_right, &fgeom->min_rect))
798
else if (strip_button (left_func_rects, left_bg_rects,
799
&n_left, &fgeom->max_rect))
801
else if (strip_button (right_func_rects, right_bg_rects,
802
&n_right, &fgeom->max_rect))
804
else if (strip_button (left_func_rects, left_bg_rects,
805
&n_left, &fgeom->close_rect))
807
else if (strip_button (right_func_rects, right_bg_rects,
808
&n_right, &fgeom->close_rect))
810
else if (strip_button (right_func_rects, right_bg_rects,
811
&n_right, &fgeom->menu_rect))
813
else if (strip_button (left_func_rects, left_bg_rects,
814
&n_left, &fgeom->menu_rect))
818
meta_bug ("Could not find a button to strip. n_left = %d n_right = %d\n",
823
/* center buttons vertically */
824
button_y = (fgeom->top_height -
825
(button_height + layout->button_border.top + layout->button_border.bottom)) / 2 + layout->button_border.top;
827
/* right edge of farthest-right button */
828
x = width - layout->right_titlebar_edge;
833
MetaButtonSpace *rect;
835
if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */
838
rect = right_func_rects[i];
839
rect->visible.x = x - layout->button_border.right - button_width;
840
if (right_buttons_has_spacer[i])
841
rect->visible.x -= (button_width * 0.75);
843
rect->visible.y = button_y;
844
rect->visible.width = button_width;
845
rect->visible.height = button_height;
847
if (flags & META_FRAME_MAXIMIZED)
849
rect->clickable.x = rect->visible.x;
850
rect->clickable.y = rect->visible.y;
851
rect->clickable.width = button_width;
852
rect->clickable.height = button_height;
854
if (i == n_right - 1)
855
rect->clickable.width += layout->right_titlebar_edge + layout->right_width + layout->button_border.right;
859
g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
861
*(right_bg_rects[i]) = rect->visible;
863
x = rect->visible.x - layout->button_border.left;
868
/* save right edge of titlebar for later use */
869
title_right_edge = x - layout->title_border.right;
871
/* Now x changes to be position from the left and we go through
872
* the left-side buttons
874
x = layout->left_titlebar_edge;
875
for (i = 0; i < n_left; i++)
877
MetaButtonSpace *rect;
879
rect = left_func_rects[i];
881
rect->visible.x = x + layout->button_border.left;
882
rect->visible.y = button_y;
883
rect->visible.width = button_width;
884
rect->visible.height = button_height;
886
if (flags & META_FRAME_MAXIMIZED)
888
rect->clickable.x = rect->visible.x;
889
rect->clickable.y = rect->visible.y;
890
rect->clickable.width = button_width;
891
rect->clickable.height = button_height;
894
g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
896
x = rect->visible.x + rect->visible.width + layout->button_border.right;
897
if (left_buttons_has_spacer[i])
898
x += (button_width * 0.75);
900
*(left_bg_rects[i]) = rect->visible;
903
/* We always fill as much vertical space as possible with title rect,
904
* rather than centering it like the buttons
906
fgeom->title_rect.x = x + layout->title_border.left;
907
fgeom->title_rect.y = layout->title_border.top;
908
fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x;
909
fgeom->title_rect.height = fgeom->top_height - layout->title_border.top - layout->title_border.bottom;
911
/* Nuke title if it won't fit */
912
if (fgeom->title_rect.width < 0 ||
913
fgeom->title_rect.height < 0)
915
fgeom->title_rect.width = 0;
916
fgeom->title_rect.height = 0;
919
if (flags & META_FRAME_SHADED)
920
min_size_for_rounding = 0;
922
min_size_for_rounding = 5;
924
fgeom->top_left_corner_rounded_radius = 0;
925
fgeom->top_right_corner_rounded_radius = 0;
926
fgeom->bottom_left_corner_rounded_radius = 0;
927
fgeom->bottom_right_corner_rounded_radius = 0;
929
if (fgeom->top_height + fgeom->left_width >= min_size_for_rounding)
930
fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius;
931
if (fgeom->top_height + fgeom->right_width >= min_size_for_rounding)
932
fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius;
934
if (fgeom->bottom_height + fgeom->left_width >= min_size_for_rounding)
935
fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius;
936
if (fgeom->bottom_height + fgeom->right_width >= min_size_for_rounding)
937
fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius;
941
* meta_gradient_spec_new: (skip)
945
meta_gradient_spec_new (MetaGradientType type)
947
MetaGradientSpec *spec;
949
spec = g_new (MetaGradientSpec, 1);
952
spec->color_specs = NULL;
958
free_color_spec (gpointer spec, gpointer user_data)
960
meta_color_spec_free (spec);
964
meta_gradient_spec_free (MetaGradientSpec *spec)
966
g_return_if_fail (spec != NULL);
968
g_slist_foreach (spec->color_specs, free_color_spec, NULL);
969
g_slist_free (spec->color_specs);
971
DEBUG_FILL_STRUCT (spec);
976
meta_gradient_spec_render (const MetaGradientSpec *spec,
987
n_colors = g_slist_length (spec->color_specs);
992
colors = g_new (GdkColor, n_colors);
995
tmp = spec->color_specs;
998
meta_color_spec_render (tmp->data, widget, &colors[i]);
1004
pixbuf = meta_gradient_create_multi (width, height,
1014
meta_gradient_spec_validate (MetaGradientSpec *spec,
1017
g_return_val_if_fail (spec != NULL, FALSE);
1019
if (g_slist_length (spec->color_specs) < 2)
1021
g_set_error (error, META_THEME_ERROR,
1022
META_THEME_ERROR_FAILED,
1023
_("Gradients should have at least two colors"));
1031
* meta_alpha_gradient_spec_new: (skip)
1034
MetaAlphaGradientSpec*
1035
meta_alpha_gradient_spec_new (MetaGradientType type,
1038
MetaAlphaGradientSpec *spec;
1040
g_return_val_if_fail (n_alphas > 0, NULL);
1042
spec = g_new0 (MetaAlphaGradientSpec, 1);
1045
spec->alphas = g_new0 (unsigned char, n_alphas);
1046
spec->n_alphas = n_alphas;
1052
meta_alpha_gradient_spec_free (MetaAlphaGradientSpec *spec)
1054
g_return_if_fail (spec != NULL);
1056
g_free (spec->alphas);
1061
* meta_color_spec_new: (skip)
1065
meta_color_spec_new (MetaColorSpecType type)
1067
MetaColorSpec *spec;
1068
MetaColorSpec dummy;
1071
size = G_STRUCT_OFFSET (MetaColorSpec, data);
1075
case META_COLOR_SPEC_BASIC:
1076
size += sizeof (dummy.data.basic);
1079
case META_COLOR_SPEC_GTK:
1080
size += sizeof (dummy.data.gtk);
1083
case META_COLOR_SPEC_BLEND:
1084
size += sizeof (dummy.data.blend);
1087
case META_COLOR_SPEC_SHADE:
1088
size += sizeof (dummy.data.shade);
1092
spec = g_malloc0 (size);
1100
meta_color_spec_free (MetaColorSpec *spec)
1102
g_return_if_fail (spec != NULL);
1106
case META_COLOR_SPEC_BASIC:
1107
DEBUG_FILL_STRUCT (&spec->data.basic);
1110
case META_COLOR_SPEC_GTK:
1111
DEBUG_FILL_STRUCT (&spec->data.gtk);
1114
case META_COLOR_SPEC_BLEND:
1115
if (spec->data.blend.foreground)
1116
meta_color_spec_free (spec->data.blend.foreground);
1117
if (spec->data.blend.background)
1118
meta_color_spec_free (spec->data.blend.background);
1119
DEBUG_FILL_STRUCT (&spec->data.blend);
1122
case META_COLOR_SPEC_SHADE:
1123
if (spec->data.shade.base)
1124
meta_color_spec_free (spec->data.shade.base);
1125
DEBUG_FILL_STRUCT (&spec->data.shade);
1133
* meta_color_spec_new_from_string: (skip)
1137
meta_color_spec_new_from_string (const char *str,
1140
MetaColorSpec *spec;
1144
if (str[0] == 'g' && str[1] == 't' && str[2] == 'k' && str[3] == ':')
1147
const char *bracket;
1148
const char *end_bracket;
1151
MetaGtkColorComponent component;
1154
while (*bracket && *bracket != '[')
1157
if (*bracket == '\0')
1159
g_set_error (err, META_THEME_ERROR,
1160
META_THEME_ERROR_FAILED,
1161
_("GTK color specification must have the state in brackets, e.g. gtk:fg[NORMAL] where NORMAL is the state; could not parse \"%s\""),
1166
end_bracket = bracket;
1168
while (*end_bracket && *end_bracket != ']')
1171
if (*end_bracket == '\0')
1173
g_set_error (err, META_THEME_ERROR,
1174
META_THEME_ERROR_FAILED,
1175
_("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\""),
1180
tmp = g_strndup (bracket + 1, end_bracket - bracket - 1);
1181
state = meta_gtk_state_from_string (tmp);
1182
if (((int) state) == -1)
1184
g_set_error (err, META_THEME_ERROR,
1185
META_THEME_ERROR_FAILED,
1186
_("Did not understand state \"%s\" in color specification"),
1193
tmp = g_strndup (str + 4, bracket - str - 4);
1194
component = meta_color_component_from_string (tmp);
1195
if (component == META_GTK_COLOR_LAST)
1197
g_set_error (err, META_THEME_ERROR,
1198
META_THEME_ERROR_FAILED,
1199
_("Did not understand color component \"%s\" in color specification"),
1206
spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
1207
spec->data.gtk.state = state;
1208
spec->data.gtk.component = component;
1209
g_assert (spec->data.gtk.state < N_GTK_STATES);
1210
g_assert (spec->data.gtk.component < META_GTK_COLOR_LAST);
1212
else if (str[0] == 'b' && str[1] == 'l' && str[2] == 'e' && str[3] == 'n' &&
1213
str[4] == 'd' && str[5] == '/')
1222
split = g_strsplit (str, "/", 4);
1224
if (split[0] == NULL || split[1] == NULL ||
1225
split[2] == NULL || split[3] == NULL)
1227
g_set_error (err, META_THEME_ERROR,
1228
META_THEME_ERROR_FAILED,
1229
_("Blend format is \"blend/bg_color/fg_color/alpha\", \"%s\" does not fit the format"),
1235
alpha = g_ascii_strtod (split[3], &end);
1236
if (end == split[3])
1238
g_set_error (err, META_THEME_ERROR,
1239
META_THEME_ERROR_FAILED,
1240
_("Could not parse alpha value \"%s\" in blended color"),
1246
if (alpha < (0.0 - 1e6) || alpha > (1.0 + 1e6))
1248
g_set_error (err, META_THEME_ERROR,
1249
META_THEME_ERROR_FAILED,
1250
_("Alpha value \"%s\" in blended color is not between 0.0 and 1.0"),
1259
bg = meta_color_spec_new_from_string (split[1], err);
1266
fg = meta_color_spec_new_from_string (split[2], err);
1269
meta_color_spec_free (bg);
1276
spec = meta_color_spec_new (META_COLOR_SPEC_BLEND);
1277
spec->data.blend.alpha = alpha;
1278
spec->data.blend.background = bg;
1279
spec->data.blend.foreground = fg;
1281
else if (str[0] == 's' && str[1] == 'h' && str[2] == 'a' && str[3] == 'd' &&
1282
str[4] == 'e' && str[5] == '/')
1288
MetaColorSpec *base;
1290
split = g_strsplit (str, "/", 3);
1292
if (split[0] == NULL || split[1] == NULL ||
1295
g_set_error (err, META_THEME_ERROR,
1296
META_THEME_ERROR_FAILED,
1297
_("Shade format is \"shade/base_color/factor\", \"%s\" does not fit the format"),
1303
factor = g_ascii_strtod (split[2], &end);
1304
if (end == split[2])
1306
g_set_error (err, META_THEME_ERROR,
1307
META_THEME_ERROR_FAILED,
1308
_("Could not parse shade factor \"%s\" in shaded color"),
1314
if (factor < (0.0 - 1e6))
1316
g_set_error (err, META_THEME_ERROR,
1317
META_THEME_ERROR_FAILED,
1318
_("Shade factor \"%s\" in shaded color is negative"),
1326
base = meta_color_spec_new_from_string (split[1], err);
1335
spec = meta_color_spec_new (META_COLOR_SPEC_SHADE);
1336
spec->data.shade.factor = factor;
1337
spec->data.shade.base = base;
1341
spec = meta_color_spec_new (META_COLOR_SPEC_BASIC);
1343
if (!gdk_color_parse (str, &spec->data.basic.color))
1345
g_set_error (err, META_THEME_ERROR,
1346
META_THEME_ERROR_FAILED,
1347
_("Could not parse color \"%s\""),
1349
meta_color_spec_free (spec);
1360
* meta_color_spec_new_gtk: (skip)
1364
meta_color_spec_new_gtk (MetaGtkColorComponent component,
1367
MetaColorSpec *spec;
1369
spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
1371
spec->data.gtk.component = component;
1372
spec->data.gtk.state = state;
1378
meta_color_spec_render (MetaColorSpec *spec,
1384
style = gtk_widget_get_style (widget);
1386
g_return_if_fail (spec != NULL);
1387
g_return_if_fail (GTK_IS_WIDGET (widget));
1388
g_return_if_fail (style != NULL);
1392
case META_COLOR_SPEC_BASIC:
1393
*color = spec->data.basic.color;
1396
case META_COLOR_SPEC_GTK:
1397
switch (spec->data.gtk.component)
1399
case META_GTK_COLOR_BG:
1400
*color = style->bg[spec->data.gtk.state];
1402
case META_GTK_COLOR_FG:
1403
*color = style->fg[spec->data.gtk.state];
1405
case META_GTK_COLOR_BASE:
1406
*color = style->base[spec->data.gtk.state];
1408
case META_GTK_COLOR_TEXT:
1409
*color = style->text[spec->data.gtk.state];
1411
case META_GTK_COLOR_LIGHT:
1412
*color = style->light[spec->data.gtk.state];
1414
case META_GTK_COLOR_DARK:
1415
*color = style->dark[spec->data.gtk.state];
1417
case META_GTK_COLOR_MID:
1418
*color = style->mid[spec->data.gtk.state];
1420
case META_GTK_COLOR_TEXT_AA:
1421
*color = style->text_aa[spec->data.gtk.state];
1423
case META_GTK_COLOR_LAST:
1424
g_assert_not_reached ();
1429
case META_COLOR_SPEC_BLEND:
1433
meta_color_spec_render (spec->data.blend.background, widget, &bg);
1434
meta_color_spec_render (spec->data.blend.foreground, widget, &fg);
1436
color_composite (&bg, &fg, spec->data.blend.alpha,
1437
&spec->data.blend.color);
1439
*color = spec->data.blend.color;
1443
case META_COLOR_SPEC_SHADE:
1445
meta_color_spec_render (spec->data.shade.base, widget,
1446
&spec->data.shade.color);
1448
gtk_style_shade (&spec->data.shade.color,
1449
&spec->data.shade.color, spec->data.shade.factor);
1451
*color = spec->data.shade.color;
1458
* Represents an operation as a string.
1460
* \param type an operation, such as addition
1461
* \return a string, such as "+"
1464
op_name (PosOperatorType type)
1470
case POS_OP_SUBTRACT:
1472
case POS_OP_MULTIPLY:
1490
* Parses a string and returns an operation.
1492
* \param p a pointer into a string representing an operation; part of an
1493
* expression somewhere, so not null-terminated
1494
* \param len set to the length of the string found. Set to 0 if none is.
1495
* \return the operation found. If none was, returns POS_OP_NONE.
1497
static PosOperatorType
1498
op_from_string (const char *p,
1510
return POS_OP_SUBTRACT;
1513
return POS_OP_MULTIPLY;
1516
return POS_OP_DIVIDE;
1531
else if (p[0] == '`' &&
1546
* Frees an array of tokens. All the tokens and their associated memory
1549
* \param tokens an array of tokens to be freed
1550
* \param n_tokens how many tokens are in the array.
1553
free_tokens (PosToken *tokens,
1558
/* n_tokens can be 0 since tokens may have been allocated more than
1559
* it was initialized
1562
for (i = 0; i < n_tokens; i++)
1563
if (tokens[i].type == POS_TOKEN_VARIABLE)
1564
g_free (tokens[i].d.v.name);
1570
* Tokenises a number in an expression.
1572
* \param p a pointer into a string representing an operation; part of an
1573
* expression somewhere, so not null-terminated
1574
* \param end_return set to a pointer to the end of the number found; but
1575
* not updated if no number was found at all
1576
* \param next set to either an integer or a float token
1577
* \param[out] err set to the problem if there was a problem
1578
* \return TRUE if a valid number was found, FALSE otherwise (and "err" will
1581
* \bug The "while (*start)..." part: what's wrong with strchr-ish things?
1582
* \bug The name is wrong: it doesn't parse anything.
1583
* \ingroup tokenizer
1586
parse_number (const char *p,
1587
const char **end_return,
1591
const char *start = p;
1596
while (*p && (*p == '.' || g_ascii_isdigit (*p)))
1601
char buf[7] = { '\0' };
1602
buf[g_unichar_to_utf8 (g_utf8_get_char (p), buf)] = '\0';
1603
g_set_error (err, META_THEME_ERROR,
1604
META_THEME_ERROR_BAD_CHARACTER,
1605
_("Coordinate expression contains character '%s' which is not allowed"),
1612
/* we need this to exclude floats like "1e6" */
1613
num_str = g_strndup (start, p - start);
1625
next->type = POS_TOKEN_DOUBLE;
1626
next->d.d.val = g_ascii_strtod (num_str, &end);
1630
g_set_error (err, META_THEME_ERROR,
1631
META_THEME_ERROR_FAILED,
1632
_("Coordinate expression contains floating point number '%s' which could not be parsed"),
1640
next->type = POS_TOKEN_INT;
1641
next->d.i.val = strtol (num_str, &end, 10);
1644
g_set_error (err, META_THEME_ERROR,
1645
META_THEME_ERROR_FAILED,
1646
_("Coordinate expression contains integer '%s' which could not be parsed"),
1655
g_assert (next->type == POS_TOKEN_INT || next->type == POS_TOKEN_DOUBLE);
1661
* Whether a variable can validly appear as part of the name of a variable.
1663
#define IS_VARIABLE_CHAR(c) (g_ascii_isalpha ((c)) || (c) == '_')
1667
debug_print_tokens (PosToken *tokens,
1672
for (i = 0; i < n_tokens; i++)
1674
PosToken *t = &tokens[i];
1681
g_print ("\"%d\"", t->d.i.val);
1683
case POS_TOKEN_DOUBLE:
1684
g_print ("\"%g\"", t->d.d.val);
1686
case POS_TOKEN_OPEN_PAREN:
1689
case POS_TOKEN_CLOSE_PAREN:
1692
case POS_TOKEN_VARIABLE:
1693
g_print ("\"%s\"", t->d.v.name);
1695
case POS_TOKEN_OPERATOR:
1696
g_print ("\"%s\"", op_name (t->d.o.op));
1706
* Tokenises an expression.
1708
* \param expr The expression
1709
* \param[out] tokens_p The resulting tokens
1710
* \param[out] n_tokens_p The number of resulting tokens
1711
* \param[out] err set to the problem if there was a problem
1713
* \return True if the expression was successfully tokenised; false otherwise.
1715
* \ingroup tokenizer
1718
pos_tokenize (const char *expr,
1719
PosToken **tokens_p,
1733
tokens = g_new (PosToken, allocated);
1741
if (n_tokens == allocated)
1744
tokens = g_renew (PosToken, tokens, allocated);
1747
next = &tokens[n_tokens];
1754
case '-': /* negative numbers aren't allowed so this is easy */
1757
next->type = POS_TOKEN_OPERATOR;
1758
next->d.o.op = op_from_string (p, &len);
1759
if (next->d.o.op != POS_OP_NONE)
1762
p = p + (len - 1); /* -1 since we ++p later */
1766
g_set_error (err, META_THEME_ERROR,
1767
META_THEME_ERROR_FAILED,
1768
_("Coordinate expression contained unknown operator at the start of this text: \"%s\""),
1776
next->type = POS_TOKEN_OPEN_PAREN;
1781
next->type = POS_TOKEN_CLOSE_PAREN;
1791
if (IS_VARIABLE_CHAR (*p))
1793
/* Assume variable */
1794
const char *start = p;
1795
while (*p && IS_VARIABLE_CHAR (*p))
1797
g_assert (p != start);
1798
next->type = POS_TOKEN_VARIABLE;
1799
next->d.v.name = g_strndup (start, p - start);
1801
--p; /* since we ++p again at the end of while loop */
1808
if (!parse_number (p, &end, next, err))
1812
p = end - 1; /* -1 since we ++p again at the end of while loop */
1823
g_set_error (err, META_THEME_ERROR,
1824
META_THEME_ERROR_FAILED,
1825
_("Coordinate expression was empty or not understood"));
1831
*n_tokens_p = n_tokens;
1836
g_assert (err == NULL || *err != NULL);
1838
free_tokens (tokens, n_tokens);
1843
* The type of a PosExpr: either integer, double, or an operation.
1854
* Type and value of an expression in a parsed sequence. We don't
1855
* keep expressions in a tree; if this is of type POS_EXPR_OPERATOR,
1856
* the arguments of the operator will be in the array positions
1857
* immediately preceding and following this operator; they cannot
1858
* themselves be operators.
1860
* \bug operator is char; it should really be of PosOperatorType.
1876
debug_print_exprs (PosExpr *exprs,
1881
for (i = 0; i < n_exprs; i++)
1883
switch (exprs[i].type)
1886
g_print (" %d", exprs[i].d.int_val);
1888
case POS_EXPR_DOUBLE:
1889
g_print (" %g", exprs[i].d.double_val);
1891
case POS_EXPR_OPERATOR:
1892
g_print (" %s", op_name (exprs[i].d.operator));
1901
do_operation (PosExpr *a,
1906
/* Promote types to double if required */
1907
if (a->type == POS_EXPR_DOUBLE ||
1908
b->type == POS_EXPR_DOUBLE)
1910
if (a->type != POS_EXPR_DOUBLE)
1912
a->type = POS_EXPR_DOUBLE;
1913
a->d.double_val = a->d.int_val;
1915
if (b->type != POS_EXPR_DOUBLE)
1917
b->type = POS_EXPR_DOUBLE;
1918
b->d.double_val = b->d.int_val;
1922
g_assert (a->type == b->type);
1924
if (a->type == POS_EXPR_INT)
1928
case POS_OP_MULTIPLY:
1929
a->d.int_val = a->d.int_val * b->d.int_val;
1932
if (b->d.int_val == 0)
1934
g_set_error (err, META_THEME_ERROR,
1935
META_THEME_ERROR_DIVIDE_BY_ZERO,
1936
_("Coordinate expression results in division by zero"));
1939
a->d.int_val = a->d.int_val / b->d.int_val;
1942
if (b->d.int_val == 0)
1944
g_set_error (err, META_THEME_ERROR,
1945
META_THEME_ERROR_DIVIDE_BY_ZERO,
1946
_("Coordinate expression results in division by zero"));
1949
a->d.int_val = a->d.int_val % b->d.int_val;
1952
a->d.int_val = a->d.int_val + b->d.int_val;
1954
case POS_OP_SUBTRACT:
1955
a->d.int_val = a->d.int_val - b->d.int_val;
1958
a->d.int_val = MAX (a->d.int_val, b->d.int_val);
1961
a->d.int_val = MIN (a->d.int_val, b->d.int_val);
1964
g_assert_not_reached ();
1968
else if (a->type == POS_EXPR_DOUBLE)
1972
case POS_OP_MULTIPLY:
1973
a->d.double_val = a->d.double_val * b->d.double_val;
1976
if (b->d.double_val == 0.0)
1978
g_set_error (err, META_THEME_ERROR,
1979
META_THEME_ERROR_DIVIDE_BY_ZERO,
1980
_("Coordinate expression results in division by zero"));
1983
a->d.double_val = a->d.double_val / b->d.double_val;
1986
g_set_error (err, META_THEME_ERROR,
1987
META_THEME_ERROR_MOD_ON_FLOAT,
1988
_("Coordinate expression tries to use mod operator on a floating-point number"));
1991
a->d.double_val = a->d.double_val + b->d.double_val;
1993
case POS_OP_SUBTRACT:
1994
a->d.double_val = a->d.double_val - b->d.double_val;
1997
a->d.double_val = MAX (a->d.double_val, b->d.double_val);
2000
a->d.double_val = MIN (a->d.double_val, b->d.double_val);
2003
g_assert_not_reached ();
2008
g_assert_not_reached ();
2014
do_operations (PosExpr *exprs,
2022
g_print ("Doing prec %d ops on %d exprs\n", precedence, *n_exprs);
2023
debug_print_exprs (exprs, *n_exprs);
2027
while (i < *n_exprs)
2031
/* exprs[i-1] first operand
2033
* exprs[i+1] second operand
2035
* we replace first operand with result of mul/div/mod,
2036
* or skip over operator and second operand if we have
2040
if (exprs[i-1].type == POS_EXPR_OPERATOR)
2042
g_set_error (err, META_THEME_ERROR,
2043
META_THEME_ERROR_FAILED,
2044
_("Coordinate expression has an operator \"%s\" where an operand was expected"),
2045
op_name (exprs[i-1].d.operator));
2049
if (exprs[i].type != POS_EXPR_OPERATOR)
2051
g_set_error (err, META_THEME_ERROR,
2052
META_THEME_ERROR_FAILED,
2053
_("Coordinate expression had an operand where an operator was expected"));
2057
if (i == (*n_exprs - 1))
2059
g_set_error (err, META_THEME_ERROR,
2060
META_THEME_ERROR_FAILED,
2061
_("Coordinate expression ended with an operator instead of an operand"));
2065
g_assert ((i+1) < *n_exprs);
2067
if (exprs[i+1].type == POS_EXPR_OPERATOR)
2069
g_set_error (err, META_THEME_ERROR,
2070
META_THEME_ERROR_FAILED,
2071
_("Coordinate expression has operator \"%c\" following operator \"%c\" with no operand in between"),
2072
exprs[i+1].d.operator,
2073
exprs[i].d.operator);
2082
switch (exprs[i].d.operator)
2086
case POS_OP_MULTIPLY:
2088
if (!do_operation (&exprs[i-1], &exprs[i+1],
2089
exprs[i].d.operator,
2096
switch (exprs[i].d.operator)
2099
case POS_OP_SUBTRACT:
2101
if (!do_operation (&exprs[i-1], &exprs[i+1],
2102
exprs[i].d.operator,
2108
/* I have no rationale at all for making these low-precedence */
2110
switch (exprs[i].d.operator)
2115
if (!do_operation (&exprs[i-1], &exprs[i+1],
2116
exprs[i].d.operator,
2126
/* exprs[i-1] first operand (now result)
2128
* exprs[i+1] second operand
2129
* exprs[i+2] new operator
2131
* we move new operator just after first operand
2133
if ((i+2) < *n_exprs)
2135
g_memmove (&exprs[i], &exprs[i+2],
2136
sizeof (PosExpr) * (*n_exprs - i - 2));
2143
/* Skip operator and next operand */
2152
* There is a predefined set of variables which can appear in an expression.
2153
* Here we take a token representing a variable, and return the current value
2154
* of that variable in a particular environment.
2155
* (The value is always an integer.)
2157
* There are supposedly some circumstances in which this function can be
2158
* called from outside Metacity, in which case env->theme will be NULL, and
2159
* therefore we can't use it to find out quark values, so we do the comparison
2160
* using strcmp, which is slower.
2162
* \param t The token representing a variable
2163
* \param[out] result The value of that variable; not set if the token did
2164
* not represent a known variable
2165
* \param env The environment within which t should be evaluated
2166
* \param[out] err set to the problem if there was a problem
2168
* \return true if we found the variable asked for, false if we didn't
2170
* \bug shouldn't t be const?
2171
* \bug we should perhaps consider some sort of lookup arrangement into an
2172
* array; also, the duplication of code is unlovely; perhaps using glib
2173
* string hashes instead of quarks would fix both problems?
2177
pos_eval_get_variable (PosToken *t,
2179
const MetaPositionExprEnv *env,
2184
if (t->d.v.name_quark == env->theme->quark_width)
2185
*result = env->rect.width;
2186
else if (t->d.v.name_quark == env->theme->quark_height)
2187
*result = env->rect.height;
2188
else if (env->object_width >= 0 &&
2189
t->d.v.name_quark == env->theme->quark_object_width)
2190
*result = env->object_width;
2191
else if (env->object_height >= 0 &&
2192
t->d.v.name_quark == env->theme->quark_object_height)
2193
*result = env->object_height;
2194
else if (t->d.v.name_quark == env->theme->quark_left_width)
2195
*result = env->left_width;
2196
else if (t->d.v.name_quark == env->theme->quark_right_width)
2197
*result = env->right_width;
2198
else if (t->d.v.name_quark == env->theme->quark_top_height)
2199
*result = env->top_height;
2200
else if (t->d.v.name_quark == env->theme->quark_bottom_height)
2201
*result = env->bottom_height;
2202
else if (t->d.v.name_quark == env->theme->quark_mini_icon_width)
2203
*result = env->mini_icon_width;
2204
else if (t->d.v.name_quark == env->theme->quark_mini_icon_height)
2205
*result = env->mini_icon_height;
2206
else if (t->d.v.name_quark == env->theme->quark_icon_width)
2207
*result = env->icon_width;
2208
else if (t->d.v.name_quark == env->theme->quark_icon_height)
2209
*result = env->icon_height;
2210
else if (t->d.v.name_quark == env->theme->quark_title_width)
2211
*result = env->title_width;
2212
else if (t->d.v.name_quark == env->theme->quark_title_height)
2213
*result = env->title_height;
2214
else if (t->d.v.name_quark == env->theme->quark_frame_x_center)
2215
*result = env->frame_x_center;
2216
else if (t->d.v.name_quark == env->theme->quark_frame_y_center)
2217
*result = env->frame_y_center;
2220
g_set_error (err, META_THEME_ERROR,
2221
META_THEME_ERROR_UNKNOWN_VARIABLE,
2222
_("Coordinate expression had unknown variable or constant \"%s\""),
2229
if (strcmp (t->d.v.name, "width") == 0)
2230
*result = env->rect.width;
2231
else if (strcmp (t->d.v.name, "height") == 0)
2232
*result = env->rect.height;
2233
else if (env->object_width >= 0 &&
2234
strcmp (t->d.v.name, "object_width") == 0)
2235
*result = env->object_width;
2236
else if (env->object_height >= 0 &&
2237
strcmp (t->d.v.name, "object_height") == 0)
2238
*result = env->object_height;
2239
else if (strcmp (t->d.v.name, "left_width") == 0)
2240
*result = env->left_width;
2241
else if (strcmp (t->d.v.name, "right_width") == 0)
2242
*result = env->right_width;
2243
else if (strcmp (t->d.v.name, "top_height") == 0)
2244
*result = env->top_height;
2245
else if (strcmp (t->d.v.name, "bottom_height") == 0)
2246
*result = env->bottom_height;
2247
else if (strcmp (t->d.v.name, "mini_icon_width") == 0)
2248
*result = env->mini_icon_width;
2249
else if (strcmp (t->d.v.name, "mini_icon_height") == 0)
2250
*result = env->mini_icon_height;
2251
else if (strcmp (t->d.v.name, "icon_width") == 0)
2252
*result = env->icon_width;
2253
else if (strcmp (t->d.v.name, "icon_height") == 0)
2254
*result = env->icon_height;
2255
else if (strcmp (t->d.v.name, "title_width") == 0)
2256
*result = env->title_width;
2257
else if (strcmp (t->d.v.name, "title_height") == 0)
2258
*result = env->title_height;
2259
else if (strcmp (t->d.v.name, "frame_x_center") == 0)
2260
*result = env->frame_x_center;
2261
else if (strcmp (t->d.v.name, "frame_y_center") == 0)
2262
*result = env->frame_y_center;
2265
g_set_error (err, META_THEME_ERROR,
2266
META_THEME_ERROR_UNKNOWN_VARIABLE,
2267
_("Coordinate expression had unknown variable or constant \"%s\""),
2277
* Evaluates a sequence of tokens within a particular environment context,
2278
* and returns the current value. May recur if parantheses are found.
2280
* \param tokens A list of tokens to evaluate.
2281
* \param n_tokens How many tokens are in the list.
2282
* \param env The environment context in which to evaluate the expression.
2283
* \param[out] result The current value of the expression
2285
* \bug Yes, we really do reparse the expression every time it's evaluated.
2286
* We should keep the parse tree around all the time and just
2287
* run the new values through it.
2291
pos_eval_helper (PosToken *tokens,
2293
const MetaPositionExprEnv *env,
2297
/* Lazy-ass hardcoded limit on number of terms in expression */
2298
#define MAX_EXPRS 32
2302
PosExpr exprs[MAX_EXPRS];
2306
/* Our first goal is to get a list of PosExpr, essentially
2307
* substituting variables and handling parentheses.
2313
for (i = 0; i < n_tokens; i++)
2315
PosToken *t = &tokens[i];
2317
if (n_exprs >= MAX_EXPRS)
2319
g_set_error (err, META_THEME_ERROR,
2320
META_THEME_ERROR_FAILED,
2321
_("Coordinate expression parser overflowed its buffer."));
2325
if (paren_level == 0)
2330
exprs[n_exprs].type = POS_EXPR_INT;
2331
exprs[n_exprs].d.int_val = t->d.i.val;
2335
case POS_TOKEN_DOUBLE:
2336
exprs[n_exprs].type = POS_EXPR_DOUBLE;
2337
exprs[n_exprs].d.double_val = t->d.d.val;
2341
case POS_TOKEN_OPEN_PAREN:
2343
if (paren_level == 1)
2347
case POS_TOKEN_CLOSE_PAREN:
2348
g_set_error (err, META_THEME_ERROR,
2349
META_THEME_ERROR_BAD_PARENS,
2350
_("Coordinate expression had a close parenthesis with no open parenthesis"));
2353
case POS_TOKEN_VARIABLE:
2354
exprs[n_exprs].type = POS_EXPR_INT;
2356
/* FIXME we should just dump all this crap
2357
* in a hash, maybe keep width/height out
2358
* for optimization purposes
2360
if (!pos_eval_get_variable (t, &exprs[n_exprs].d.int_val, env, err))
2366
case POS_TOKEN_OPERATOR:
2367
exprs[n_exprs].type = POS_EXPR_OPERATOR;
2368
exprs[n_exprs].d.operator = t->d.o.op;
2375
g_assert (paren_level > 0);
2380
case POS_TOKEN_DOUBLE:
2381
case POS_TOKEN_VARIABLE:
2382
case POS_TOKEN_OPERATOR:
2385
case POS_TOKEN_OPEN_PAREN:
2389
case POS_TOKEN_CLOSE_PAREN:
2390
if (paren_level == 1)
2392
/* We closed a toplevel paren group, so recurse */
2393
if (!pos_eval_helper (&tokens[first_paren+1],
2394
i - first_paren - 1,
2410
if (paren_level > 0)
2412
g_set_error (err, META_THEME_ERROR,
2413
META_THEME_ERROR_BAD_PARENS,
2414
_("Coordinate expression had an open parenthesis with no close parenthesis"));
2418
/* Now we have no parens and no vars; so we just do all the multiplies
2419
* and divides, then all the add and subtract.
2423
g_set_error (err, META_THEME_ERROR,
2424
META_THEME_ERROR_FAILED,
2425
_("Coordinate expression doesn't seem to have any operators or operands"));
2429
/* precedence 1 ops */
2431
while (precedence >= 0)
2433
if (!do_operations (exprs, &n_exprs, precedence, err))
2438
g_assert (n_exprs == 1);
2446
* expr = int | double | expr * expr | expr / expr |
2447
* expr + expr | expr - expr | (expr)
2449
* so very not worth fooling with bison, yet so very painful by hand.
2452
* Evaluates an expression.
2454
* \param spec The expression to evaluate.
2455
* \param env The environment context to evaluate the expression in.
2456
* \param[out] val_p The integer value of the expression; if the expression
2457
* is of type float, this will be rounded. If we return
2458
* FALSE because the expression is invalid, this will be
2460
* \param[out] err The error, if anything went wrong.
2462
* \return True if we evaluated the expression successfully; false otherwise.
2464
* \bug Shouldn't spec be const?
2468
pos_eval (MetaDrawSpec *spec,
2469
const MetaPositionExprEnv *env,
2477
if (pos_eval_helper (spec->tokens, spec->n_tokens, env, &expr, err))
2482
*val_p = expr.d.int_val;
2484
case POS_EXPR_DOUBLE:
2485
*val_p = expr.d.double_val;
2487
case POS_EXPR_OPERATOR:
2488
g_assert_not_reached ();
2499
/* We always return both X and Y, but only one will be meaningful in
2504
* meta_parse_position_expression: (skip)
2508
meta_parse_position_expression (MetaDrawSpec *spec,
2509
const MetaPositionExprEnv *env,
2514
/* All positions are in a coordinate system with x, y at the origin.
2515
* The expression can have -, +, *, / as operators, floating point
2516
* or integer constants, and the variables "width" and "height" and
2517
* optionally "object_width" and object_height". Negative numbers
2526
if (pos_eval (spec, env, &spec->value, err) == FALSE)
2528
g_assert (err == NULL || *err != NULL);
2536
*x_return = env->rect.x + val;
2538
*y_return = env->rect.y + val;
2545
* meta_parse_size_expression: (skip)
2549
meta_parse_size_expression (MetaDrawSpec *spec,
2550
const MetaPositionExprEnv *env,
2560
if (pos_eval (spec, env, &spec->value, err) == FALSE)
2562
g_assert (err == NULL || *err != NULL);
2570
*val_return = MAX (val, 1); /* require that sizes be at least 1x1 */
2575
/* To do this we tokenize, replace variable tokens
2576
* that are constants, then reassemble. The purpose
2577
* here is to optimize expressions so we don't do hash
2578
* lookups to eval them. Obviously it's a tradeoff that
2579
* slows down theme load times.
2582
meta_theme_replace_constants (MetaTheme *theme,
2590
gboolean is_constant = TRUE;
2592
/* Loop through tokenized string looking for variables to replace */
2593
for (i = 0; i < n_tokens; i++)
2595
PosToken *t = &tokens[i];
2597
if (t->type == POS_TOKEN_VARIABLE)
2599
if (meta_theme_lookup_int_constant (theme, t->d.v.name, &ival))
2601
t->type = POS_TOKEN_INT;
2604
else if (meta_theme_lookup_float_constant (theme, t->d.v.name, &dval))
2606
t->type = POS_TOKEN_DOUBLE;
2611
/* If we've found a variable that cannot be replaced then the
2612
expression is not a constant expression and we want to
2613
replace it with a GQuark */
2615
t->d.v.name_quark = g_quark_from_string (t->d.v.name);
2616
is_constant = FALSE;
2625
parse_x_position_unchecked (MetaDrawSpec *spec,
2626
const MetaPositionExprEnv *env)
2633
if (!meta_parse_position_expression (spec, env, &retval, NULL, &error))
2635
meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
2638
g_error_free (error);
2645
parse_y_position_unchecked (MetaDrawSpec *spec,
2646
const MetaPositionExprEnv *env)
2653
if (!meta_parse_position_expression (spec, env, NULL, &retval, &error))
2655
meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
2658
g_error_free (error);
2665
parse_size_unchecked (MetaDrawSpec *spec,
2666
MetaPositionExprEnv *env)
2673
if (!meta_parse_size_expression (spec, env, &retval, &error))
2675
meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
2678
g_error_free (error);
2685
meta_draw_spec_free (MetaDrawSpec *spec)
2688
free_tokens (spec->tokens, spec->n_tokens);
2689
g_slice_free (MetaDrawSpec, spec);
2693
* meta_draw_spec_new: (skip)
2697
meta_draw_spec_new (MetaTheme *theme,
2703
spec = g_slice_new0 (MetaDrawSpec);
2705
pos_tokenize (expr, &spec->tokens, &spec->n_tokens, NULL);
2707
spec->constant = meta_theme_replace_constants (theme, spec->tokens,
2708
spec->n_tokens, NULL);
2713
result = pos_eval (spec, NULL, &spec->value, error);
2714
if (result == FALSE)
2716
meta_draw_spec_free (spec);
2725
* meta_draw_op_new: (skip)
2729
meta_draw_op_new (MetaDrawType type)
2735
size = G_STRUCT_OFFSET (MetaDrawOp, data);
2739
case META_DRAW_LINE:
2740
size += sizeof (dummy.data.line);
2743
case META_DRAW_RECTANGLE:
2744
size += sizeof (dummy.data.rectangle);
2748
size += sizeof (dummy.data.arc);
2751
case META_DRAW_CLIP:
2752
size += sizeof (dummy.data.clip);
2755
case META_DRAW_TINT:
2756
size += sizeof (dummy.data.tint);
2759
case META_DRAW_GRADIENT:
2760
size += sizeof (dummy.data.gradient);
2763
case META_DRAW_IMAGE:
2764
size += sizeof (dummy.data.image);
2767
case META_DRAW_GTK_ARROW:
2768
size += sizeof (dummy.data.gtk_arrow);
2771
case META_DRAW_GTK_BOX:
2772
size += sizeof (dummy.data.gtk_box);
2775
case META_DRAW_GTK_VLINE:
2776
size += sizeof (dummy.data.gtk_vline);
2779
case META_DRAW_ICON:
2780
size += sizeof (dummy.data.icon);
2783
case META_DRAW_TITLE:
2784
size += sizeof (dummy.data.title);
2786
case META_DRAW_OP_LIST:
2787
size += sizeof (dummy.data.op_list);
2789
case META_DRAW_TILE:
2790
size += sizeof (dummy.data.tile);
2794
op = g_malloc0 (size);
2802
meta_draw_op_free (MetaDrawOp *op)
2804
g_return_if_fail (op != NULL);
2808
case META_DRAW_LINE:
2809
if (op->data.line.color_spec)
2810
meta_color_spec_free (op->data.line.color_spec);
2812
meta_draw_spec_free (op->data.line.x1);
2813
meta_draw_spec_free (op->data.line.y1);
2814
meta_draw_spec_free (op->data.line.x2);
2815
meta_draw_spec_free (op->data.line.y2);
2818
case META_DRAW_RECTANGLE:
2819
if (op->data.rectangle.color_spec)
2820
g_free (op->data.rectangle.color_spec);
2822
meta_draw_spec_free (op->data.rectangle.x);
2823
meta_draw_spec_free (op->data.rectangle.y);
2824
meta_draw_spec_free (op->data.rectangle.width);
2825
meta_draw_spec_free (op->data.rectangle.height);
2829
if (op->data.arc.color_spec)
2830
g_free (op->data.arc.color_spec);
2832
meta_draw_spec_free (op->data.arc.x);
2833
meta_draw_spec_free (op->data.arc.y);
2834
meta_draw_spec_free (op->data.arc.width);
2835
meta_draw_spec_free (op->data.arc.height);
2838
case META_DRAW_CLIP:
2839
meta_draw_spec_free (op->data.clip.x);
2840
meta_draw_spec_free (op->data.clip.y);
2841
meta_draw_spec_free (op->data.clip.width);
2842
meta_draw_spec_free (op->data.clip.height);
2845
case META_DRAW_TINT:
2846
if (op->data.tint.color_spec)
2847
meta_color_spec_free (op->data.tint.color_spec);
2849
if (op->data.tint.alpha_spec)
2850
meta_alpha_gradient_spec_free (op->data.tint.alpha_spec);
2852
meta_draw_spec_free (op->data.tint.x);
2853
meta_draw_spec_free (op->data.tint.y);
2854
meta_draw_spec_free (op->data.tint.width);
2855
meta_draw_spec_free (op->data.tint.height);
2858
case META_DRAW_GRADIENT:
2859
if (op->data.gradient.gradient_spec)
2860
meta_gradient_spec_free (op->data.gradient.gradient_spec);
2862
if (op->data.gradient.alpha_spec)
2863
meta_alpha_gradient_spec_free (op->data.gradient.alpha_spec);
2865
meta_draw_spec_free (op->data.gradient.x);
2866
meta_draw_spec_free (op->data.gradient.y);
2867
meta_draw_spec_free (op->data.gradient.width);
2868
meta_draw_spec_free (op->data.gradient.height);
2871
case META_DRAW_IMAGE:
2872
if (op->data.image.alpha_spec)
2873
meta_alpha_gradient_spec_free (op->data.image.alpha_spec);
2875
if (op->data.image.pixbuf)
2876
g_object_unref (G_OBJECT (op->data.image.pixbuf));
2878
if (op->data.image.colorize_spec)
2879
meta_color_spec_free (op->data.image.colorize_spec);
2881
if (op->data.image.colorize_cache_pixbuf)
2882
g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
2884
meta_draw_spec_free (op->data.image.x);
2885
meta_draw_spec_free (op->data.image.y);
2886
meta_draw_spec_free (op->data.image.width);
2887
meta_draw_spec_free (op->data.image.height);
2890
case META_DRAW_GTK_ARROW:
2891
meta_draw_spec_free (op->data.gtk_arrow.x);
2892
meta_draw_spec_free (op->data.gtk_arrow.y);
2893
meta_draw_spec_free (op->data.gtk_arrow.width);
2894
meta_draw_spec_free (op->data.gtk_arrow.height);
2897
case META_DRAW_GTK_BOX:
2898
meta_draw_spec_free (op->data.gtk_box.x);
2899
meta_draw_spec_free (op->data.gtk_box.y);
2900
meta_draw_spec_free (op->data.gtk_box.width);
2901
meta_draw_spec_free (op->data.gtk_box.height);
2904
case META_DRAW_GTK_VLINE:
2905
meta_draw_spec_free (op->data.gtk_vline.x);
2906
meta_draw_spec_free (op->data.gtk_vline.y1);
2907
meta_draw_spec_free (op->data.gtk_vline.y2);
2910
case META_DRAW_ICON:
2911
if (op->data.icon.alpha_spec)
2912
meta_alpha_gradient_spec_free (op->data.icon.alpha_spec);
2914
meta_draw_spec_free (op->data.icon.x);
2915
meta_draw_spec_free (op->data.icon.y);
2916
meta_draw_spec_free (op->data.icon.width);
2917
meta_draw_spec_free (op->data.icon.height);
2920
case META_DRAW_TITLE:
2921
if (op->data.title.color_spec)
2922
meta_color_spec_free (op->data.title.color_spec);
2924
meta_draw_spec_free (op->data.title.x);
2925
meta_draw_spec_free (op->data.title.y);
2926
if (op->data.title.ellipsize_width)
2927
meta_draw_spec_free (op->data.title.ellipsize_width);
2930
case META_DRAW_OP_LIST:
2931
if (op->data.op_list.op_list)
2932
meta_draw_op_list_unref (op->data.op_list.op_list);
2934
meta_draw_spec_free (op->data.op_list.x);
2935
meta_draw_spec_free (op->data.op_list.y);
2936
meta_draw_spec_free (op->data.op_list.width);
2937
meta_draw_spec_free (op->data.op_list.height);
2940
case META_DRAW_TILE:
2941
if (op->data.tile.op_list)
2942
meta_draw_op_list_unref (op->data.tile.op_list);
2944
meta_draw_spec_free (op->data.tile.x);
2945
meta_draw_spec_free (op->data.tile.y);
2946
meta_draw_spec_free (op->data.tile.width);
2947
meta_draw_spec_free (op->data.tile.height);
2948
meta_draw_spec_free (op->data.tile.tile_xoffset);
2949
meta_draw_spec_free (op->data.tile.tile_yoffset);
2950
meta_draw_spec_free (op->data.tile.tile_width);
2951
meta_draw_spec_free (op->data.tile.tile_height);
2959
apply_alpha (GdkPixbuf *pixbuf,
2960
MetaAlphaGradientSpec *spec,
2961
gboolean force_copy)
2963
GdkPixbuf *new_pixbuf;
2964
gboolean needs_alpha;
2966
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
2968
needs_alpha = spec && (spec->n_alphas > 1 ||
2969
spec->alphas[0] != 0xff);
2974
if (!gdk_pixbuf_get_has_alpha (pixbuf))
2976
new_pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
2977
g_object_unref (G_OBJECT (pixbuf));
2978
pixbuf = new_pixbuf;
2980
else if (force_copy)
2982
new_pixbuf = gdk_pixbuf_copy (pixbuf);
2983
g_object_unref (G_OBJECT (pixbuf));
2984
pixbuf = new_pixbuf;
2987
g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
2989
meta_gradient_add_alpha (pixbuf, spec->alphas, spec->n_alphas, spec->type);
2995
pixbuf_tile (GdkPixbuf *tile,
3004
tile_width = gdk_pixbuf_get_width (tile);
3005
tile_height = gdk_pixbuf_get_height (tile);
3007
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
3008
gdk_pixbuf_get_has_alpha (tile),
3019
w = MIN (tile_width, width - i);
3020
h = MIN (tile_height, height - j);
3022
gdk_pixbuf_copy_area (tile,
3038
replicate_rows (GdkPixbuf *src,
3044
unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
3045
unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
3046
unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
3048
unsigned char *dest_pixels;
3050
unsigned int dest_rowstride;
3053
result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
3055
dest_rowstride = gdk_pixbuf_get_rowstride (result);
3056
dest_pixels = gdk_pixbuf_get_pixels (result);
3058
for (i = 0; i < height; i++)
3059
memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
3065
replicate_cols (GdkPixbuf *src,
3071
unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
3072
unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
3073
unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
3075
unsigned char *dest_pixels;
3077
unsigned int dest_rowstride;
3080
result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
3082
dest_rowstride = gdk_pixbuf_get_rowstride (result);
3083
dest_pixels = gdk_pixbuf_get_pixels (result);
3085
for (i = 0; i < height; i++)
3087
unsigned char *p = dest_pixels + dest_rowstride * i;
3088
unsigned char *q = pixels + src_rowstride * i;
3090
unsigned char r = *(q++);
3091
unsigned char g = *(q++);
3092
unsigned char b = *(q++);
3094
if (n_channels == 4)
3100
for (j = 0; j < width; j++)
3110
for (j = 0; j < width; j++)
3123
scale_and_alpha_pixbuf (GdkPixbuf *src,
3124
MetaAlphaGradientSpec *alpha_spec,
3125
MetaImageFillType fill_type,
3128
gboolean vertical_stripes,
3129
gboolean horizontal_stripes)
3132
GdkPixbuf *temp_pixbuf;
3138
if (gdk_pixbuf_get_width (pixbuf) == width &&
3139
gdk_pixbuf_get_height (pixbuf) == height)
3141
g_object_ref (G_OBJECT (pixbuf));
3145
if (fill_type == META_IMAGE_FILL_TILE)
3147
pixbuf = pixbuf_tile (pixbuf, width, height);
3151
int src_h, src_w, dest_h, dest_w;
3152
src_h = gdk_pixbuf_get_height (src);
3153
src_w = gdk_pixbuf_get_width (src);
3155
/* prefer to replicate_cols if possible, as that
3156
* is faster (no memory reads)
3158
if (horizontal_stripes)
3160
dest_w = gdk_pixbuf_get_width (src);
3163
else if (vertical_stripes)
3166
dest_h = gdk_pixbuf_get_height (src);
3175
if (dest_w == src_w && dest_h == src_h)
3178
g_object_ref (G_OBJECT (temp_pixbuf));
3182
temp_pixbuf = gdk_pixbuf_scale_simple (src,
3184
GDK_INTERP_BILINEAR);
3187
/* prefer to replicate_cols if possible, as that
3188
* is faster (no memory reads)
3190
if (horizontal_stripes)
3192
pixbuf = replicate_cols (temp_pixbuf, 0, 0, width, height);
3193
g_object_unref (G_OBJECT (temp_pixbuf));
3195
else if (vertical_stripes)
3197
pixbuf = replicate_rows (temp_pixbuf, 0, 0, width, height);
3198
g_object_unref (G_OBJECT (temp_pixbuf));
3202
pixbuf = temp_pixbuf;
3208
pixbuf = apply_alpha (pixbuf, alpha_spec, pixbuf == src);
3214
draw_op_as_pixbuf (const MetaDrawOp *op,
3216
const MetaDrawInfo *info,
3220
/* Try to get the op as a pixbuf, assuming w/h in the op
3221
* matches the width/height passed in. return NULL
3222
* if the op can't be converted to an equivalent pixbuf.
3230
case META_DRAW_LINE:
3233
case META_DRAW_RECTANGLE:
3234
if (op->data.rectangle.filled)
3238
meta_color_spec_render (op->data.rectangle.color_spec,
3242
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
3246
gdk_pixbuf_fill (pixbuf, GDK_COLOR_RGBA (color));
3253
case META_DRAW_CLIP:
3256
case META_DRAW_TINT:
3262
meta_color_spec_render (op->data.rectangle.color_spec,
3267
op->data.tint.alpha_spec &&
3268
(op->data.tint.alpha_spec->n_alphas > 1 ||
3269
op->data.tint.alpha_spec->alphas[0] != 0xff);
3271
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
3277
rgba = GDK_COLOR_RGBA (color);
3279
gdk_pixbuf_fill (pixbuf, rgba);
3281
else if (op->data.tint.alpha_spec->n_alphas == 1)
3283
rgba = GDK_COLOR_RGBA (color);
3285
rgba |= op->data.tint.alpha_spec->alphas[0];
3287
gdk_pixbuf_fill (pixbuf, rgba);
3291
rgba = GDK_COLOR_RGBA (color);
3293
gdk_pixbuf_fill (pixbuf, rgba);
3295
meta_gradient_add_alpha (pixbuf,
3296
op->data.tint.alpha_spec->alphas,
3297
op->data.tint.alpha_spec->n_alphas,
3298
op->data.tint.alpha_spec->type);
3303
case META_DRAW_GRADIENT:
3305
pixbuf = meta_gradient_spec_render (op->data.gradient.gradient_spec,
3306
widget, width, height);
3308
pixbuf = apply_alpha (pixbuf,
3309
op->data.gradient.alpha_spec,
3315
case META_DRAW_IMAGE:
3317
if (op->data.image.colorize_spec)
3321
meta_color_spec_render (op->data.image.colorize_spec,
3324
if (op->data.image.colorize_cache_pixbuf == NULL ||
3325
op->data.image.colorize_cache_pixel != GDK_COLOR_RGB (color))
3327
if (op->data.image.colorize_cache_pixbuf)
3328
g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
3330
/* const cast here */
3331
((MetaDrawOp*)op)->data.image.colorize_cache_pixbuf =
3332
colorize_pixbuf (op->data.image.pixbuf,
3334
((MetaDrawOp*)op)->data.image.colorize_cache_pixel =
3335
GDK_COLOR_RGB (color);
3338
if (op->data.image.colorize_cache_pixbuf)
3340
pixbuf = scale_and_alpha_pixbuf (op->data.image.colorize_cache_pixbuf,
3341
op->data.image.alpha_spec,
3342
op->data.image.fill_type,
3344
op->data.image.vertical_stripes,
3345
op->data.image.horizontal_stripes);
3350
pixbuf = scale_and_alpha_pixbuf (op->data.image.pixbuf,
3351
op->data.image.alpha_spec,
3352
op->data.image.fill_type,
3354
op->data.image.vertical_stripes,
3355
op->data.image.horizontal_stripes);
3360
case META_DRAW_GTK_ARROW:
3361
case META_DRAW_GTK_BOX:
3362
case META_DRAW_GTK_VLINE:
3365
case META_DRAW_ICON:
3366
if (info->mini_icon &&
3367
width <= gdk_pixbuf_get_width (info->mini_icon) &&
3368
height <= gdk_pixbuf_get_height (info->mini_icon))
3369
pixbuf = scale_and_alpha_pixbuf (info->mini_icon,
3370
op->data.icon.alpha_spec,
3371
op->data.icon.fill_type,
3374
else if (info->icon)
3375
pixbuf = scale_and_alpha_pixbuf (info->icon,
3376
op->data.icon.alpha_spec,
3377
op->data.icon.fill_type,
3382
case META_DRAW_TITLE:
3385
case META_DRAW_OP_LIST:
3388
case META_DRAW_TILE:
3396
fill_env (MetaPositionExprEnv *env,
3397
const MetaDrawInfo *info,
3398
MetaRectangle logical_region)
3400
/* FIXME this stuff could be raised into draw_op_list_draw() probably
3402
env->rect = logical_region;
3403
env->object_width = -1;
3404
env->object_height = -1;
3407
env->left_width = info->fgeom->left_width;
3408
env->right_width = info->fgeom->right_width;
3409
env->top_height = info->fgeom->top_height;
3410
env->bottom_height = info->fgeom->bottom_height;
3411
env->frame_x_center = info->fgeom->width / 2 - logical_region.x;
3412
env->frame_y_center = info->fgeom->height / 2 - logical_region.y;
3416
env->left_width = 0;
3417
env->right_width = 0;
3418
env->top_height = 0;
3419
env->bottom_height = 0;
3420
env->frame_x_center = 0;
3421
env->frame_y_center = 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,
3450
const MetaDrawInfo *info,
3452
MetaPositionExprEnv *env)
3458
cairo_set_line_width (cr, 1.0);
3462
case META_DRAW_LINE:
3466
meta_color_spec_render (op->data.line.color_spec, widget, &color);
3467
gdk_cairo_set_source_color (cr, &color);
3469
if (op->data.line.width > 0)
3470
cairo_set_line_width (cr, op->data.line.width);
3472
if (op->data.line.dash_on_length > 0 &&
3473
op->data.line.dash_off_length > 0)
3475
double dash_list[2];
3476
dash_list[0] = op->data.line.dash_on_length;
3477
dash_list[1] = op->data.line.dash_off_length;
3478
cairo_set_dash (cr, dash_list, 2, 0);
3481
x1 = parse_x_position_unchecked (op->data.line.x1, env);
3482
y1 = parse_y_position_unchecked (op->data.line.y1, env);
3484
if (!op->data.line.x2 &&
3485
!op->data.line.y2 &&
3486
op->data.line.width==0)
3488
cairo_rectangle (cr, x1, y1, 1, 1);
3493
if (op->data.line.x2)
3494
x2 = parse_x_position_unchecked (op->data.line.x2, env);
3498
if (op->data.line.y2)
3499
y2 = parse_y_position_unchecked (op->data.line.y2, env);
3503
/* This is one of the cases where we are matching the exact
3504
* pixel aligned rectangle produced by X; for zero-width lines
3505
* the generic algorithm produces the right result so we don't
3506
* need to handle them here.
3508
if ((y1 == y2 || x1 == x2) && op->data.line.width != 0)
3510
double offset = op->data.line.width % 2 ? .5 : 0;
3514
cairo_move_to (cr, x1, y1 + offset);
3515
cairo_line_to (cr, x2, y2 + offset);
3519
cairo_move_to (cr, x1 + offset, y1);
3520
cairo_line_to (cr, x2 + offset, y2);
3525
/* zero-width lines include both end-points in X, unlike wide lines */
3526
if (op->data.line.width == 0)
3527
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3529
cairo_move_to (cr, x1 + .5, y1 + .5);
3530
cairo_line_to (cr, x2 + .5, y2 + .5);
3537
case META_DRAW_RECTANGLE:
3539
int rx, ry, rwidth, rheight;
3541
meta_color_spec_render (op->data.rectangle.color_spec, widget, &color);
3542
gdk_cairo_set_source_color (cr, &color);
3544
rx = parse_x_position_unchecked (op->data.rectangle.x, env);
3545
ry = parse_y_position_unchecked (op->data.rectangle.y, env);
3546
rwidth = parse_size_unchecked (op->data.rectangle.width, env);
3547
rheight = parse_size_unchecked (op->data.rectangle.height, env);
3549
/* Filled and stroked rectangles are the other cases
3550
* we pixel-align to X rasterization
3552
if (op->data.rectangle.filled)
3554
cairo_rectangle (cr, rx, ry, rwidth, rheight);
3559
cairo_rectangle (cr, rx + .5, ry + .5, rwidth, rheight);
3567
int rx, ry, rwidth, rheight;
3568
double start_angle, end_angle;
3569
double center_x, center_y;
3571
meta_color_spec_render (op->data.arc.color_spec, widget, &color);
3572
gdk_cairo_set_source_color (cr, &color);
3574
rx = parse_x_position_unchecked (op->data.arc.x, env);
3575
ry = parse_y_position_unchecked (op->data.arc.y, env);
3576
rwidth = parse_size_unchecked (op->data.arc.width, env);
3577
rheight = parse_size_unchecked (op->data.arc.height, env);
3579
start_angle = op->data.arc.start_angle * (M_PI / 180.)
3580
- (.5 * M_PI); /* start at 12 instead of 3 oclock */
3581
end_angle = start_angle + op->data.arc.extent_angle * (M_PI / 180.);
3582
center_x = rx + (double)rwidth / 2. + .5;
3583
center_y = ry + (double)rheight / 2. + .5;
3587
cairo_translate (cr, center_x, center_y);
3588
cairo_scale (cr, (double)rwidth / 2., (double)rheight / 2.);
3590
if (op->data.arc.extent_angle >= 0)
3591
cairo_arc (cr, 0, 0, 1, start_angle, end_angle);
3593
cairo_arc_negative (cr, 0, 0, 1, start_angle, end_angle);
3597
if (op->data.arc.filled)
3599
cairo_line_to (cr, center_x, center_y);
3607
case META_DRAW_CLIP:
3610
case META_DRAW_TINT:
3612
int rx, ry, rwidth, rheight;
3613
gboolean needs_alpha;
3615
needs_alpha = op->data.tint.alpha_spec &&
3616
(op->data.tint.alpha_spec->n_alphas > 1 ||
3617
op->data.tint.alpha_spec->alphas[0] != 0xff);
3619
rx = parse_x_position_unchecked (op->data.tint.x, env);
3620
ry = parse_y_position_unchecked (op->data.tint.y, env);
3621
rwidth = parse_size_unchecked (op->data.tint.width, env);
3622
rheight = parse_size_unchecked (op->data.tint.height, env);
3626
meta_color_spec_render (op->data.tint.color_spec, widget, &color);
3627
gdk_cairo_set_source_color (cr, &color);
3629
cairo_rectangle (cr, rx, ry, rwidth, rheight);
3636
pixbuf = draw_op_as_pixbuf (op, widget, info,
3641
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
3644
g_object_unref (G_OBJECT (pixbuf));
3650
case META_DRAW_GRADIENT:
3652
int rx, ry, rwidth, rheight;
3655
rx = parse_x_position_unchecked (op->data.gradient.x, env);
3656
ry = parse_y_position_unchecked (op->data.gradient.y, env);
3657
rwidth = parse_size_unchecked (op->data.gradient.width, env);
3658
rheight = parse_size_unchecked (op->data.gradient.height, env);
3660
pixbuf = draw_op_as_pixbuf (op, widget, info,
3665
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
3668
g_object_unref (G_OBJECT (pixbuf));
3673
case META_DRAW_IMAGE:
3675
int rx, ry, rwidth, rheight;
3678
if (op->data.image.pixbuf)
3680
env->object_width = gdk_pixbuf_get_width (op->data.image.pixbuf);
3681
env->object_height = gdk_pixbuf_get_height (op->data.image.pixbuf);
3684
rwidth = parse_size_unchecked (op->data.image.width, env);
3685
rheight = parse_size_unchecked (op->data.image.height, env);
3687
pixbuf = draw_op_as_pixbuf (op, widget, info,
3692
rx = parse_x_position_unchecked (op->data.image.x, env);
3693
ry = parse_y_position_unchecked (op->data.image.y, env);
3695
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
3698
g_object_unref (G_OBJECT (pixbuf));
3703
case META_DRAW_GTK_ARROW:
3705
int rx, ry, rwidth, rheight;
3707
rx = parse_x_position_unchecked (op->data.gtk_arrow.x, env);
3708
ry = parse_y_position_unchecked (op->data.gtk_arrow.y, env);
3709
rwidth = parse_size_unchecked (op->data.gtk_arrow.width, env);
3710
rheight = parse_size_unchecked (op->data.gtk_arrow.height, env);
3712
gtk_paint_arrow (style_gtk,
3714
op->data.gtk_arrow.state,
3715
op->data.gtk_arrow.shadow,
3718
op->data.gtk_arrow.arrow,
3719
op->data.gtk_arrow.filled,
3720
rx, ry, rwidth, rheight);
3724
case META_DRAW_GTK_BOX:
3726
int rx, ry, rwidth, rheight;
3728
rx = parse_x_position_unchecked (op->data.gtk_box.x, env);
3729
ry = parse_y_position_unchecked (op->data.gtk_box.y, env);
3730
rwidth = parse_size_unchecked (op->data.gtk_box.width, env);
3731
rheight = parse_size_unchecked (op->data.gtk_box.height, env);
3733
gtk_paint_box (style_gtk,
3735
op->data.gtk_box.state,
3736
op->data.gtk_box.shadow,
3739
rx, ry, rwidth, rheight);
3743
case META_DRAW_GTK_VLINE:
3747
rx = parse_x_position_unchecked (op->data.gtk_vline.x, env);
3748
ry1 = parse_y_position_unchecked (op->data.gtk_vline.y1, env);
3749
ry2 = parse_y_position_unchecked (op->data.gtk_vline.y2, env);
3751
gtk_paint_vline (style_gtk,
3753
op->data.gtk_vline.state,
3760
case META_DRAW_ICON:
3762
int rx, ry, rwidth, rheight;
3765
rwidth = parse_size_unchecked (op->data.icon.width, env);
3766
rheight = parse_size_unchecked (op->data.icon.height, env);
3768
pixbuf = draw_op_as_pixbuf (op, widget, info,
3773
rx = parse_x_position_unchecked (op->data.icon.x, env);
3774
ry = parse_y_position_unchecked (op->data.icon.y, env);
3776
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
3779
g_object_unref (G_OBJECT (pixbuf));
3784
case META_DRAW_TITLE:
3785
if (info->title_layout)
3788
PangoRectangle ink_rect, logical_rect;
3790
meta_color_spec_render (op->data.title.color_spec, widget, &color);
3791
gdk_cairo_set_source_color (cr, &color);
3793
rx = parse_x_position_unchecked (op->data.title.x, env);
3794
ry = parse_y_position_unchecked (op->data.title.y, env);
3796
if (op->data.title.ellipsize_width)
3798
int ellipsize_width;
3801
ellipsize_width = parse_x_position_unchecked (op->data.title.ellipsize_width, env);
3802
/* HACK: parse_x_position_unchecked adds in env->rect.x, subtract out again */
3803
ellipsize_width -= env->rect.x;
3805
pango_layout_set_width (info->title_layout, -1);
3806
pango_layout_get_pixel_extents (info->title_layout,
3807
&ink_rect, &logical_rect);
3809
/* Pango's idea of ellipsization is with respect to the logical rect.
3810
* correct for this, by reducing the ellipsization width by the overflow
3811
* of the un-ellipsized text on the right... it's always the visual
3812
* right we want regardless of bidi, since since the X we pass in to
3813
* cairo_move_to() is always the left edge of the line.
3815
right_bearing = (ink_rect.x + ink_rect.width) - (logical_rect.x + logical_rect.width);
3816
right_bearing = MAX (right_bearing, 0);
3818
ellipsize_width -= right_bearing;
3819
ellipsize_width = MAX (ellipsize_width, 0);
3821
/* Only ellipsizing when necessary is a performance optimization -
3822
* pango_layout_set_width() will force a relayout if it isn't the
3823
* same as the current width of -1.
3825
if (ellipsize_width < logical_rect.width)
3826
pango_layout_set_width (info->title_layout, PANGO_SCALE * ellipsize_width);
3829
cairo_move_to (cr, rx, ry);
3830
pango_cairo_show_layout (cr, info->title_layout);
3832
/* Remove any ellipsization we might have set; will short-circuit
3833
* if the width is already -1 */
3834
pango_layout_set_width (info->title_layout, -1);
3838
case META_DRAW_OP_LIST:
3840
MetaRectangle d_rect;
3842
d_rect.x = parse_x_position_unchecked (op->data.op_list.x, env);
3843
d_rect.y = parse_y_position_unchecked (op->data.op_list.y, env);
3844
d_rect.width = parse_size_unchecked (op->data.op_list.width, env);
3845
d_rect.height = parse_size_unchecked (op->data.op_list.height, env);
3847
meta_draw_op_list_draw_with_style (op->data.op_list.op_list,
3848
style_gtk, widget, cr, info,
3853
case META_DRAW_TILE:
3855
int rx, ry, rwidth, rheight;
3856
int tile_xoffset, tile_yoffset;
3859
rx = parse_x_position_unchecked (op->data.tile.x, env);
3860
ry = parse_y_position_unchecked (op->data.tile.y, env);
3861
rwidth = parse_size_unchecked (op->data.tile.width, env);
3862
rheight = parse_size_unchecked (op->data.tile.height, env);
3866
cairo_rectangle (cr, rx, ry, rwidth, rheight);
3869
tile_xoffset = parse_x_position_unchecked (op->data.tile.tile_xoffset, env);
3870
tile_yoffset = parse_y_position_unchecked (op->data.tile.tile_yoffset, env);
3871
/* tile offset should not include x/y */
3872
tile_xoffset -= rect.x;
3873
tile_yoffset -= rect.y;
3875
tile.width = parse_size_unchecked (op->data.tile.tile_width, env);
3876
tile.height = parse_size_unchecked (op->data.tile.tile_height, env);
3878
tile.x = rx - tile_xoffset;
3880
while (tile.x < (rx + rwidth))
3882
tile.y = ry - tile_yoffset;
3883
while (tile.y < (ry + rheight))
3885
meta_draw_op_list_draw_with_style (op->data.tile.op_list,
3886
style_gtk, widget, cr, info,
3889
tile.y += tile.height;
3892
tile.x += tile.width;
3904
meta_draw_op_draw_with_style (const MetaDrawOp *op,
3905
GtkStyle *style_gtk,
3908
const MetaDrawInfo *info,
3909
MetaRectangle logical_region)
3911
MetaPositionExprEnv env;
3913
fill_env (&env, info, logical_region);
3915
meta_draw_op_draw_with_env (op, style_gtk, widget, cr,
3916
info, logical_region,
3922
meta_draw_op_draw (const MetaDrawOp *op,
3925
const MetaDrawInfo *info,
3926
MetaRectangle logical_region)
3928
meta_draw_op_draw_with_style (op, gtk_widget_get_style (widget), widget,
3929
cr, info, logical_region);
3933
* meta_draw_op_list_new: (skip)
3937
meta_draw_op_list_new (int n_preallocs)
3939
MetaDrawOpList *op_list;
3941
g_return_val_if_fail (n_preallocs >= 0, NULL);
3943
op_list = g_new (MetaDrawOpList, 1);
3945
op_list->refcount = 1;
3946
op_list->n_allocated = n_preallocs;
3947
op_list->ops = g_new (MetaDrawOp*, op_list->n_allocated);
3954
meta_draw_op_list_ref (MetaDrawOpList *op_list)
3956
g_return_if_fail (op_list != NULL);
3958
op_list->refcount += 1;
3962
meta_draw_op_list_unref (MetaDrawOpList *op_list)
3964
g_return_if_fail (op_list != NULL);
3965
g_return_if_fail (op_list->refcount > 0);
3967
op_list->refcount -= 1;
3969
if (op_list->refcount == 0)
3973
for (i = 0; i < op_list->n_ops; i++)
3974
meta_draw_op_free (op_list->ops[i]);
3976
g_free (op_list->ops);
3978
DEBUG_FILL_STRUCT (op_list);
3984
meta_draw_op_list_draw_with_style (const MetaDrawOpList *op_list,
3985
GtkStyle *style_gtk,
3988
const MetaDrawInfo *info,
3992
MetaPositionExprEnv env;
3994
if (op_list->n_ops == 0)
3997
fill_env (&env, info, rect);
3999
/* FIXME this can be optimized, potentially a lot, by
4000
* compressing multiple ops when possible. For example,
4001
* anything convertible to a pixbuf can be composited
4002
* client-side, and putting a color tint over a pixbuf
4003
* can be done without creating the solid-color pixbuf.
4005
* To implement this my plan is to have the idea of a
4006
* compiled draw op (with the string expressions already
4007
* evaluated), we make an array of those, and then fold
4008
* adjacent items when possible.
4013
for (i = 0; i < op_list->n_ops; i++)
4015
MetaDrawOp *op = op_list->ops[i];
4017
if (op->type == META_DRAW_CLIP)
4021
cairo_rectangle (cr,
4022
parse_x_position_unchecked (op->data.clip.x, &env),
4023
parse_y_position_unchecked (op->data.clip.y, &env),
4024
parse_size_unchecked (op->data.clip.width, &env),
4025
parse_size_unchecked (op->data.clip.height, &env));
4030
else if (gdk_cairo_get_clip_rectangle (cr, NULL))
4032
meta_draw_op_draw_with_env (op,
4033
style_gtk, widget, cr, info,
4043
meta_draw_op_list_draw (const MetaDrawOpList *op_list,
4046
const MetaDrawInfo *info,
4050
meta_draw_op_list_draw_with_style (op_list, gtk_widget_get_style (widget), widget,
4055
meta_draw_op_list_append (MetaDrawOpList *op_list,
4058
if (op_list->n_ops == op_list->n_allocated)
4060
op_list->n_allocated *= 2;
4061
op_list->ops = g_renew (MetaDrawOp*, op_list->ops, op_list->n_allocated);
4064
op_list->ops[op_list->n_ops] = op;
4065
op_list->n_ops += 1;
4069
meta_draw_op_list_validate (MetaDrawOpList *op_list,
4072
g_return_val_if_fail (op_list != NULL, FALSE);
4074
/* empty lists are OK, nothing else to check really */
4079
/* This is not done in validate, since we wouldn't know the name
4080
* of the list to report the error. It might be nice to
4081
* store names inside the list sometime.
4084
meta_draw_op_list_contains (MetaDrawOpList *op_list,
4085
MetaDrawOpList *child)
4089
/* mmm, huge tree recursion */
4091
for (i = 0; i < op_list->n_ops; i++)
4093
if (op_list->ops[i]->type == META_DRAW_OP_LIST)
4095
if (op_list->ops[i]->data.op_list.op_list == child)
4098
if (meta_draw_op_list_contains (op_list->ops[i]->data.op_list.op_list,
4102
else if (op_list->ops[i]->type == META_DRAW_TILE)
4104
if (op_list->ops[i]->data.tile.op_list == child)
4107
if (meta_draw_op_list_contains (op_list->ops[i]->data.tile.op_list,
4117
* Constructor for a MetaFrameStyle.
4119
* \param parent The parent style. Data not filled in here will be
4120
* looked for in the parent style, and in its parent
4123
* \return The newly-constructed style.
4126
meta_frame_style_new (MetaFrameStyle *parent)
4128
MetaFrameStyle *style;
4130
style = g_new0 (MetaFrameStyle, 1);
4132
style->refcount = 1;
4134
/* Default alpha is fully opaque */
4135
style->window_background_alpha = 255;
4137
style->parent = parent;
4139
meta_frame_style_ref (parent);
4145
* Increases the reference count of a frame style.
4146
* If the style is NULL, this is a no-op.
4148
* \param style The style.
4151
meta_frame_style_ref (MetaFrameStyle *style)
4153
g_return_if_fail (style != NULL);
4155
style->refcount += 1;
4159
free_button_ops (MetaDrawOpList *op_lists[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST])
4163
for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
4164
for (j = 0; j < META_BUTTON_STATE_LAST; j++)
4166
meta_draw_op_list_unref (op_lists[i][j]);
4170
meta_frame_style_unref (MetaFrameStyle *style)
4172
g_return_if_fail (style != NULL);
4173
g_return_if_fail (style->refcount > 0);
4175
style->refcount -= 1;
4177
if (style->refcount == 0)
4181
free_button_ops (style->buttons);
4183
for (i = 0; i < META_FRAME_PIECE_LAST; i++)
4184
if (style->pieces[i])
4185
meta_draw_op_list_unref (style->pieces[i]);
4188
meta_frame_layout_unref (style->layout);
4190
if (style->window_background_color)
4191
meta_color_spec_free (style->window_background_color);
4193
/* we hold a reference to any parent style */
4195
meta_frame_style_unref (style->parent);
4197
DEBUG_FILL_STRUCT (style);
4202
static MetaDrawOpList*
4203
get_button (MetaFrameStyle *style,
4204
MetaButtonType type,
4205
MetaButtonState state)
4207
MetaDrawOpList *op_list;
4208
MetaFrameStyle *parent;
4212
while (parent && op_list == NULL)
4214
op_list = parent->buttons[type][state];
4215
parent = parent->parent;
4218
/* We fall back to middle button backgrounds if we don't
4219
* have the ones on the sides
4222
if (op_list == NULL &&
4223
(type == META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND ||
4224
type == META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND))
4225
return get_button (style, META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND,
4228
if (op_list == NULL &&
4229
(type == META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND ||
4230
type == META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND))
4231
return get_button (style, META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND,
4234
/* We fall back to normal if no prelight */
4235
if (op_list == NULL &&
4236
state == META_BUTTON_STATE_PRELIGHT)
4237
return get_button (style, type, META_BUTTON_STATE_NORMAL);
4243
meta_frame_style_validate (MetaFrameStyle *style,
4244
guint current_theme_version,
4249
g_return_val_if_fail (style != NULL, FALSE);
4250
g_return_val_if_fail (style->layout != NULL, FALSE);
4252
for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
4254
/* for now the "positional" buttons are optional */
4255
if (i >= META_BUTTON_TYPE_CLOSE)
4257
for (j = 0; j < META_BUTTON_STATE_LAST; j++)
4259
if (get_button (style, i, j) == NULL &&
4260
meta_theme_earliest_version_with_button (i) <= current_theme_version
4263
g_set_error (error, META_THEME_ERROR,
4264
META_THEME_ERROR_FAILED,
4265
_("<button function=\"%s\" state=\"%s\" draw_ops=\"whatever\"/> must be specified for this frame style"),
4266
meta_button_type_to_string (i),
4267
meta_button_state_to_string (j));
4278
button_rect (MetaButtonType type,
4279
const MetaFrameGeometry *fgeom,
4280
int middle_background_offset,
4285
case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
4286
*rect = fgeom->left_left_background;
4289
case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
4290
*rect = fgeom->left_middle_backgrounds[middle_background_offset];
4293
case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
4294
*rect = fgeom->left_right_background;
4297
case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
4298
*rect = fgeom->right_left_background;
4301
case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
4302
*rect = fgeom->right_middle_backgrounds[middle_background_offset];
4305
case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
4306
*rect = fgeom->right_right_background;
4309
case META_BUTTON_TYPE_CLOSE:
4310
*rect = fgeom->close_rect.visible;
4313
case META_BUTTON_TYPE_SHADE:
4314
*rect = fgeom->shade_rect.visible;
4317
case META_BUTTON_TYPE_UNSHADE:
4318
*rect = fgeom->unshade_rect.visible;
4321
case META_BUTTON_TYPE_ABOVE:
4322
*rect = fgeom->above_rect.visible;
4325
case META_BUTTON_TYPE_UNABOVE:
4326
*rect = fgeom->unabove_rect.visible;
4329
case META_BUTTON_TYPE_STICK:
4330
*rect = fgeom->stick_rect.visible;
4333
case META_BUTTON_TYPE_UNSTICK:
4334
*rect = fgeom->unstick_rect.visible;
4337
case META_BUTTON_TYPE_MAXIMIZE:
4338
*rect = fgeom->max_rect.visible;
4341
case META_BUTTON_TYPE_MINIMIZE:
4342
*rect = fgeom->min_rect.visible;
4345
case META_BUTTON_TYPE_MENU:
4346
*rect = fgeom->menu_rect.visible;
4349
case META_BUTTON_TYPE_LAST:
4350
g_assert_not_reached ();
4356
meta_frame_style_draw_with_style (MetaFrameStyle *style,
4357
GtkStyle *style_gtk,
4360
const MetaFrameGeometry *fgeom,
4363
PangoLayout *title_layout,
4365
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
4366
GdkPixbuf *mini_icon,
4370
GdkRectangle titlebar_rect;
4371
GdkRectangle left_titlebar_edge;
4372
GdkRectangle right_titlebar_edge;
4373
GdkRectangle bottom_titlebar_edge;
4374
GdkRectangle top_titlebar_edge;
4375
GdkRectangle left_edge, right_edge, bottom_edge;
4376
PangoRectangle logical_rect;
4377
MetaDrawInfo draw_info;
4379
titlebar_rect.x = 0;
4380
titlebar_rect.y = 0;
4381
titlebar_rect.width = fgeom->width;
4382
titlebar_rect.height = fgeom->top_height;
4384
left_titlebar_edge.x = titlebar_rect.x;
4385
left_titlebar_edge.y = titlebar_rect.y + fgeom->top_titlebar_edge;
4386
left_titlebar_edge.width = fgeom->left_titlebar_edge;
4387
left_titlebar_edge.height = titlebar_rect.height - fgeom->top_titlebar_edge - fgeom->bottom_titlebar_edge;
4389
right_titlebar_edge.y = left_titlebar_edge.y;
4390
right_titlebar_edge.height = left_titlebar_edge.height;
4391
right_titlebar_edge.width = fgeom->right_titlebar_edge;
4392
right_titlebar_edge.x = titlebar_rect.x + titlebar_rect.width - right_titlebar_edge.width;
4394
top_titlebar_edge.x = titlebar_rect.x;
4395
top_titlebar_edge.y = titlebar_rect.y;
4396
top_titlebar_edge.width = titlebar_rect.width;
4397
top_titlebar_edge.height = fgeom->top_titlebar_edge;
4399
bottom_titlebar_edge.x = titlebar_rect.x;
4400
bottom_titlebar_edge.width = titlebar_rect.width;
4401
bottom_titlebar_edge.height = fgeom->bottom_titlebar_edge;
4402
bottom_titlebar_edge.y = titlebar_rect.y + titlebar_rect.height - bottom_titlebar_edge.height;
4405
left_edge.y = fgeom->top_height;
4406
left_edge.width = fgeom->left_width;
4407
left_edge.height = fgeom->height - fgeom->top_height - fgeom->bottom_height;
4409
right_edge.x = fgeom->width - fgeom->right_width;
4410
right_edge.y = fgeom->top_height;
4411
right_edge.width = fgeom->right_width;
4412
right_edge.height = fgeom->height - fgeom->top_height - fgeom->bottom_height;
4415
bottom_edge.y = fgeom->height - fgeom->bottom_height;
4416
bottom_edge.width = fgeom->width;
4417
bottom_edge.height = fgeom->bottom_height;
4420
pango_layout_get_pixel_extents (title_layout,
4421
NULL, &logical_rect);
4423
draw_info.mini_icon = mini_icon;
4424
draw_info.icon = icon;
4425
draw_info.title_layout = title_layout;
4426
draw_info.title_layout_width = title_layout ? logical_rect.width : 0;
4427
draw_info.title_layout_height = title_layout ? logical_rect.height : 0;
4428
draw_info.fgeom = fgeom;
4430
/* The enum is in the order the pieces should be rendered. */
4432
while (i < META_FRAME_PIECE_LAST)
4436
switch ((MetaFramePiece) i)
4438
case META_FRAME_PIECE_ENTIRE_BACKGROUND:
4441
rect.width = fgeom->width;
4442
rect.height = fgeom->height;
4445
case META_FRAME_PIECE_TITLEBAR:
4446
rect = titlebar_rect;
4449
case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE:
4450
rect = left_titlebar_edge;
4453
case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE:
4454
rect = right_titlebar_edge;
4457
case META_FRAME_PIECE_TOP_TITLEBAR_EDGE:
4458
rect = top_titlebar_edge;
4461
case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE:
4462
rect = bottom_titlebar_edge;
4465
case META_FRAME_PIECE_TITLEBAR_MIDDLE:
4466
rect.x = left_titlebar_edge.x + left_titlebar_edge.width;
4467
rect.y = top_titlebar_edge.y + top_titlebar_edge.height;
4468
rect.width = titlebar_rect.width - left_titlebar_edge.width -
4469
right_titlebar_edge.width;
4470
rect.height = titlebar_rect.height - top_titlebar_edge.height - bottom_titlebar_edge.height;
4473
case META_FRAME_PIECE_TITLE:
4474
rect = fgeom->title_rect;
4477
case META_FRAME_PIECE_LEFT_EDGE:
4481
case META_FRAME_PIECE_RIGHT_EDGE:
4485
case META_FRAME_PIECE_BOTTOM_EDGE:
4489
case META_FRAME_PIECE_OVERLAY:
4492
rect.width = fgeom->width;
4493
rect.height = fgeom->height;
4496
case META_FRAME_PIECE_LAST:
4497
g_assert_not_reached ();
4503
gdk_cairo_rectangle (cr, &rect);
4506
if (gdk_cairo_get_clip_rectangle (cr, NULL))
4508
MetaDrawOpList *op_list;
4509
MetaFrameStyle *parent;
4513
while (parent && op_list == NULL)
4515
op_list = parent->pieces[i];
4516
parent = parent->parent;
4521
MetaRectangle m_rect;
4522
m_rect = meta_rect (rect.x, rect.y, rect.width, rect.height);
4523
meta_draw_op_list_draw_with_style (op_list,
4534
/* Draw buttons just before overlay */
4535
if ((i + 1) == META_FRAME_PIECE_OVERLAY)
4537
MetaDrawOpList *op_list;
4538
int middle_bg_offset;
4540
middle_bg_offset = 0;
4542
while (j < META_BUTTON_TYPE_LAST)
4544
button_rect (j, fgeom, middle_bg_offset, &rect);
4546
op_list = get_button (style, j, button_states[j]);
4551
gdk_cairo_rectangle (cr, &rect);
4554
if (gdk_cairo_get_clip_rectangle (cr, NULL))
4556
MetaRectangle m_rect;
4558
m_rect = meta_rect (rect.x, rect.y,
4559
rect.width, rect.height);
4561
meta_draw_op_list_draw_with_style (op_list,
4572
/* MIDDLE_BACKGROUND type may get drawn more than once */
4573
if ((j == META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND ||
4574
j == META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND) &&
4575
(middle_bg_offset < (MAX_MIDDLE_BACKGROUNDS - 1)))
4581
middle_bg_offset = 0;
4592
meta_frame_style_draw (MetaFrameStyle *style,
4595
const MetaFrameGeometry *fgeom,
4598
PangoLayout *title_layout,
4600
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
4601
GdkPixbuf *mini_icon,
4604
meta_frame_style_draw_with_style (style, gtk_widget_get_style (widget), widget,
4605
cr, fgeom, client_width, client_height,
4606
title_layout, text_height,
4607
button_states, mini_icon, icon);
4611
meta_frame_style_set_new (MetaFrameStyleSet *parent)
4613
MetaFrameStyleSet *style_set;
4615
style_set = g_new0 (MetaFrameStyleSet, 1);
4617
style_set->parent = parent;
4619
meta_frame_style_set_ref (parent);
4621
style_set->refcount = 1;
4627
free_focus_styles (MetaFrameStyle *focus_styles[META_FRAME_FOCUS_LAST])
4631
for (i = 0; i < META_FRAME_FOCUS_LAST; i++)
4632
if (focus_styles[i])
4633
meta_frame_style_unref (focus_styles[i]);
4637
meta_frame_style_set_ref (MetaFrameStyleSet *style_set)
4639
g_return_if_fail (style_set != NULL);
4641
style_set->refcount += 1;
4645
meta_frame_style_set_unref (MetaFrameStyleSet *style_set)
4647
g_return_if_fail (style_set != NULL);
4648
g_return_if_fail (style_set->refcount > 0);
4650
style_set->refcount -= 1;
4652
if (style_set->refcount == 0)
4656
for (i = 0; i < META_FRAME_RESIZE_LAST; i++)
4658
free_focus_styles (style_set->normal_styles[i]);
4659
free_focus_styles (style_set->shaded_styles[i]);
4662
free_focus_styles (style_set->maximized_styles);
4663
free_focus_styles (style_set->maximized_and_shaded_styles);
4665
if (style_set->parent)
4666
meta_frame_style_set_unref (style_set->parent);
4668
DEBUG_FILL_STRUCT (style_set);
4674
static MetaFrameStyle*
4675
get_style (MetaFrameStyleSet *style_set,
4676
MetaFrameState state,
4677
MetaFrameResize resize,
4678
MetaFrameFocus focus)
4680
MetaFrameStyle *style;
4686
case META_FRAME_STATE_NORMAL:
4687
case META_FRAME_STATE_SHADED:
4689
if (state == META_FRAME_STATE_SHADED)
4690
style = style_set->shaded_styles[resize][focus];
4692
style = style_set->normal_styles[resize][focus];
4694
/* Try parent if we failed here */
4695
if (style == NULL && style_set->parent)
4696
style = get_style (style_set->parent, state, resize, focus);
4698
/* Allow people to omit the vert/horz/none resize modes */
4699
if (style == NULL &&
4700
resize != META_FRAME_RESIZE_BOTH)
4701
style = get_style (style_set, state, META_FRAME_RESIZE_BOTH, focus);
4706
MetaFrameStyle **styles;
4712
case META_FRAME_STATE_MAXIMIZED:
4713
styles = style_set->maximized_styles;
4715
case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
4716
styles = style_set->maximized_and_shaded_styles;
4718
case META_FRAME_STATE_NORMAL:
4719
case META_FRAME_STATE_SHADED:
4720
case META_FRAME_STATE_LAST:
4721
g_assert_not_reached ();
4725
style = styles[focus];
4727
/* Try parent if we failed here */
4728
if (style == NULL && style_set->parent)
4729
style = get_style (style_set->parent, state, resize, focus);
4737
check_state (MetaFrameStyleSet *style_set,
4738
MetaFrameState state,
4743
for (i = 0; i < META_FRAME_FOCUS_LAST; i++)
4745
if (get_style (style_set, state,
4746
META_FRAME_RESIZE_NONE, i) == NULL)
4748
/* Translators: This error occurs when a <frame> tag is missing
4749
* in theme XML. The "<frame ...>" is intended as a noun phrase,
4750
* and the "missing" qualifies it. You should translate "whatever".
4752
g_set_error (error, META_THEME_ERROR,
4753
META_THEME_ERROR_FAILED,
4754
_("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
4755
meta_frame_state_to_string (state),
4756
meta_frame_resize_to_string (META_FRAME_RESIZE_NONE),
4757
meta_frame_focus_to_string (i));
4766
meta_frame_style_set_validate (MetaFrameStyleSet *style_set,
4771
g_return_val_if_fail (style_set != NULL, FALSE);
4773
for (i = 0; i < META_FRAME_RESIZE_LAST; i++)
4774
for (j = 0; j < META_FRAME_FOCUS_LAST; j++)
4775
if (get_style (style_set, META_FRAME_STATE_NORMAL, i, j) == NULL)
4777
g_set_error (error, META_THEME_ERROR,
4778
META_THEME_ERROR_FAILED,
4779
_("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
4780
meta_frame_state_to_string (META_FRAME_STATE_NORMAL),
4781
meta_frame_resize_to_string (i),
4782
meta_frame_focus_to_string (j));
4786
if (!check_state (style_set, META_FRAME_STATE_SHADED, error))
4789
if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED, error))
4792
if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED_AND_SHADED, error))
4799
* meta_theme_get_current: (skip)
4803
meta_theme_get_current (void)
4805
return meta_current_theme;
4809
meta_theme_set_current (const char *name,
4810
gboolean force_reload)
4812
MetaTheme *new_theme;
4815
meta_topic (META_DEBUG_THEMES, "Setting current theme to \"%s\"\n", name);
4817
if (!force_reload &&
4818
meta_current_theme &&
4819
strcmp (name, meta_current_theme->name) == 0)
4823
new_theme = meta_theme_load (name, &err);
4825
if (new_theme == NULL)
4827
meta_warning (_("Failed to load theme \"%s\": %s\n"),
4828
name, err->message);
4833
if (meta_current_theme)
4834
meta_theme_free (meta_current_theme);
4836
meta_current_theme = new_theme;
4838
meta_topic (META_DEBUG_THEMES, "New theme is \"%s\"\n", meta_current_theme->name);
4843
* meta_theme_new: (skip)
4847
meta_theme_new (void)
4851
theme = g_new0 (MetaTheme, 1);
4853
theme->images_by_filename =
4854
g_hash_table_new_full (g_str_hash,
4857
(GDestroyNotify) g_object_unref);
4859
theme->layouts_by_name =
4860
g_hash_table_new_full (g_str_hash,
4863
(GDestroyNotify) meta_frame_layout_unref);
4865
theme->draw_op_lists_by_name =
4866
g_hash_table_new_full (g_str_hash,
4869
(GDestroyNotify) meta_draw_op_list_unref);
4871
theme->styles_by_name =
4872
g_hash_table_new_full (g_str_hash,
4875
(GDestroyNotify) meta_frame_style_unref);
4877
theme->style_sets_by_name =
4878
g_hash_table_new_full (g_str_hash,
4881
(GDestroyNotify) meta_frame_style_set_unref);
4883
/* Create our variable quarks so we can look up variables without
4884
having to strcmp for the names */
4885
theme->quark_width = g_quark_from_static_string ("width");
4886
theme->quark_height = g_quark_from_static_string ("height");
4887
theme->quark_object_width = g_quark_from_static_string ("object_width");
4888
theme->quark_object_height = g_quark_from_static_string ("object_height");
4889
theme->quark_left_width = g_quark_from_static_string ("left_width");
4890
theme->quark_right_width = g_quark_from_static_string ("right_width");
4891
theme->quark_top_height = g_quark_from_static_string ("top_height");
4892
theme->quark_bottom_height = g_quark_from_static_string ("bottom_height");
4893
theme->quark_mini_icon_width = g_quark_from_static_string ("mini_icon_width");
4894
theme->quark_mini_icon_height = g_quark_from_static_string ("mini_icon_height");
4895
theme->quark_icon_width = g_quark_from_static_string ("icon_width");
4896
theme->quark_icon_height = g_quark_from_static_string ("icon_height");
4897
theme->quark_title_width = g_quark_from_static_string ("title_width");
4898
theme->quark_title_height = g_quark_from_static_string ("title_height");
4899
theme->quark_frame_x_center = g_quark_from_static_string ("frame_x_center");
4900
theme->quark_frame_y_center = g_quark_from_static_string ("frame_y_center");
4906
meta_theme_free (MetaTheme *theme)
4910
g_return_if_fail (theme != NULL);
4912
g_free (theme->name);
4913
g_free (theme->dirname);
4914
g_free (theme->filename);
4915
g_free (theme->readable_name);
4916
g_free (theme->date);
4917
g_free (theme->description);
4918
g_free (theme->author);
4919
g_free (theme->copyright);
4921
/* be more careful when destroying the theme hash tables,
4922
since they are only constructed as needed, and may be NULL. */
4923
if (theme->integer_constants)
4924
g_hash_table_destroy (theme->integer_constants);
4925
if (theme->images_by_filename)
4926
g_hash_table_destroy (theme->images_by_filename);
4927
if (theme->layouts_by_name)
4928
g_hash_table_destroy (theme->layouts_by_name);
4929
if (theme->draw_op_lists_by_name)
4930
g_hash_table_destroy (theme->draw_op_lists_by_name);
4931
if (theme->styles_by_name)
4932
g_hash_table_destroy (theme->styles_by_name);
4933
if (theme->style_sets_by_name)
4934
g_hash_table_destroy (theme->style_sets_by_name);
4936
for (i = 0; i < META_FRAME_TYPE_LAST; i++)
4937
if (theme->style_sets_by_type[i])
4938
meta_frame_style_set_unref (theme->style_sets_by_type[i]);
4940
DEBUG_FILL_STRUCT (theme);
4945
meta_theme_validate (MetaTheme *theme,
4950
g_return_val_if_fail (theme != NULL, FALSE);
4952
/* FIXME what else should be checked? */
4954
g_assert (theme->name);
4956
if (theme->readable_name == NULL)
4958
/* Translators: This error means that a necessary XML tag (whose name
4959
* is given in angle brackets) was not found in a given theme (whose
4960
* name is given second, in quotation marks).
4962
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4963
_("No <%s> set for theme \"%s\""), "name", theme->name);
4967
if (theme->author == NULL)
4969
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4970
_("No <%s> set for theme \"%s\""), "author", theme->name);
4974
if (theme->date == NULL)
4976
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4977
_("No <%s> set for theme \"%s\""), "date", theme->name);
4981
if (theme->description == NULL)
4983
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4984
_("No <%s> set for theme \"%s\""), "description", theme->name);
4988
if (theme->copyright == NULL)
4990
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4991
_("No <%s> set for theme \"%s\""), "copyright", theme->name);
4995
for (i = 0; i < (int)META_FRAME_TYPE_LAST; i++)
4996
if (i != (int)META_FRAME_TYPE_ATTACHED && theme->style_sets_by_type[i] == NULL)
4998
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4999
_("No frame style set for window type \"%s\" in theme \"%s\", add a <window type=\"%s\" style_set=\"whatever\"/> element"),
5000
meta_frame_type_to_string (i),
5002
meta_frame_type_to_string (i));
5011
* meta_theme_load_image: (skip)
5015
meta_theme_load_image (MetaTheme *theme,
5016
const char *filename,
5017
guint size_of_theme_icons,
5022
pixbuf = g_hash_table_lookup (theme->images_by_filename,
5028
if (g_str_has_prefix (filename, "theme:") &&
5029
META_THEME_ALLOWS (theme, META_THEME_IMAGES_FROM_ICON_THEMES))
5031
pixbuf = gtk_icon_theme_load_icon (
5032
gtk_icon_theme_get_default (),
5034
size_of_theme_icons,
5037
if (pixbuf == NULL) return NULL;
5042
full_path = g_build_filename (theme->dirname, filename, NULL);
5044
pixbuf = gdk_pixbuf_new_from_file (full_path, error);
5053
g_hash_table_replace (theme->images_by_filename,
5054
g_strdup (filename),
5060
g_object_ref (G_OBJECT (pixbuf));
5065
static MetaFrameStyle*
5066
theme_get_style (MetaTheme *theme,
5068
MetaFrameFlags flags)
5070
MetaFrameState state;
5071
MetaFrameResize resize;
5072
MetaFrameFocus focus;
5073
MetaFrameStyle *style;
5074
MetaFrameStyleSet *style_set;
5076
style_set = theme->style_sets_by_type[type];
5078
if (style_set == NULL && type == META_FRAME_TYPE_ATTACHED)
5079
style_set = theme->style_sets_by_type[META_FRAME_TYPE_BORDER];
5081
/* Right now the parser forces a style set for all other types,
5082
* but this fallback code is here in case I take that out.
5084
if (style_set == NULL)
5085
style_set = theme->style_sets_by_type[META_FRAME_TYPE_NORMAL];
5086
if (style_set == NULL)
5089
switch (flags & (META_FRAME_MAXIMIZED | META_FRAME_SHADED))
5092
state = META_FRAME_STATE_NORMAL;
5094
case META_FRAME_MAXIMIZED:
5095
state = META_FRAME_STATE_MAXIMIZED;
5097
case META_FRAME_SHADED:
5098
state = META_FRAME_STATE_SHADED;
5100
case (META_FRAME_MAXIMIZED | META_FRAME_SHADED):
5101
state = META_FRAME_STATE_MAXIMIZED_AND_SHADED;
5104
g_assert_not_reached ();
5105
state = META_FRAME_STATE_LAST; /* compiler */
5109
switch (flags & (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE))
5112
resize = META_FRAME_RESIZE_NONE;
5114
case META_FRAME_ALLOWS_VERTICAL_RESIZE:
5115
resize = META_FRAME_RESIZE_VERTICAL;
5117
case META_FRAME_ALLOWS_HORIZONTAL_RESIZE:
5118
resize = META_FRAME_RESIZE_HORIZONTAL;
5120
case (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE):
5121
resize = META_FRAME_RESIZE_BOTH;
5124
g_assert_not_reached ();
5125
resize = META_FRAME_RESIZE_LAST; /* compiler */
5129
/* re invert the styles used for focus/unfocussed while flashing a frame */
5130
if (((flags & META_FRAME_HAS_FOCUS) && !(flags & META_FRAME_IS_FLASHING))
5131
|| (!(flags & META_FRAME_HAS_FOCUS) && (flags & META_FRAME_IS_FLASHING)))
5132
focus = META_FRAME_FOCUS_YES;
5134
focus = META_FRAME_FOCUS_NO;
5136
style = get_style (style_set, state, resize, focus);
5142
meta_theme_get_frame_style (MetaTheme *theme,
5144
MetaFrameFlags flags)
5146
MetaFrameStyle *style;
5148
g_return_val_if_fail (type < META_FRAME_TYPE_LAST, NULL);
5150
style = theme_get_style (theme, type, flags);
5156
meta_theme_get_title_scale (MetaTheme *theme,
5158
MetaFrameFlags flags)
5160
MetaFrameStyle *style;
5162
g_return_val_if_fail (type < META_FRAME_TYPE_LAST, 1.0);
5164
style = theme_get_style (theme, type, flags);
5166
/* Parser is not supposed to allow this currently */
5170
return style->layout->title_scale;
5174
meta_theme_draw_frame_with_style (MetaTheme *theme,
5175
GtkStyle *style_gtk,
5179
MetaFrameFlags flags,
5182
PangoLayout *title_layout,
5184
const MetaButtonLayout *button_layout,
5185
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
5186
GdkPixbuf *mini_icon,
5189
MetaFrameGeometry fgeom;
5190
MetaFrameStyle *style;
5192
g_return_if_fail (type < META_FRAME_TYPE_LAST);
5194
style = theme_get_style (theme, type, flags);
5196
/* Parser is not supposed to allow this currently */
5200
meta_frame_layout_calc_geometry (style->layout,
5203
client_width, client_height,
5208
meta_frame_style_draw_with_style (style,
5213
client_width, client_height,
5221
meta_theme_draw_frame (MetaTheme *theme,
5225
MetaFrameFlags flags,
5228
PangoLayout *title_layout,
5230
const MetaButtonLayout *button_layout,
5231
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
5232
GdkPixbuf *mini_icon,
5235
meta_theme_draw_frame_with_style (theme, gtk_widget_get_style (widget), widget,
5237
client_width, client_height,
5238
title_layout, text_height,
5239
button_layout, button_states,
5244
meta_theme_draw_frame_by_name (MetaTheme *theme,
5247
const gchar *style_name,
5248
MetaFrameFlags flags,
5251
PangoLayout *title_layout,
5253
const MetaButtonLayout *button_layout,
5254
MetaButtonState button_states[META_BUTTON_TYPE_LAST],
5255
GdkPixbuf *mini_icon,
5258
MetaFrameGeometry fgeom;
5259
MetaFrameStyle *style;
5261
style = meta_theme_lookup_style (theme, style_name);
5263
/* Parser is not supposed to allow this currently */
5267
meta_frame_layout_calc_geometry (style->layout,
5270
client_width, client_height,
5275
meta_frame_style_draw (style,
5279
client_width, client_height,
5287
meta_theme_get_frame_borders (MetaTheme *theme,
5290
MetaFrameFlags flags,
5296
MetaFrameStyle *style;
5298
g_return_if_fail (type < META_FRAME_TYPE_LAST);
5309
style = theme_get_style (theme, type, flags);
5311
/* Parser is not supposed to allow this currently */
5315
meta_frame_layout_get_borders (style->layout,
5318
top_height, bottom_height,
5319
left_width, right_width);
5323
meta_theme_calc_geometry (MetaTheme *theme,
5326
MetaFrameFlags flags,
5329
const MetaButtonLayout *button_layout,
5330
MetaFrameGeometry *fgeom)
5332
MetaFrameStyle *style;
5334
g_return_if_fail (type < META_FRAME_TYPE_LAST);
5336
style = theme_get_style (theme, type, flags);
5338
/* Parser is not supposed to allow this currently */
5342
meta_frame_layout_calc_geometry (style->layout,
5345
client_width, client_height,
5352
meta_theme_lookup_layout (MetaTheme *theme,
5355
return g_hash_table_lookup (theme->layouts_by_name, name);
5359
meta_theme_insert_layout (MetaTheme *theme,
5361
MetaFrameLayout *layout)
5363
meta_frame_layout_ref (layout);
5364
g_hash_table_replace (theme->layouts_by_name, g_strdup (name), layout);
5368
meta_theme_lookup_draw_op_list (MetaTheme *theme,
5371
return g_hash_table_lookup (theme->draw_op_lists_by_name, name);
5375
meta_theme_insert_draw_op_list (MetaTheme *theme,
5377
MetaDrawOpList *op_list)
5379
meta_draw_op_list_ref (op_list);
5380
g_hash_table_replace (theme->draw_op_lists_by_name, g_strdup (name), op_list);
5384
meta_theme_lookup_style (MetaTheme *theme,
5387
return g_hash_table_lookup (theme->styles_by_name, name);
5391
meta_theme_insert_style (MetaTheme *theme,
5393
MetaFrameStyle *style)
5395
meta_frame_style_ref (style);
5396
g_hash_table_replace (theme->styles_by_name, g_strdup (name), style);
5400
meta_theme_lookup_style_set (MetaTheme *theme,
5403
return g_hash_table_lookup (theme->style_sets_by_name, name);
5407
meta_theme_insert_style_set (MetaTheme *theme,
5409
MetaFrameStyleSet *style_set)
5411
meta_frame_style_set_ref (style_set);
5412
g_hash_table_replace (theme->style_sets_by_name, g_strdup (name), style_set);
5416
first_uppercase (const char *str)
5418
return g_ascii_isupper (*str);
5422
meta_theme_define_int_constant (MetaTheme *theme,
5427
if (theme->integer_constants == NULL)
5428
theme->integer_constants = g_hash_table_new_full (g_str_hash,
5433
if (!first_uppercase (name))
5435
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5436
_("User-defined constants must begin with a capital letter; \"%s\" does not"),
5441
if (g_hash_table_lookup_extended (theme->integer_constants, name, NULL, NULL))
5443
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5444
_("Constant \"%s\" has already been defined"),
5450
g_hash_table_insert (theme->integer_constants,
5452
GINT_TO_POINTER (value));
5458
meta_theme_lookup_int_constant (MetaTheme *theme,
5466
if (theme->integer_constants == NULL)
5469
if (g_hash_table_lookup_extended (theme->integer_constants,
5470
name, NULL, &old_value))
5472
*value = GPOINTER_TO_INT (old_value);
5482
meta_theme_define_float_constant (MetaTheme *theme,
5489
if (theme->float_constants == NULL)
5490
theme->float_constants = g_hash_table_new_full (g_str_hash,
5495
if (!first_uppercase (name))
5497
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5498
_("User-defined constants must begin with a capital letter; \"%s\" does not"),
5503
if (g_hash_table_lookup_extended (theme->float_constants, name, NULL, NULL))
5505
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5506
_("Constant \"%s\" has already been defined"),
5512
d = g_new (double, 1);
5515
g_hash_table_insert (theme->float_constants,
5516
g_strdup (name), d);
5522
meta_theme_lookup_float_constant (MetaTheme *theme,
5530
if (theme->float_constants == NULL)
5533
d = g_hash_table_lookup (theme->float_constants, name);
5547
meta_theme_define_color_constant (MetaTheme *theme,
5552
if (theme->color_constants == NULL)
5553
theme->color_constants = g_hash_table_new_full (g_str_hash,
5558
if (!first_uppercase (name))
5560
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5561
_("User-defined constants must begin with a capital letter; \"%s\" does not"),
5566
if (g_hash_table_lookup_extended (theme->color_constants, name, NULL, NULL))
5568
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
5569
_("Constant \"%s\" has already been defined"),
5575
g_hash_table_insert (theme->color_constants,
5583
* Looks up a colour constant.
5585
* \param theme the theme containing the constant
5586
* \param name the name of the constant
5587
* \param value [out] the string representation of the colour, or NULL if it
5589
* \return TRUE if it exists, FALSE otherwise
5592
meta_theme_lookup_color_constant (MetaTheme *theme,
5600
if (theme->color_constants == NULL)
5603
result = g_hash_table_lookup (theme->color_constants, name);
5617
PangoFontDescription*
5618
meta_gtk_widget_get_font_desc (GtkWidget *widget,
5620
const PangoFontDescription *override)
5622
PangoFontDescription *font_desc;
5624
g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
5626
font_desc = pango_font_description_copy (gtk_widget_get_style (widget)->font_desc);
5629
pango_font_description_merge (font_desc, override, TRUE);
5631
pango_font_description_set_size (font_desc,
5632
MAX (pango_font_description_get_size (font_desc) * scale, 1));
5638
* Returns the height of the letters in a particular font.
5640
* \param font_desc the font
5641
* \param context the context of the font
5642
* \return the height of the letters
5645
meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc,
5646
PangoContext *context)
5648
PangoFontMetrics *metrics;
5649
PangoLanguage *lang;
5652
lang = pango_context_get_language (context);
5653
metrics = pango_context_get_metrics (context, font_desc, lang);
5655
retval = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
5656
pango_font_metrics_get_descent (metrics));
5658
pango_font_metrics_unref (metrics);
5663
MetaGtkColorComponent
5664
meta_color_component_from_string (const char *str)
5666
if (strcmp ("fg", str) == 0)
5667
return META_GTK_COLOR_FG;
5668
else if (strcmp ("bg", str) == 0)
5669
return META_GTK_COLOR_BG;
5670
else if (strcmp ("light", str) == 0)
5671
return META_GTK_COLOR_LIGHT;
5672
else if (strcmp ("dark", str) == 0)
5673
return META_GTK_COLOR_DARK;
5674
else if (strcmp ("mid", str) == 0)
5675
return META_GTK_COLOR_MID;
5676
else if (strcmp ("text", str) == 0)
5677
return META_GTK_COLOR_TEXT;
5678
else if (strcmp ("base", str) == 0)
5679
return META_GTK_COLOR_BASE;
5680
else if (strcmp ("text_aa", str) == 0)
5681
return META_GTK_COLOR_TEXT_AA;
5683
return META_GTK_COLOR_LAST;
5687
meta_color_component_to_string (MetaGtkColorComponent component)
5691
case META_GTK_COLOR_FG:
5693
case META_GTK_COLOR_BG:
5695
case META_GTK_COLOR_LIGHT:
5697
case META_GTK_COLOR_DARK:
5699
case META_GTK_COLOR_MID:
5701
case META_GTK_COLOR_TEXT:
5703
case META_GTK_COLOR_BASE:
5705
case META_GTK_COLOR_TEXT_AA:
5707
case META_GTK_COLOR_LAST:
5715
meta_button_state_from_string (const char *str)
5717
if (strcmp ("normal", str) == 0)
5718
return META_BUTTON_STATE_NORMAL;
5719
else if (strcmp ("pressed", str) == 0)
5720
return META_BUTTON_STATE_PRESSED;
5721
else if (strcmp ("prelight", str) == 0)
5722
return META_BUTTON_STATE_PRELIGHT;
5724
return META_BUTTON_STATE_LAST;
5728
meta_button_state_to_string (MetaButtonState state)
5732
case META_BUTTON_STATE_NORMAL:
5734
case META_BUTTON_STATE_PRESSED:
5736
case META_BUTTON_STATE_PRELIGHT:
5738
case META_BUTTON_STATE_LAST:
5746
meta_button_type_from_string (const char *str, MetaTheme *theme)
5748
if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS))
5750
if (strcmp ("shade", str) == 0)
5751
return META_BUTTON_TYPE_SHADE;
5752
else if (strcmp ("above", str) == 0)
5753
return META_BUTTON_TYPE_ABOVE;
5754
else if (strcmp ("stick", str) == 0)
5755
return META_BUTTON_TYPE_STICK;
5756
else if (strcmp ("unshade", str) == 0)
5757
return META_BUTTON_TYPE_UNSHADE;
5758
else if (strcmp ("unabove", str) == 0)
5759
return META_BUTTON_TYPE_UNABOVE;
5760
else if (strcmp ("unstick", str) == 0)
5761
return META_BUTTON_TYPE_UNSTICK;
5764
if (strcmp ("close", str) == 0)
5765
return META_BUTTON_TYPE_CLOSE;
5766
else if (strcmp ("maximize", str) == 0)
5767
return META_BUTTON_TYPE_MAXIMIZE;
5768
else if (strcmp ("minimize", str) == 0)
5769
return META_BUTTON_TYPE_MINIMIZE;
5770
else if (strcmp ("menu", str) == 0)
5771
return META_BUTTON_TYPE_MENU;
5772
else if (strcmp ("left_left_background", str) == 0)
5773
return META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND;
5774
else if (strcmp ("left_middle_background", str) == 0)
5775
return META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND;
5776
else if (strcmp ("left_right_background", str) == 0)
5777
return META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND;
5778
else if (strcmp ("right_left_background", str) == 0)
5779
return META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND;
5780
else if (strcmp ("right_middle_background", str) == 0)
5781
return META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND;
5782
else if (strcmp ("right_right_background", str) == 0)
5783
return META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND;
5785
return META_BUTTON_TYPE_LAST;
5789
meta_button_type_to_string (MetaButtonType type)
5793
case META_BUTTON_TYPE_CLOSE:
5795
case META_BUTTON_TYPE_MAXIMIZE:
5797
case META_BUTTON_TYPE_MINIMIZE:
5799
case META_BUTTON_TYPE_SHADE:
5801
case META_BUTTON_TYPE_ABOVE:
5803
case META_BUTTON_TYPE_STICK:
5805
case META_BUTTON_TYPE_UNSHADE:
5807
case META_BUTTON_TYPE_UNABOVE:
5809
case META_BUTTON_TYPE_UNSTICK:
5811
case META_BUTTON_TYPE_MENU:
5813
case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
5814
return "left_left_background";
5815
case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
5816
return "left_middle_background";
5817
case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
5818
return "left_right_background";
5819
case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
5820
return "right_left_background";
5821
case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
5822
return "right_middle_background";
5823
case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
5824
return "right_right_background";
5825
case META_BUTTON_TYPE_LAST:
5833
meta_frame_piece_from_string (const char *str)
5835
if (strcmp ("entire_background", str) == 0)
5836
return META_FRAME_PIECE_ENTIRE_BACKGROUND;
5837
else if (strcmp ("titlebar", str) == 0)
5838
return META_FRAME_PIECE_TITLEBAR;
5839
else if (strcmp ("titlebar_middle", str) == 0)
5840
return META_FRAME_PIECE_TITLEBAR_MIDDLE;
5841
else if (strcmp ("left_titlebar_edge", str) == 0)
5842
return META_FRAME_PIECE_LEFT_TITLEBAR_EDGE;
5843
else if (strcmp ("right_titlebar_edge", str) == 0)
5844
return META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE;
5845
else if (strcmp ("top_titlebar_edge", str) == 0)
5846
return META_FRAME_PIECE_TOP_TITLEBAR_EDGE;
5847
else if (strcmp ("bottom_titlebar_edge", str) == 0)
5848
return META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE;
5849
else if (strcmp ("title", str) == 0)
5850
return META_FRAME_PIECE_TITLE;
5851
else if (strcmp ("left_edge", str) == 0)
5852
return META_FRAME_PIECE_LEFT_EDGE;
5853
else if (strcmp ("right_edge", str) == 0)
5854
return META_FRAME_PIECE_RIGHT_EDGE;
5855
else if (strcmp ("bottom_edge", str) == 0)
5856
return META_FRAME_PIECE_BOTTOM_EDGE;
5857
else if (strcmp ("overlay", str) == 0)
5858
return META_FRAME_PIECE_OVERLAY;
5860
return META_FRAME_PIECE_LAST;
5864
meta_frame_piece_to_string (MetaFramePiece piece)
5868
case META_FRAME_PIECE_ENTIRE_BACKGROUND:
5869
return "entire_background";
5870
case META_FRAME_PIECE_TITLEBAR:
5872
case META_FRAME_PIECE_TITLEBAR_MIDDLE:
5873
return "titlebar_middle";
5874
case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE:
5875
return "left_titlebar_edge";
5876
case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE:
5877
return "right_titlebar_edge";
5878
case META_FRAME_PIECE_TOP_TITLEBAR_EDGE:
5879
return "top_titlebar_edge";
5880
case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE:
5881
return "bottom_titlebar_edge";
5882
case META_FRAME_PIECE_TITLE:
5884
case META_FRAME_PIECE_LEFT_EDGE:
5886
case META_FRAME_PIECE_RIGHT_EDGE:
5887
return "right_edge";
5888
case META_FRAME_PIECE_BOTTOM_EDGE:
5889
return "bottom_edge";
5890
case META_FRAME_PIECE_OVERLAY:
5892
case META_FRAME_PIECE_LAST:
5900
meta_frame_state_from_string (const char *str)
5902
if (strcmp ("normal", str) == 0)
5903
return META_FRAME_STATE_NORMAL;
5904
else if (strcmp ("maximized", str) == 0)
5905
return META_FRAME_STATE_MAXIMIZED;
5906
else if (strcmp ("shaded", str) == 0)
5907
return META_FRAME_STATE_SHADED;
5908
else if (strcmp ("maximized_and_shaded", str) == 0)
5909
return META_FRAME_STATE_MAXIMIZED_AND_SHADED;
5911
return META_FRAME_STATE_LAST;
5915
meta_frame_state_to_string (MetaFrameState state)
5919
case META_FRAME_STATE_NORMAL:
5921
case META_FRAME_STATE_MAXIMIZED:
5923
case META_FRAME_STATE_SHADED:
5925
case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
5926
return "maximized_and_shaded";
5927
case META_FRAME_STATE_LAST:
5935
meta_frame_resize_from_string (const char *str)
5937
if (strcmp ("none", str) == 0)
5938
return META_FRAME_RESIZE_NONE;
5939
else if (strcmp ("vertical", str) == 0)
5940
return META_FRAME_RESIZE_VERTICAL;
5941
else if (strcmp ("horizontal", str) == 0)
5942
return META_FRAME_RESIZE_HORIZONTAL;
5943
else if (strcmp ("both", str) == 0)
5944
return META_FRAME_RESIZE_BOTH;
5946
return META_FRAME_RESIZE_LAST;
5950
meta_frame_resize_to_string (MetaFrameResize resize)
5954
case META_FRAME_RESIZE_NONE:
5956
case META_FRAME_RESIZE_VERTICAL:
5958
case META_FRAME_RESIZE_HORIZONTAL:
5959
return "horizontal";
5960
case META_FRAME_RESIZE_BOTH:
5962
case META_FRAME_RESIZE_LAST:
5970
meta_frame_focus_from_string (const char *str)
5972
if (strcmp ("no", str) == 0)
5973
return META_FRAME_FOCUS_NO;
5974
else if (strcmp ("yes", str) == 0)
5975
return META_FRAME_FOCUS_YES;
5977
return META_FRAME_FOCUS_LAST;
5981
meta_frame_focus_to_string (MetaFrameFocus focus)
5985
case META_FRAME_FOCUS_NO:
5987
case META_FRAME_FOCUS_YES:
5989
case META_FRAME_FOCUS_LAST:
5997
meta_frame_type_from_string (const char *str)
5999
if (strcmp ("normal", str) == 0)
6000
return META_FRAME_TYPE_NORMAL;
6001
else if (strcmp ("dialog", str) == 0)
6002
return META_FRAME_TYPE_DIALOG;
6003
else if (strcmp ("modal_dialog", str) == 0)
6004
return META_FRAME_TYPE_MODAL_DIALOG;
6005
else if (strcmp ("utility", str) == 0)
6006
return META_FRAME_TYPE_UTILITY;
6007
else if (strcmp ("menu", str) == 0)
6008
return META_FRAME_TYPE_MENU;
6009
else if (strcmp ("border", str) == 0)
6010
return META_FRAME_TYPE_BORDER;
6011
else if (strcmp ("attached", str) == 0)
6012
return META_FRAME_TYPE_ATTACHED;
6014
else if (strcmp ("toolbar", str) == 0)
6015
return META_FRAME_TYPE_TOOLBAR;
6018
return META_FRAME_TYPE_LAST;
6022
* meta_frame_type_to_string:
6024
* Converts a frame type enum value to the name string that would
6025
* appear in the theme definition file.
6027
* Return value: the string value
6030
meta_frame_type_to_string (MetaFrameType type)
6034
case META_FRAME_TYPE_NORMAL:
6036
case META_FRAME_TYPE_DIALOG:
6038
case META_FRAME_TYPE_MODAL_DIALOG:
6039
return "modal_dialog";
6040
case META_FRAME_TYPE_UTILITY:
6042
case META_FRAME_TYPE_MENU:
6044
case META_FRAME_TYPE_BORDER:
6046
case META_FRAME_TYPE_ATTACHED:
6049
case META_FRAME_TYPE_TOOLBAR:
6052
case META_FRAME_TYPE_LAST:
6060
meta_gradient_type_from_string (const char *str)
6062
if (strcmp ("vertical", str) == 0)
6063
return META_GRADIENT_VERTICAL;
6064
else if (strcmp ("horizontal", str) == 0)
6065
return META_GRADIENT_HORIZONTAL;
6066
else if (strcmp ("diagonal", str) == 0)
6067
return META_GRADIENT_DIAGONAL;
6069
return META_GRADIENT_LAST;
6073
meta_gradient_type_to_string (MetaGradientType type)
6077
case META_GRADIENT_VERTICAL:
6079
case META_GRADIENT_HORIZONTAL:
6080
return "horizontal";
6081
case META_GRADIENT_DIAGONAL:
6083
case META_GRADIENT_LAST:
6091
meta_gtk_state_from_string (const char *str)
6093
if (strcmp ("normal", str) == 0 || strcmp ("NORMAL", str) == 0)
6094
return GTK_STATE_NORMAL;
6095
else if (strcmp ("prelight", str) == 0 || strcmp ("PRELIGHT", str) == 0)
6096
return GTK_STATE_PRELIGHT;
6097
else if (strcmp ("active", str) == 0 || strcmp ("ACTIVE", str) == 0)
6098
return GTK_STATE_ACTIVE;
6099
else if (strcmp ("selected", str) == 0 || strcmp ("SELECTED", str) == 0)
6100
return GTK_STATE_SELECTED;
6101
else if (strcmp ("insensitive", str) == 0 || strcmp ("INSENSITIVE", str) == 0)
6102
return GTK_STATE_INSENSITIVE;
6104
return -1; /* hack */
6108
meta_gtk_state_to_string (GtkStateType state)
6112
case GTK_STATE_NORMAL:
6114
case GTK_STATE_PRELIGHT:
6116
case GTK_STATE_ACTIVE:
6118
case GTK_STATE_SELECTED:
6120
case GTK_STATE_INSENSITIVE:
6121
return "INSENSITIVE";
6128
meta_gtk_shadow_from_string (const char *str)
6130
if (strcmp ("none", str) == 0)
6131
return GTK_SHADOW_NONE;
6132
else if (strcmp ("in", str) == 0)
6133
return GTK_SHADOW_IN;
6134
else if (strcmp ("out", str) == 0)
6135
return GTK_SHADOW_OUT;
6136
else if (strcmp ("etched_in", str) == 0)
6137
return GTK_SHADOW_ETCHED_IN;
6138
else if (strcmp ("etched_out", str) == 0)
6139
return GTK_SHADOW_ETCHED_OUT;
6145
meta_gtk_shadow_to_string (GtkShadowType shadow)
6149
case GTK_SHADOW_NONE:
6153
case GTK_SHADOW_OUT:
6155
case GTK_SHADOW_ETCHED_IN:
6157
case GTK_SHADOW_ETCHED_OUT:
6158
return "etched_out";
6165
meta_gtk_arrow_from_string (const char *str)
6167
if (strcmp ("up", str) == 0)
6168
return GTK_ARROW_UP;
6169
else if (strcmp ("down", str) == 0)
6170
return GTK_ARROW_DOWN;
6171
else if (strcmp ("left", str) == 0)
6172
return GTK_ARROW_LEFT;
6173
else if (strcmp ("right", str) == 0)
6174
return GTK_ARROW_RIGHT;
6175
else if (strcmp ("none", str) == 0)
6176
return GTK_ARROW_NONE;
6182
meta_gtk_arrow_to_string (GtkArrowType arrow)
6188
case GTK_ARROW_DOWN:
6190
case GTK_ARROW_LEFT:
6192
case GTK_ARROW_RIGHT:
6194
case GTK_ARROW_NONE:
6202
* Returns a fill_type from a string. The inverse of
6203
* meta_image_fill_type_to_string().
6205
* \param str a string representing a fill_type
6206
* \result the fill_type, or -1 if it represents no fill_type.
6209
meta_image_fill_type_from_string (const char *str)
6211
if (strcmp ("tile", str) == 0)
6212
return META_IMAGE_FILL_TILE;
6213
else if (strcmp ("scale", str) == 0)
6214
return META_IMAGE_FILL_SCALE;
6220
* Returns a string representation of a fill_type. The inverse of
6221
* meta_image_fill_type_from_string().
6223
* \param fill_type the fill type
6224
* \result a string representing that type
6227
meta_image_fill_type_to_string (MetaImageFillType fill_type)
6231
case META_IMAGE_FILL_TILE:
6233
case META_IMAGE_FILL_SCALE:
6241
* Takes a colour "a", scales the lightness and saturation by a certain amount,
6242
* and sets "b" to the resulting colour.
6243
* gtkstyle.c cut-and-pastage.
6245
* \param a the starting colour
6246
* \param b [out] the resulting colour
6247
* \param k amount to scale lightness and saturation by
6250
gtk_style_shade (GdkColor *a,
6258
red = (gdouble) a->red / 65535.0;
6259
green = (gdouble) a->green / 65535.0;
6260
blue = (gdouble) a->blue / 65535.0;
6262
rgb_to_hls (&red, &green, &blue);
6267
else if (green < 0.0)
6273
else if (blue < 0.0)
6276
hls_to_rgb (&red, &green, &blue);
6278
b->red = red * 65535.0;
6279
b->green = green * 65535.0;
6280
b->blue = blue * 65535.0;
6284
* Converts a red/green/blue triplet to a hue/lightness/saturation triplet.
6286
* \param r on input, red; on output, hue
6287
* \param g on input, green; on output, lightness
6288
* \param b on input, blue; on output, saturation
6291
rgb_to_hls (gdouble *r,
6332
l = (max + min) / 2;
6339
s = (max - min) / (max + min);
6341
s = (max - min) / (2 - max - min);
6345
h = (green - blue) / delta;
6346
else if (green == max)
6347
h = 2 + (blue - red) / delta;
6348
else if (blue == max)
6349
h = 4 + (red - green) / delta;
6362
* Converts a hue/lightness/saturation triplet to a red/green/blue triplet.
6364
* \param h on input, hue; on output, red
6365
* \param l on input, lightness; on output, green
6366
* \param s on input, saturation; on output, blue
6369
hls_to_rgb (gdouble *h,
6382
if (lightness <= 0.5)
6383
m2 = lightness * (1 + saturation);
6385
m2 = lightness + saturation - lightness * saturation;
6386
m1 = 2 * lightness - m2;
6388
if (saturation == 0)
6403
r = m1 + (m2 - m1) * hue / 60;
6407
r = m1 + (m2 - m1) * (240 - hue) / 60;
6418
g = m1 + (m2 - m1) * hue / 60;
6422
g = m1 + (m2 - m1) * (240 - hue) / 60;
6433
b = m1 + (m2 - m1) * hue / 60;
6437
b = m1 + (m2 - m1) * (240 - hue) / 60;
6448
/* These are some functions I'm saving to use in optimizing
6449
* MetaDrawOpList, namely to pre-composite pixbufs on client side
6450
* prior to rendering to the server
6453
draw_bg_solid_composite (const MetaTextureSpec *bg,
6454
const MetaTextureSpec *fg,
6457
GdkDrawable *drawable,
6458
const GdkRectangle *clip,
6459
MetaTextureDrawMode mode,
6469
g_assert (bg->type == META_TEXTURE_SOLID);
6470
g_assert (fg->type != META_TEXTURE_COMPOSITE);
6471
g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
6473
meta_color_spec_render (bg->data.solid.color_spec,
6479
case META_TEXTURE_SOLID:
6483
meta_color_spec_render (fg->data.solid.color_spec,
6487
color_composite (&bg_color, &fg_color,
6490
draw_color_rectangle (widget, drawable, &fg_color, clip,
6491
x, y, width, height);
6495
case META_TEXTURE_GRADIENT:
6496
/* FIXME I think we could just composite all the colors in
6497
* the gradient prior to generating the gradient?
6500
case META_TEXTURE_IMAGE:
6503
GdkPixbuf *composited;
6505
pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
6511
composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
6512
gdk_pixbuf_get_has_alpha (pixbuf), 8,
6513
gdk_pixbuf_get_width (pixbuf),
6514
gdk_pixbuf_get_height (pixbuf));
6516
if (composited == NULL)
6518
g_object_unref (G_OBJECT (pixbuf));
6522
gdk_pixbuf_composite_color (pixbuf,
6525
gdk_pixbuf_get_width (pixbuf),
6526
gdk_pixbuf_get_height (pixbuf),
6527
0.0, 0.0, /* offsets */
6528
1.0, 1.0, /* scale */
6529
GDK_INTERP_BILINEAR,
6531
0, 0, /* check offsets */
6533
GDK_COLOR_RGB (bg_color),
6534
GDK_COLOR_RGB (bg_color));
6536
/* Need to draw background since pixbuf is not
6537
* necessarily covering the whole thing
6539
draw_color_rectangle (widget, drawable, &bg_color, clip,
6540
x, y, width, height);
6542
render_pixbuf_aligned (drawable, clip, composited,
6544
x, y, width, height);
6546
g_object_unref (G_OBJECT (pixbuf));
6547
g_object_unref (G_OBJECT (composited));
6551
case META_TEXTURE_BLANK:
6552
case META_TEXTURE_COMPOSITE:
6553
case META_TEXTURE_SHAPE_LIST:
6554
g_assert_not_reached ();
6560
draw_bg_gradient_composite (const MetaTextureSpec *bg,
6561
const MetaTextureSpec *fg,
6564
GdkDrawable *drawable,
6565
const GdkRectangle *clip,
6566
MetaTextureDrawMode mode,
6574
g_assert (bg->type == META_TEXTURE_GRADIENT);
6575
g_assert (fg->type != META_TEXTURE_COMPOSITE);
6576
g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
6580
case META_TEXTURE_SOLID:
6581
case META_TEXTURE_GRADIENT:
6582
case META_TEXTURE_IMAGE:
6584
GdkPixbuf *bg_pixbuf;
6585
GdkPixbuf *fg_pixbuf;
6586
GdkPixbuf *composited;
6587
int fg_width, fg_height;
6589
bg_pixbuf = meta_texture_spec_render (bg, widget, mode, 255,
6592
if (bg_pixbuf == NULL)
6595
fg_pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
6598
if (fg_pixbuf == NULL)
6600
g_object_unref (G_OBJECT (bg_pixbuf));
6604
/* gradients always fill the entire target area */
6605
g_assert (gdk_pixbuf_get_width (bg_pixbuf) == width);
6606
g_assert (gdk_pixbuf_get_height (bg_pixbuf) == height);
6608
composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
6609
gdk_pixbuf_get_has_alpha (bg_pixbuf), 8,
6610
gdk_pixbuf_get_width (bg_pixbuf),
6611
gdk_pixbuf_get_height (bg_pixbuf));
6613
if (composited == NULL)
6615
g_object_unref (G_OBJECT (bg_pixbuf));
6616
g_object_unref (G_OBJECT (fg_pixbuf));
6620
fg_width = gdk_pixbuf_get_width (fg_pixbuf);
6621
fg_height = gdk_pixbuf_get_height (fg_pixbuf);
6623
/* If we wanted to be all cool we could deal with the
6624
* offsets and try to composite only in the clip rectangle,
6625
* but I just don't care enough to figure it out.
6628
gdk_pixbuf_composite (fg_pixbuf,
6630
x + (width - fg_width) * xalign,
6631
y + (height - fg_height) * yalign,
6632
gdk_pixbuf_get_width (fg_pixbuf),
6633
gdk_pixbuf_get_height (fg_pixbuf),
6634
0.0, 0.0, /* offsets */
6635
1.0, 1.0, /* scale */
6636
GDK_INTERP_BILINEAR,
6639
gdk_cairo_set_source_pixbuf (cr, composited, x, y);
6642
g_object_unref (G_OBJECT (bg_pixbuf));
6643
g_object_unref (G_OBJECT (fg_pixbuf));
6644
g_object_unref (G_OBJECT (composited));
6648
case META_TEXTURE_BLANK:
6649
case META_TEXTURE_SHAPE_LIST:
6650
case META_TEXTURE_COMPOSITE:
6651
g_assert_not_reached ();
6658
* Returns the earliest version of the theme format which required support
6659
* for a particular button. (For example, "shade" first appeared in v2, and
6662
* \param type the button type
6663
* \return the number of the theme format
6666
meta_theme_earliest_version_with_button (MetaButtonType type)
6670
case META_BUTTON_TYPE_CLOSE:
6671
case META_BUTTON_TYPE_MAXIMIZE:
6672
case META_BUTTON_TYPE_MINIMIZE:
6673
case META_BUTTON_TYPE_MENU:
6674
case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
6675
case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
6676
case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
6677
case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
6678
case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
6679
case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
6682
case META_BUTTON_TYPE_SHADE:
6683
case META_BUTTON_TYPE_ABOVE:
6684
case META_BUTTON_TYPE_STICK:
6685
case META_BUTTON_TYPE_UNSHADE:
6686
case META_BUTTON_TYPE_UNABOVE:
6687
case META_BUTTON_TYPE_UNSTICK:
6691
meta_warning("Unknown button %d\n", type);