00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00055 #include <config.h>
00056 #include "theme.h"
00057 #include "theme-parser.h"
00058 #include "util.h"
00059 #include "gradient.h"
00060 #include <gtk/gtkwidget.h>
00061 #include <gtk/gtkimage.h>
00062 #include <gtk/gtkicontheme.h>
00063 #include <string.h>
00064 #include <stdlib.h>
00065 #include <math.h>
00066
00067 #define GDK_COLOR_RGBA(color) \
00068 ((guint32) (0xff | \
00069 (((color).red / 256) << 24) | \
00070 (((color).green / 256) << 16) | \
00071 (((color).blue / 256) << 8)))
00072
00073 #define GDK_COLOR_RGB(color) \
00074 ((guint32) ((((color).red / 256) << 16) | \
00075 (((color).green / 256) << 8) | \
00076 (((color).blue / 256))))
00077
00078 #define ALPHA_TO_UCHAR(d) ((unsigned char) ((d) * 255))
00079
00080 #define DEBUG_FILL_STRUCT(s) memset ((s), 0xef, sizeof (*(s)))
00081 #define CLAMP_UCHAR(v) ((guchar) (CLAMP (((int)v), (int)0, (int)255)))
00082 #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
00083
00084 static void gtk_style_shade (GdkColor *a,
00085 GdkColor *b,
00086 gdouble k);
00087 static void rgb_to_hls (gdouble *r,
00088 gdouble *g,
00089 gdouble *b);
00090 static void hls_to_rgb (gdouble *h,
00091 gdouble *l,
00092 gdouble *s);
00093
00097 static MetaTheme *meta_current_theme = NULL;
00098
00099 static GdkPixbuf *
00100 colorize_pixbuf (GdkPixbuf *orig,
00101 GdkColor *new_color)
00102 {
00103 GdkPixbuf *pixbuf;
00104 double intensity;
00105 int x, y;
00106 const guchar *src;
00107 guchar *dest;
00108 int orig_rowstride;
00109 int dest_rowstride;
00110 int width, height;
00111 gboolean has_alpha;
00112 const guchar *src_pixels;
00113 guchar *dest_pixels;
00114
00115 pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (orig), gdk_pixbuf_get_has_alpha (orig),
00116 gdk_pixbuf_get_bits_per_sample (orig),
00117 gdk_pixbuf_get_width (orig), gdk_pixbuf_get_height (orig));
00118
00119 if (pixbuf == NULL)
00120 return NULL;
00121
00122 orig_rowstride = gdk_pixbuf_get_rowstride (orig);
00123 dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
00124 width = gdk_pixbuf_get_width (pixbuf);
00125 height = gdk_pixbuf_get_height (pixbuf);
00126 has_alpha = gdk_pixbuf_get_has_alpha (orig);
00127 src_pixels = gdk_pixbuf_get_pixels (orig);
00128 dest_pixels = gdk_pixbuf_get_pixels (pixbuf);
00129
00130 for (y = 0; y < height; y++)
00131 {
00132 src = src_pixels + y * orig_rowstride;
00133 dest = dest_pixels + y * dest_rowstride;
00134
00135 for (x = 0; x < width; x++)
00136 {
00137 double dr, dg, db;
00138
00139 intensity = INTENSITY (src[0], src[1], src[2]) / 255.0;
00140
00141 if (intensity <= 0.5)
00142 {
00143
00144 dr = (new_color->red * intensity * 2.0) / 65535.0;
00145 dg = (new_color->green * intensity * 2.0) / 65535.0;
00146 db = (new_color->blue * intensity * 2.0) / 65535.0;
00147 }
00148 else
00149 {
00150
00151 dr = (new_color->red + (65535 - new_color->red) * (intensity - 0.5) * 2.0) / 65535.0;
00152 dg = (new_color->green + (65535 - new_color->green) * (intensity - 0.5) * 2.0) / 65535.0;
00153 db = (new_color->blue + (65535 - new_color->blue) * (intensity - 0.5) * 2.0) / 65535.0;
00154 }
00155
00156 dest[0] = CLAMP_UCHAR (255 * dr);
00157 dest[1] = CLAMP_UCHAR (255 * dg);
00158 dest[2] = CLAMP_UCHAR (255 * db);
00159
00160 if (has_alpha)
00161 {
00162 dest[3] = src[3];
00163 src += 4;
00164 dest += 4;
00165 }
00166 else
00167 {
00168 src += 3;
00169 dest += 3;
00170 }
00171 }
00172 }
00173
00174 return pixbuf;
00175 }
00176
00177 static void
00178 color_composite (const GdkColor *bg,
00179 const GdkColor *fg,
00180 double alpha_d,
00181 GdkColor *color)
00182 {
00183 guint16 alpha;
00184
00185 *color = *bg;
00186 alpha = alpha_d * 0xffff;
00187 color->red = color->red + (((fg->red - color->red) * alpha + 0x8000) >> 16);
00188 color->green = color->green + (((fg->green - color->green) * alpha + 0x8000) >> 16);
00189 color->blue = color->blue + (((fg->blue - color->blue) * alpha + 0x8000) >> 16);
00190 }
00191
00197 static void
00198 init_border (GtkBorder *border)
00199 {
00200 border->top = -1;
00201 border->bottom = -1;
00202 border->left = -1;
00203 border->right = -1;
00204 }
00205
00212 MetaFrameLayout*
00213 meta_frame_layout_new (void)
00214 {
00215 MetaFrameLayout *layout;
00216
00217 layout = g_new0 (MetaFrameLayout, 1);
00218
00219 layout->refcount = 1;
00220
00221
00222 layout->left_width = -1;
00223 layout->right_width = -1;
00224 layout->bottom_height = -1;
00225
00226 init_border (&layout->title_border);
00227
00228 layout->title_vertical_pad = -1;
00229
00230 layout->right_titlebar_edge = -1;
00231 layout->left_titlebar_edge = -1;
00232
00233 layout->button_sizing = META_BUTTON_SIZING_LAST;
00234 layout->button_aspect = 1.0;
00235 layout->button_width = -1;
00236 layout->button_height = -1;
00237
00238 layout->has_title = TRUE;
00239 layout->title_scale = 1.0;
00240
00241 init_border (&layout->button_border);
00242
00243 return layout;
00244 }
00245
00249 static gboolean
00250 validate_border (const GtkBorder *border,
00251 const char **bad)
00252 {
00253 *bad = NULL;
00254
00255 if (border->top < 0)
00256 *bad = _("top");
00257 else if (border->bottom < 0)
00258 *bad = _("bottom");
00259 else if (border->left < 0)
00260 *bad = _("left");
00261 else if (border->right < 0)
00262 *bad = _("right");
00263
00264 return *bad == NULL;
00265 }
00266
00280 static gboolean
00281 validate_geometry_value (int val,
00282 const char *name,
00283 GError **error)
00284 {
00285 if (val < 0)
00286 {
00287 g_set_error (error, META_THEME_ERROR,
00288 META_THEME_ERROR_FRAME_GEOMETRY,
00289 _("frame geometry does not specify \"%s\" dimension"),
00290 name);
00291 return FALSE;
00292 }
00293 else
00294 return TRUE;
00295 }
00296
00297 static gboolean
00298 validate_geometry_border (const GtkBorder *border,
00299 const char *name,
00300 GError **error)
00301 {
00302 const char *bad;
00303
00304 if (!validate_border (border, &bad))
00305 {
00306 g_set_error (error, META_THEME_ERROR,
00307 META_THEME_ERROR_FRAME_GEOMETRY,
00308 _("frame geometry does not specify dimension \"%s\" for border \"%s\""),
00309 bad, name);
00310 return FALSE;
00311 }
00312 else
00313 return TRUE;
00314 }
00315
00316 gboolean
00317 meta_frame_layout_validate (const MetaFrameLayout *layout,
00318 GError **error)
00319 {
00320 g_return_val_if_fail (layout != NULL, FALSE);
00321
00322 #define CHECK_GEOMETRY_VALUE(vname) if (!validate_geometry_value (layout->vname, #vname, error)) return FALSE
00323
00324 #define CHECK_GEOMETRY_BORDER(bname) if (!validate_geometry_border (&layout->bname, #bname, error)) return FALSE
00325
00326 CHECK_GEOMETRY_VALUE (left_width);
00327 CHECK_GEOMETRY_VALUE (right_width);
00328 CHECK_GEOMETRY_VALUE (bottom_height);
00329
00330 CHECK_GEOMETRY_BORDER (title_border);
00331
00332 CHECK_GEOMETRY_VALUE (title_vertical_pad);
00333
00334 CHECK_GEOMETRY_VALUE (right_titlebar_edge);
00335 CHECK_GEOMETRY_VALUE (left_titlebar_edge);
00336
00337 switch (layout->button_sizing)
00338 {
00339 case META_BUTTON_SIZING_ASPECT:
00340 if (layout->button_aspect < (0.1) ||
00341 layout->button_aspect > (15.0))
00342 {
00343 g_set_error (error, META_THEME_ERROR,
00344 META_THEME_ERROR_FRAME_GEOMETRY,
00345 _("Button aspect ratio %g is not reasonable"),
00346 layout->button_aspect);
00347 return FALSE;
00348 }
00349 break;
00350 case META_BUTTON_SIZING_FIXED:
00351 CHECK_GEOMETRY_VALUE (button_width);
00352 CHECK_GEOMETRY_VALUE (button_height);
00353 break;
00354 case META_BUTTON_SIZING_LAST:
00355 g_set_error (error, META_THEME_ERROR,
00356 META_THEME_ERROR_FRAME_GEOMETRY,
00357 _("Frame geometry does not specify size of buttons"));
00358 return FALSE;
00359 }
00360
00361 CHECK_GEOMETRY_BORDER (button_border);
00362
00363 return TRUE;
00364 }
00365
00366 MetaFrameLayout*
00367 meta_frame_layout_copy (const MetaFrameLayout *src)
00368 {
00369 MetaFrameLayout *layout;
00370
00371 layout = g_new0 (MetaFrameLayout, 1);
00372
00373 *layout = *src;
00374
00375 layout->refcount = 1;
00376
00377 return layout;
00378 }
00379
00380 void
00381 meta_frame_layout_ref (MetaFrameLayout *layout)
00382 {
00383 g_return_if_fail (layout != NULL);
00384
00385 layout->refcount += 1;
00386 }
00387
00388 void
00389 meta_frame_layout_unref (MetaFrameLayout *layout)
00390 {
00391 g_return_if_fail (layout != NULL);
00392 g_return_if_fail (layout->refcount > 0);
00393
00394 layout->refcount -= 1;
00395
00396 if (layout->refcount == 0)
00397 {
00398 DEBUG_FILL_STRUCT (layout);
00399 g_free (layout);
00400 }
00401 }
00402
00403 void
00404 meta_frame_layout_get_borders (const MetaFrameLayout *layout,
00405 int text_height,
00406 MetaFrameFlags flags,
00407 int *top_height,
00408 int *bottom_height,
00409 int *left_width,
00410 int *right_width)
00411 {
00412 int buttons_height, title_height;
00413
00414 g_return_if_fail (top_height != NULL);
00415 g_return_if_fail (bottom_height != NULL);
00416 g_return_if_fail (left_width != NULL);
00417 g_return_if_fail (right_width != NULL);
00418
00419 if (!layout->has_title)
00420 text_height = 0;
00421
00422 buttons_height = layout->button_height +
00423 layout->button_border.top + layout->button_border.bottom;
00424 title_height = text_height +
00425 layout->title_vertical_pad +
00426 layout->title_border.top + layout->title_border.bottom;
00427
00428 if (top_height)
00429 {
00430 *top_height = MAX (buttons_height, title_height);
00431 }
00432
00433 if (left_width)
00434 *left_width = layout->left_width;
00435 if (right_width)
00436 *right_width = layout->right_width;
00437
00438 if (bottom_height)
00439 {
00440 if (flags & META_FRAME_SHADED)
00441 *bottom_height = 0;
00442 else
00443 *bottom_height = layout->bottom_height;
00444 }
00445
00446 if (flags & META_FRAME_FULLSCREEN)
00447 {
00448 if (top_height)
00449 *top_height = 0;
00450 if (bottom_height)
00451 *bottom_height = 0;
00452 if (left_width)
00453 *left_width = 0;
00454 if (right_width)
00455 *right_width = 0;
00456 }
00457 }
00458
00459 static MetaButtonSpace*
00460 rect_for_function (MetaFrameGeometry *fgeom,
00461 MetaFrameFlags flags,
00462 MetaButtonFunction function,
00463 MetaTheme *theme)
00464 {
00465
00466
00467
00468 if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS))
00469 {
00470 switch (function)
00471 {
00472 case META_BUTTON_FUNCTION_SHADE:
00473 if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED))
00474 return &fgeom->shade_rect;
00475 else
00476 return NULL;
00477 case META_BUTTON_FUNCTION_ABOVE:
00478 if (!(flags & META_FRAME_ABOVE))
00479 return &fgeom->above_rect;
00480 else
00481 return NULL;
00482 case META_BUTTON_FUNCTION_STICK:
00483 if (!(flags & META_FRAME_STUCK))
00484 return &fgeom->stick_rect;
00485 else
00486 return NULL;
00487 case META_BUTTON_FUNCTION_UNSHADE:
00488 if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED))
00489 return &fgeom->unshade_rect;
00490 else
00491 return NULL;
00492 case META_BUTTON_FUNCTION_UNABOVE:
00493 if (flags & META_FRAME_ABOVE)
00494 return &fgeom->unabove_rect;
00495 else
00496 return NULL;
00497 case META_BUTTON_FUNCTION_UNSTICK:
00498 if (flags & META_FRAME_STUCK)
00499 return &fgeom->unstick_rect;
00500 default:
00501 ;
00502 }
00503 }
00504
00505
00506
00507 switch (function)
00508 {
00509 case META_BUTTON_FUNCTION_MENU:
00510 if (flags & META_FRAME_ALLOWS_MENU)
00511 return &fgeom->menu_rect;
00512 else
00513 return NULL;
00514 case META_BUTTON_FUNCTION_MINIMIZE:
00515 if (flags & META_FRAME_ALLOWS_MINIMIZE)
00516 return &fgeom->min_rect;
00517 else
00518 return NULL;
00519 case META_BUTTON_FUNCTION_MAXIMIZE:
00520 if (flags & META_FRAME_ALLOWS_MAXIMIZE)
00521 return &fgeom->max_rect;
00522 else
00523 return NULL;
00524 case META_BUTTON_FUNCTION_CLOSE:
00525 if (flags & META_FRAME_ALLOWS_DELETE)
00526 return &fgeom->close_rect;
00527 else
00528 return NULL;
00529 case META_BUTTON_FUNCTION_STICK:
00530 case META_BUTTON_FUNCTION_SHADE:
00531 case META_BUTTON_FUNCTION_ABOVE:
00532 case META_BUTTON_FUNCTION_UNSTICK:
00533 case META_BUTTON_FUNCTION_UNSHADE:
00534 case META_BUTTON_FUNCTION_UNABOVE:
00535
00536
00537
00538
00539
00540 return NULL;
00541
00542 case META_BUTTON_FUNCTION_LAST:
00543 return NULL;
00544 }
00545
00546 return NULL;
00547 }
00548
00549 static gboolean
00550 strip_button (MetaButtonSpace *func_rects[MAX_BUTTONS_PER_CORNER],
00551 GdkRectangle *bg_rects[MAX_BUTTONS_PER_CORNER],
00552 int *n_rects,
00553 MetaButtonSpace *to_strip)
00554 {
00555 int i;
00556
00557 i = 0;
00558 while (i < *n_rects)
00559 {
00560 if (func_rects[i] == to_strip)
00561 {
00562 *n_rects -= 1;
00563
00564
00565 while (i < *n_rects)
00566 {
00567 func_rects[i] = func_rects[i+1];
00568 bg_rects[i] = bg_rects[i+1];
00569
00570 ++i;
00571 }
00572
00573 func_rects[i] = NULL;
00574 bg_rects[i] = NULL;
00575
00576 return TRUE;
00577 }
00578
00579 ++i;
00580 }
00581
00582 return FALSE;
00583 }
00584
00585 void
00586 meta_frame_layout_calc_geometry (const MetaFrameLayout *layout,
00587 int text_height,
00588 MetaFrameFlags flags,
00589 int client_width,
00590 int client_height,
00591 const MetaButtonLayout *button_layout,
00592 MetaFrameGeometry *fgeom,
00593 MetaTheme *theme)
00594 {
00595 int i, n_left, n_right, n_left_spacers, n_right_spacers;
00596 int x;
00597 int button_y;
00598 int title_right_edge;
00599 int width, height;
00600 int button_width, button_height;
00601 int min_size_for_rounding;
00602
00603
00604
00605
00606 MetaButtonSpace *left_func_rects[MAX_BUTTONS_PER_CORNER];
00607 MetaButtonSpace *right_func_rects[MAX_BUTTONS_PER_CORNER];
00608 GdkRectangle *left_bg_rects[MAX_BUTTONS_PER_CORNER];
00609 gboolean left_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
00610 GdkRectangle *right_bg_rects[MAX_BUTTONS_PER_CORNER];
00611 gboolean right_buttons_has_spacer[MAX_BUTTONS_PER_CORNER];
00612
00613 meta_frame_layout_get_borders (layout, text_height,
00614 flags,
00615 &fgeom->top_height,
00616 &fgeom->bottom_height,
00617 &fgeom->left_width,
00618 &fgeom->right_width);
00619
00620 width = client_width + fgeom->left_width + fgeom->right_width;
00621
00622 height = ((flags & META_FRAME_SHADED) ? 0: client_height) +
00623 fgeom->top_height + fgeom->bottom_height;
00624
00625 fgeom->width = width;
00626 fgeom->height = height;
00627
00628 fgeom->top_titlebar_edge = layout->title_border.top;
00629 fgeom->bottom_titlebar_edge = layout->title_border.bottom;
00630 fgeom->left_titlebar_edge = layout->left_titlebar_edge;
00631 fgeom->right_titlebar_edge = layout->right_titlebar_edge;
00632
00633
00634 button_width = -1;
00635 button_height = -1;
00636
00637 switch (layout->button_sizing)
00638 {
00639 case META_BUTTON_SIZING_ASPECT:
00640 button_height = fgeom->top_height - layout->button_border.top - layout->button_border.bottom;
00641 button_width = button_height / layout->button_aspect;
00642 break;
00643 case META_BUTTON_SIZING_FIXED:
00644 button_width = layout->button_width;
00645 button_height = layout->button_height;
00646 break;
00647 case META_BUTTON_SIZING_LAST:
00648 g_assert_not_reached ();
00649 break;
00650 }
00651
00652
00653
00654
00655
00656
00657
00658
00659 memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0',
00660 LENGTH_OF_BUTTON_RECTS);
00661
00662 n_left = 0;
00663 n_right = 0;
00664 n_left_spacers = 0;
00665 n_right_spacers = 0;
00666
00667 if (!layout->hide_buttons)
00668 {
00669
00670 for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
00671 {
00672 left_func_rects[n_left] = rect_for_function (fgeom, flags,
00673 button_layout->left_buttons[i],
00674 theme);
00675 if (left_func_rects[n_left] != NULL)
00676 {
00677 left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i];
00678 if (button_layout->left_buttons_has_spacer[i])
00679 ++n_left_spacers;
00680
00681 ++n_left;
00682 }
00683 }
00684
00685 for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++)
00686 {
00687 right_func_rects[n_right] = rect_for_function (fgeom, flags,
00688 button_layout->right_buttons[i],
00689 theme);
00690 if (right_func_rects[n_right] != NULL)
00691 {
00692 right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i];
00693 if (button_layout->right_buttons_has_spacer[i])
00694 ++n_right_spacers;
00695
00696 ++n_right;
00697 }
00698 }
00699 }
00700
00701 for (i = 0; i < MAX_BUTTONS_PER_CORNER; i++)
00702 {
00703 left_bg_rects[i] = NULL;
00704 right_bg_rects[i] = NULL;
00705 }
00706
00707 for (i = 0; i < n_left; i++)
00708 {
00709 if (i == 0)
00710 left_bg_rects[i] = &fgeom->left_left_background;
00711 else if (i == (n_left - 1))
00712 left_bg_rects[i] = &fgeom->left_right_background;
00713 else
00714 left_bg_rects[i] = &fgeom->left_middle_backgrounds[i - 1];
00715 }
00716
00717 for (i = 0; i < n_right; i++)
00718 {
00719
00720 if (i == (n_right - 1))
00721 right_bg_rects[i] = &fgeom->right_right_background;
00722 else if (i == 0)
00723 right_bg_rects[i] = &fgeom->right_left_background;
00724 else
00725 right_bg_rects[i] = &fgeom->right_middle_backgrounds[i - 1];
00726 }
00727
00728
00729 while (n_left > 0 || n_right > 0)
00730 {
00731 int space_used_by_buttons;
00732 int space_available;
00733
00734 space_available = fgeom->width - layout->left_titlebar_edge - layout->right_titlebar_edge;
00735
00736 space_used_by_buttons = 0;
00737
00738 space_used_by_buttons += button_width * n_left;
00739 space_used_by_buttons += (button_width * 0.75) * n_left_spacers;
00740 space_used_by_buttons += layout->button_border.left * n_left;
00741 space_used_by_buttons += layout->button_border.right * n_left;
00742
00743 space_used_by_buttons += button_width * n_right;
00744 space_used_by_buttons += (button_width * 0.75) * n_right_spacers;
00745 space_used_by_buttons += layout->button_border.left * n_right;
00746 space_used_by_buttons += layout->button_border.right * n_right;
00747
00748 if (space_used_by_buttons <= space_available)
00749 break;
00750
00751
00752 if (n_left_spacers > 0)
00753 {
00754 left_buttons_has_spacer[--n_left_spacers] = FALSE;
00755 continue;
00756 }
00757 else if (n_right_spacers > 0)
00758 {
00759 right_buttons_has_spacer[--n_right_spacers] = FALSE;
00760 continue;
00761 }
00762
00763
00764
00765
00766
00767 if (strip_button (left_func_rects, left_bg_rects,
00768 &n_left, &fgeom->above_rect))
00769 continue;
00770 else if (strip_button (right_func_rects, right_bg_rects,
00771 &n_right, &fgeom->above_rect))
00772 continue;
00773 else if (strip_button (left_func_rects, left_bg_rects,
00774 &n_left, &fgeom->stick_rect))
00775 continue;
00776 else if (strip_button (right_func_rects, right_bg_rects,
00777 &n_right, &fgeom->stick_rect))
00778 continue;
00779 else if (strip_button (left_func_rects, left_bg_rects,
00780 &n_left, &fgeom->shade_rect))
00781 continue;
00782 else if (strip_button (right_func_rects, right_bg_rects,
00783 &n_right, &fgeom->shade_rect))
00784 continue;
00785 else if (strip_button (left_func_rects, left_bg_rects,
00786 &n_left, &fgeom->min_rect))
00787 continue;
00788 else if (strip_button (right_func_rects, right_bg_rects,
00789 &n_right, &fgeom->min_rect))
00790 continue;
00791 else if (strip_button (left_func_rects, left_bg_rects,
00792 &n_left, &fgeom->max_rect))
00793 continue;
00794 else if (strip_button (right_func_rects, right_bg_rects,
00795 &n_right, &fgeom->max_rect))
00796 continue;
00797 else if (strip_button (left_func_rects, left_bg_rects,
00798 &n_left, &fgeom->close_rect))
00799 continue;
00800 else if (strip_button (right_func_rects, right_bg_rects,
00801 &n_right, &fgeom->close_rect))
00802 continue;
00803 else if (strip_button (right_func_rects, right_bg_rects,
00804 &n_right, &fgeom->menu_rect))
00805 continue;
00806 else if (strip_button (left_func_rects, left_bg_rects,
00807 &n_left, &fgeom->menu_rect))
00808 continue;
00809 else
00810 {
00811 meta_bug ("Could not find a button to strip. n_left = %d n_right = %d\n",
00812 n_left, n_right);
00813 }
00814 }
00815
00816
00817 button_y = (fgeom->top_height -
00818 (button_height + layout->button_border.top + layout->button_border.bottom)) / 2 + layout->button_border.top;
00819
00820
00821 x = width - layout->right_titlebar_edge;
00822
00823 i = n_right - 1;
00824 while (i >= 0)
00825 {
00826 MetaButtonSpace *rect;
00827
00828 if (x < 0)
00829 break;
00830
00831 rect = right_func_rects[i];
00832 rect->visible.x = x - layout->button_border.right - button_width;
00833 if (right_buttons_has_spacer[i])
00834 rect->visible.x -= (button_width * 0.75);
00835
00836 rect->visible.y = button_y;
00837 rect->visible.width = button_width;
00838 rect->visible.height = button_height;
00839
00840 if (flags & META_FRAME_MAXIMIZED)
00841 {
00842 rect->clickable.x = rect->visible.x;
00843 rect->clickable.y = 0;
00844 rect->clickable.width = rect->visible.width;
00845 rect->clickable.height = button_height + button_y;
00846
00847 if (i == n_right - 1)
00848 rect->clickable.width += layout->right_titlebar_edge + layout->right_width + layout->button_border.right;
00849
00850 }
00851 else
00852 g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
00853
00854 *(right_bg_rects[i]) = rect->visible;
00855
00856 x = rect->visible.x - layout->button_border.left;
00857
00858 --i;
00859 }
00860
00861
00862 title_right_edge = x - layout->title_border.right;
00863
00864
00865
00866
00867 x = layout->left_titlebar_edge;
00868 for (i = 0; i < n_left; i++)
00869 {
00870 MetaButtonSpace *rect;
00871
00872 rect = left_func_rects[i];
00873
00874 rect->visible.x = x + layout->button_border.left;
00875 rect->visible.y = button_y;
00876 rect->visible.width = button_width;
00877 rect->visible.height = button_height;
00878
00879 if (flags & META_FRAME_MAXIMIZED)
00880 {
00881 if (i==0)
00882 {
00883 rect->clickable.x = 0;
00884 rect->clickable.width = button_width + x;
00885 }
00886 else
00887 {
00888 rect->clickable.x = rect->visible.x;
00889 rect->clickable.width = button_width;
00890 }
00891
00892 rect->clickable.y = 0;
00893 rect->clickable.height = button_height + button_y;
00894 }
00895 else
00896 g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
00897
00898
00899 x = rect->visible.x + rect->visible.width + layout->button_border.right;
00900 if (left_buttons_has_spacer[i])
00901 x += (button_width * 0.75);
00902
00903 *(left_bg_rects[i]) = rect->visible;
00904 }
00905
00906
00907
00908
00909 fgeom->title_rect.x = x + layout->title_border.left;
00910 fgeom->title_rect.y = layout->title_border.top;
00911 fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x;
00912 fgeom->title_rect.height = fgeom->top_height - layout->title_border.top - layout->title_border.bottom;
00913
00914
00915 if (fgeom->title_rect.width < 0 ||
00916 fgeom->title_rect.height < 0)
00917 {
00918 fgeom->title_rect.width = 0;
00919 fgeom->title_rect.height = 0;
00920 }
00921
00922 if (flags & META_FRAME_SHADED)
00923 min_size_for_rounding = 0;
00924 else
00925 min_size_for_rounding = 5;
00926
00927 fgeom->top_left_corner_rounded_radius = 0;
00928 fgeom->top_right_corner_rounded_radius = 0;
00929 fgeom->bottom_left_corner_rounded_radius = 0;
00930 fgeom->bottom_right_corner_rounded_radius = 0;
00931
00932 if (fgeom->top_height + fgeom->left_width >= min_size_for_rounding)
00933 fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius;
00934 if (fgeom->top_height + fgeom->right_width >= min_size_for_rounding)
00935 fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius;
00936
00937 if (fgeom->bottom_height + fgeom->left_width >= min_size_for_rounding)
00938 fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius;
00939 if (fgeom->bottom_height + fgeom->right_width >= min_size_for_rounding)
00940 fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius;
00941 }
00942
00943 MetaGradientSpec*
00944 meta_gradient_spec_new (MetaGradientType type)
00945 {
00946 MetaGradientSpec *spec;
00947
00948 spec = g_new (MetaGradientSpec, 1);
00949
00950 spec->type = type;
00951 spec->color_specs = NULL;
00952
00953 return spec;
00954 }
00955
00956 static void
00957 free_color_spec (gpointer spec, gpointer user_data)
00958 {
00959 meta_color_spec_free (spec);
00960 }
00961
00962 void
00963 meta_gradient_spec_free (MetaGradientSpec *spec)
00964 {
00965 g_return_if_fail (spec != NULL);
00966
00967 g_slist_foreach (spec->color_specs, free_color_spec, NULL);
00968 g_slist_free (spec->color_specs);
00969
00970 DEBUG_FILL_STRUCT (spec);
00971 g_free (spec);
00972 }
00973
00974 GdkPixbuf*
00975 meta_gradient_spec_render (const MetaGradientSpec *spec,
00976 GtkWidget *widget,
00977 int width,
00978 int height)
00979 {
00980 int n_colors;
00981 GdkColor *colors;
00982 GSList *tmp;
00983 int i;
00984 GdkPixbuf *pixbuf;
00985
00986 n_colors = g_slist_length (spec->color_specs);
00987
00988 if (n_colors == 0)
00989 return NULL;
00990
00991 colors = g_new (GdkColor, n_colors);
00992
00993 i = 0;
00994 tmp = spec->color_specs;
00995 while (tmp != NULL)
00996 {
00997 meta_color_spec_render (tmp->data, widget, &colors[i]);
00998
00999 tmp = tmp->next;
01000 ++i;
01001 }
01002
01003 pixbuf = meta_gradient_create_multi (width, height,
01004 colors, n_colors,
01005 spec->type);
01006
01007 g_free (colors);
01008
01009 return pixbuf;
01010 }
01011
01012 gboolean
01013 meta_gradient_spec_validate (MetaGradientSpec *spec,
01014 GError **error)
01015 {
01016 g_return_val_if_fail (spec != NULL, FALSE);
01017
01018 if (g_slist_length (spec->color_specs) < 2)
01019 {
01020 g_set_error (error, META_THEME_ERROR,
01021 META_THEME_ERROR_FAILED,
01022 _("Gradients should have at least two colors"));
01023 return FALSE;
01024 }
01025
01026 return TRUE;
01027 }
01028
01029 MetaAlphaGradientSpec*
01030 meta_alpha_gradient_spec_new (MetaGradientType type,
01031 int n_alphas)
01032 {
01033 MetaAlphaGradientSpec *spec;
01034
01035 g_return_val_if_fail (n_alphas > 0, NULL);
01036
01037 spec = g_new0 (MetaAlphaGradientSpec, 1);
01038
01039 spec->type = type;
01040 spec->alphas = g_new0 (unsigned char, n_alphas);
01041 spec->n_alphas = n_alphas;
01042
01043 return spec;
01044 }
01045
01046 void
01047 meta_alpha_gradient_spec_free (MetaAlphaGradientSpec *spec)
01048 {
01049 g_return_if_fail (spec != NULL);
01050
01051 g_free (spec->alphas);
01052 g_free (spec);
01053 }
01054
01055 MetaColorSpec*
01056 meta_color_spec_new (MetaColorSpecType type)
01057 {
01058 MetaColorSpec *spec;
01059 MetaColorSpec dummy;
01060 int size;
01061
01062 size = G_STRUCT_OFFSET (MetaColorSpec, data);
01063
01064 switch (type)
01065 {
01066 case META_COLOR_SPEC_BASIC:
01067 size += sizeof (dummy.data.basic);
01068 break;
01069
01070 case META_COLOR_SPEC_GTK:
01071 size += sizeof (dummy.data.gtk);
01072 break;
01073
01074 case META_COLOR_SPEC_BLEND:
01075 size += sizeof (dummy.data.blend);
01076 break;
01077
01078 case META_COLOR_SPEC_SHADE:
01079 size += sizeof (dummy.data.shade);
01080 break;
01081 }
01082
01083 spec = g_malloc0 (size);
01084
01085 spec->type = type;
01086
01087 return spec;
01088 }
01089
01090 void
01091 meta_color_spec_free (MetaColorSpec *spec)
01092 {
01093 g_return_if_fail (spec != NULL);
01094
01095 switch (spec->type)
01096 {
01097 case META_COLOR_SPEC_BASIC:
01098 DEBUG_FILL_STRUCT (&spec->data.basic);
01099 break;
01100
01101 case META_COLOR_SPEC_GTK:
01102 DEBUG_FILL_STRUCT (&spec->data.gtk);
01103 break;
01104
01105 case META_COLOR_SPEC_BLEND:
01106 if (spec->data.blend.foreground)
01107 meta_color_spec_free (spec->data.blend.foreground);
01108 if (spec->data.blend.background)
01109 meta_color_spec_free (spec->data.blend.background);
01110 DEBUG_FILL_STRUCT (&spec->data.blend);
01111 break;
01112
01113 case META_COLOR_SPEC_SHADE:
01114 if (spec->data.shade.base)
01115 meta_color_spec_free (spec->data.shade.base);
01116 DEBUG_FILL_STRUCT (&spec->data.shade);
01117 break;
01118 }
01119
01120 g_free (spec);
01121 }
01122
01123 MetaColorSpec*
01124 meta_color_spec_new_from_string (const char *str,
01125 GError **err)
01126 {
01127 MetaColorSpec *spec;
01128
01129 spec = NULL;
01130
01131 if (str[0] == 'g' && str[1] == 't' && str[2] == 'k' && str[3] == ':')
01132 {
01133
01134 const char *bracket;
01135 const char *end_bracket;
01136 char *tmp;
01137 GtkStateType state;
01138 MetaGtkColorComponent component;
01139
01140 bracket = str;
01141 while (*bracket && *bracket != '[')
01142 ++bracket;
01143
01144 if (*bracket == '\0')
01145 {
01146 g_set_error (err, META_THEME_ERROR,
01147 META_THEME_ERROR_FAILED,
01148 _("GTK color specification must have the state in brackets, e.g. gtk:fg[NORMAL] where NORMAL is the state; could not parse \"%s\""),
01149 str);
01150 return NULL;
01151 }
01152
01153 end_bracket = bracket;
01154 ++end_bracket;
01155 while (*end_bracket && *end_bracket != ']')
01156 ++end_bracket;
01157
01158 if (*end_bracket == '\0')
01159 {
01160 g_set_error (err, META_THEME_ERROR,
01161 META_THEME_ERROR_FAILED,
01162 _("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\""),
01163 str);
01164 return NULL;
01165 }
01166
01167 tmp = g_strndup (bracket + 1, end_bracket - bracket - 1);
01168 state = meta_gtk_state_from_string (tmp);
01169 if (((int) state) == -1)
01170 {
01171 g_set_error (err, META_THEME_ERROR,
01172 META_THEME_ERROR_FAILED,
01173 _("Did not understand state \"%s\" in color specification"),
01174 tmp);
01175 g_free (tmp);
01176 return NULL;
01177 }
01178 g_free (tmp);
01179
01180 tmp = g_strndup (str + 4, bracket - str - 4);
01181 component = meta_color_component_from_string (tmp);
01182 if (component == META_GTK_COLOR_LAST)
01183 {
01184 g_set_error (err, META_THEME_ERROR,
01185 META_THEME_ERROR_FAILED,
01186 _("Did not understand color component \"%s\" in color specification"),
01187 tmp);
01188 g_free (tmp);
01189 return NULL;
01190 }
01191 g_free (tmp);
01192
01193 spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
01194 spec->data.gtk.state = state;
01195 spec->data.gtk.component = component;
01196 g_assert (spec->data.gtk.state < N_GTK_STATES);
01197 g_assert (spec->data.gtk.component < META_GTK_COLOR_LAST);
01198 }
01199 else if (str[0] == 'b' && str[1] == 'l' && str[2] == 'e' && str[3] == 'n' &&
01200 str[4] == 'd' && str[5] == '/')
01201 {
01202
01203 char **split;
01204 double alpha;
01205 char *end;
01206 MetaColorSpec *fg;
01207 MetaColorSpec *bg;
01208
01209 split = g_strsplit (str, "/", 4);
01210
01211 if (split[0] == NULL || split[1] == NULL ||
01212 split[2] == NULL || split[3] == NULL)
01213 {
01214 g_set_error (err, META_THEME_ERROR,
01215 META_THEME_ERROR_FAILED,
01216 _("Blend format is \"blend/bg_color/fg_color/alpha\", \"%s\" does not fit the format"),
01217 str);
01218 g_strfreev (split);
01219 return NULL;
01220 }
01221
01222 alpha = g_ascii_strtod (split[3], &end);
01223 if (end == split[3])
01224 {
01225 g_set_error (err, META_THEME_ERROR,
01226 META_THEME_ERROR_FAILED,
01227 _("Could not parse alpha value \"%s\" in blended color"),
01228 split[3]);
01229 g_strfreev (split);
01230 return NULL;
01231 }
01232
01233 if (alpha < (0.0 - 1e6) || alpha > (1.0 + 1e6))
01234 {
01235 g_set_error (err, META_THEME_ERROR,
01236 META_THEME_ERROR_FAILED,
01237 _("Alpha value \"%s\" in blended color is not between 0.0 and 1.0"),
01238 split[3]);
01239 g_strfreev (split);
01240 return NULL;
01241 }
01242
01243 fg = NULL;
01244 bg = NULL;
01245
01246 bg = meta_color_spec_new_from_string (split[1], err);
01247 if (bg == NULL)
01248 {
01249 g_strfreev (split);
01250 return NULL;
01251 }
01252
01253 fg = meta_color_spec_new_from_string (split[2], err);
01254 if (fg == NULL)
01255 {
01256 meta_color_spec_free (bg);
01257 g_strfreev (split);
01258 return NULL;
01259 }
01260
01261 g_strfreev (split);
01262
01263 spec = meta_color_spec_new (META_COLOR_SPEC_BLEND);
01264 spec->data.blend.alpha = alpha;
01265 spec->data.blend.background = bg;
01266 spec->data.blend.foreground = fg;
01267 }
01268 else if (str[0] == 's' && str[1] == 'h' && str[2] == 'a' && str[3] == 'd' &&
01269 str[4] == 'e' && str[5] == '/')
01270 {
01271
01272 char **split;
01273 double factor;
01274 char *end;
01275 MetaColorSpec *base;
01276
01277 split = g_strsplit (str, "/", 3);
01278
01279 if (split[0] == NULL || split[1] == NULL ||
01280 split[2] == NULL)
01281 {
01282 g_set_error (err, META_THEME_ERROR,
01283 META_THEME_ERROR_FAILED,
01284 _("Shade format is \"shade/base_color/factor\", \"%s\" does not fit the format"),
01285 str);
01286 g_strfreev (split);
01287 return NULL;
01288 }
01289
01290 factor = g_ascii_strtod (split[2], &end);
01291 if (end == split[2])
01292 {
01293 g_set_error (err, META_THEME_ERROR,
01294 META_THEME_ERROR_FAILED,
01295 _("Could not parse shade factor \"%s\" in shaded color"),
01296 split[2]);
01297 g_strfreev (split);
01298 return NULL;
01299 }
01300
01301 if (factor < (0.0 - 1e6))
01302 {
01303 g_set_error (err, META_THEME_ERROR,
01304 META_THEME_ERROR_FAILED,
01305 _("Shade factor \"%s\" in shaded color is negative"),
01306 split[2]);
01307 g_strfreev (split);
01308 return NULL;
01309 }
01310
01311 base = NULL;
01312
01313 base = meta_color_spec_new_from_string (split[1], err);
01314 if (base == NULL)
01315 {
01316 g_strfreev (split);
01317 return NULL;
01318 }
01319
01320 g_strfreev (split);
01321
01322 spec = meta_color_spec_new (META_COLOR_SPEC_SHADE);
01323 spec->data.shade.factor = factor;
01324 spec->data.shade.base = base;
01325 }
01326 else
01327 {
01328 spec = meta_color_spec_new (META_COLOR_SPEC_BASIC);
01329
01330 if (!gdk_color_parse (str, &spec->data.basic.color))
01331 {
01332 g_set_error (err, META_THEME_ERROR,
01333 META_THEME_ERROR_FAILED,
01334 _("Could not parse color \"%s\""),
01335 str);
01336 meta_color_spec_free (spec);
01337 return NULL;
01338 }
01339 }
01340
01341 g_assert (spec);
01342
01343 return spec;
01344 }
01345
01346 MetaColorSpec*
01347 meta_color_spec_new_gtk (MetaGtkColorComponent component,
01348 GtkStateType state)
01349 {
01350 MetaColorSpec *spec;
01351
01352 spec = meta_color_spec_new (META_COLOR_SPEC_GTK);
01353
01354 spec->data.gtk.component = component;
01355 spec->data.gtk.state = state;
01356
01357 return spec;
01358 }
01359
01360 void
01361 meta_color_spec_render (MetaColorSpec *spec,
01362 GtkWidget *widget,
01363 GdkColor *color)
01364 {
01365 g_return_if_fail (spec != NULL);
01366 g_return_if_fail (GTK_IS_WIDGET (widget));
01367 g_return_if_fail (widget->style != NULL);
01368
01369 switch (spec->type)
01370 {
01371 case META_COLOR_SPEC_BASIC:
01372 *color = spec->data.basic.color;
01373 break;
01374
01375 case META_COLOR_SPEC_GTK:
01376 switch (spec->data.gtk.component)
01377 {
01378 case META_GTK_COLOR_BG:
01379 *color = widget->style->bg[spec->data.gtk.state];
01380 break;
01381 case META_GTK_COLOR_FG:
01382 *color = widget->style->fg[spec->data.gtk.state];
01383 break;
01384 case META_GTK_COLOR_BASE:
01385 *color = widget->style->base[spec->data.gtk.state];
01386 break;
01387 case META_GTK_COLOR_TEXT:
01388 *color = widget->style->text[spec->data.gtk.state];
01389 break;
01390 case META_GTK_COLOR_LIGHT:
01391 *color = widget->style->light[spec->data.gtk.state];
01392 break;
01393 case META_GTK_COLOR_DARK:
01394 *color = widget->style->dark[spec->data.gtk.state];
01395 break;
01396 case META_GTK_COLOR_MID:
01397 *color = widget->style->mid[spec->data.gtk.state];
01398 break;
01399 case META_GTK_COLOR_TEXT_AA:
01400 *color = widget->style->text_aa[spec->data.gtk.state];
01401 break;
01402 case META_GTK_COLOR_LAST:
01403 g_assert_not_reached ();
01404 break;
01405 }
01406 break;
01407
01408 case META_COLOR_SPEC_BLEND:
01409 {
01410 GdkColor bg, fg;
01411
01412 meta_color_spec_render (spec->data.blend.background, widget, &bg);
01413 meta_color_spec_render (spec->data.blend.foreground, widget, &fg);
01414
01415 color_composite (&bg, &fg, spec->data.blend.alpha,
01416 &spec->data.blend.color);
01417
01418 *color = spec->data.blend.color;
01419 }
01420 break;
01421
01422 case META_COLOR_SPEC_SHADE:
01423 {
01424 meta_color_spec_render (spec->data.shade.base, widget,
01425 &spec->data.shade.color);
01426
01427 gtk_style_shade (&spec->data.shade.color,
01428 &spec->data.shade.color, spec->data.shade.factor);
01429
01430 *color = spec->data.shade.color;
01431 }
01432 break;
01433 }
01434 }
01435
01442 static const char*
01443 op_name (PosOperatorType type)
01444 {
01445 switch (type)
01446 {
01447 case POS_OP_ADD:
01448 return "+";
01449 case POS_OP_SUBTRACT:
01450 return "-";
01451 case POS_OP_MULTIPLY:
01452 return "*";
01453 case POS_OP_DIVIDE:
01454 return "/";
01455 case POS_OP_MOD:
01456 return "%";
01457 case POS_OP_MAX:
01458 return "`max`";
01459 case POS_OP_MIN:
01460 return "`min`";
01461 case POS_OP_NONE:
01462 break;
01463 }
01464
01465 return "<unknown>";
01466 }
01467
01476 static PosOperatorType
01477 op_from_string (const char *p,
01478 int *len)
01479 {
01480 *len = 0;
01481
01482 switch (*p)
01483 {
01484 case '+':
01485 *len = 1;
01486 return POS_OP_ADD;
01487 case '-':
01488 *len = 1;
01489 return POS_OP_SUBTRACT;
01490 case '*':
01491 *len = 1;
01492 return POS_OP_MULTIPLY;
01493 case '/':
01494 *len = 1;
01495 return POS_OP_DIVIDE;
01496 case '%':
01497 *len = 1;
01498 return POS_OP_MOD;
01499
01500 case '`':
01501 if (p[0] == '`' &&
01502 p[1] == 'm' &&
01503 p[2] == 'a' &&
01504 p[3] == 'x' &&
01505 p[4] == '`')
01506 {
01507 *len = 5;
01508 return POS_OP_MAX;
01509 }
01510 else if (p[0] == '`' &&
01511 p[1] == 'm' &&
01512 p[2] == 'i' &&
01513 p[3] == 'n' &&
01514 p[4] == '`')
01515 {
01516 *len = 5;
01517 return POS_OP_MIN;
01518 }
01519 }
01520
01521 return POS_OP_NONE;
01522 }
01523
01531 static void
01532 free_tokens (PosToken *tokens,
01533 int n_tokens)
01534 {
01535 int i;
01536
01537
01538
01539
01540
01541 for (i = 0; i < n_tokens; i++)
01542 if (tokens[i].type == POS_TOKEN_VARIABLE)
01543 g_free (tokens[i].d.v.name);
01544
01545 g_free (tokens);
01546 }
01547
01564 static gboolean
01565 parse_number (const char *p,
01566 const char **end_return,
01567 PosToken *next,
01568 GError **err)
01569 {
01570 const char *start = p;
01571 char *end;
01572 gboolean is_float;
01573 char *num_str;
01574
01575 while (*p && (*p == '.' || g_ascii_isdigit (*p)))
01576 ++p;
01577
01578 if (p == start)
01579 {
01580 char buf[7] = { '\0' };
01581 buf[g_unichar_to_utf8 (g_utf8_get_char (p), buf)] = '\0';
01582 g_set_error (err, META_THEME_ERROR,
01583 META_THEME_ERROR_BAD_CHARACTER,
01584 _("Coordinate expression contains character '%s' which is not allowed"),
01585 buf);
01586 return FALSE;
01587 }
01588
01589 *end_return = p;
01590
01591
01592 num_str = g_strndup (start, p - start);
01593 start = num_str;
01594 is_float = FALSE;
01595 while (*start)
01596 {
01597 if (*start == '.')
01598 is_float = TRUE;
01599 ++start;
01600 }
01601
01602 if (is_float)
01603 {
01604 next->type = POS_TOKEN_DOUBLE;
01605 next->d.d.val = g_ascii_strtod (num_str, &end);
01606
01607 if (end == num_str)
01608 {
01609 g_set_error (err, META_THEME_ERROR,
01610 META_THEME_ERROR_FAILED,
01611 _("Coordinate expression contains floating point number '%s' which could not be parsed"),
01612 num_str);
01613 g_free (num_str);
01614 return FALSE;
01615 }
01616 }
01617 else
01618 {
01619 next->type = POS_TOKEN_INT;
01620 next->d.i.val = strtol (num_str, &end, 10);
01621 if (end == num_str)
01622 {
01623 g_set_error (err, META_THEME_ERROR,
01624 META_THEME_ERROR_FAILED,
01625 _("Coordinate expression contains integer '%s' which could not be parsed"),
01626 num_str);
01627 g_free (num_str);
01628 return FALSE;
01629 }
01630 }
01631
01632 g_free (num_str);
01633
01634 g_assert (next->type == POS_TOKEN_INT || next->type == POS_TOKEN_DOUBLE);
01635
01636 return TRUE;
01637 }
01638
01642 #define IS_VARIABLE_CHAR(c) (g_ascii_isalpha ((c)) || (c) == '_')
01643
01644 #if 0
01645 static void
01646 debug_print_tokens (PosToken *tokens,
01647 int n_tokens)
01648 {
01649 int i;
01650
01651 for (i = 0; i < n_tokens; i++)
01652 {
01653 PosToken *t = &tokens[i];
01654
01655 g_print (" ");
01656
01657 switch (t->type)
01658 {
01659 case POS_TOKEN_INT:
01660 g_print ("\"%d\"", t->d.i.val);
01661 break;
01662 case POS_TOKEN_DOUBLE:
01663 g_print ("\"%g\"", t->d.d.val);
01664 break;
01665 case POS_TOKEN_OPEN_PAREN:
01666 g_print ("\"(\"");
01667 break;
01668 case POS_TOKEN_CLOSE_PAREN:
01669 g_print ("\")\"");
01670 break;
01671 case POS_TOKEN_VARIABLE:
01672 g_print ("\"%s\"", t->d.v.name);
01673 break;
01674 case POS_TOKEN_OPERATOR:
01675 g_print ("\"%s\"", op_name (t->d.o.op));
01676 break;
01677 }
01678 }
01679
01680 g_print ("\n");
01681 }
01682 #endif
01683
01696 static gboolean
01697 pos_tokenize (const char *expr,
01698 PosToken **tokens_p,
01699 int *n_tokens_p,
01700 GError **err)
01701 {
01702 PosToken *tokens;
01703 int n_tokens;
01704 int allocated;
01705 const char *p;
01706
01707 *tokens_p = NULL;
01708 *n_tokens_p = 0;
01709
01710 allocated = 3;
01711 n_tokens = 0;
01712 tokens = g_new (PosToken, allocated);
01713
01714 p = expr;
01715 while (*p)
01716 {
01717 PosToken *next;
01718 int len;
01719
01720 if (n_tokens == allocated)
01721 {
01722 allocated *= 2;
01723 tokens = g_renew (PosToken, tokens, allocated);
01724 }
01725
01726 next = &tokens[n_tokens];
01727
01728 switch (*p)
01729 {
01730 case '*':
01731 case '/':
01732 case '+':
01733 case '-':
01734 case '%':
01735 case '`':
01736 next->type = POS_TOKEN_OPERATOR;
01737 next->d.o.op = op_from_string (p, &len);
01738 if (next->d.o.op != POS_OP_NONE)
01739 {
01740 ++n_tokens;
01741 p = p + (len - 1);
01742 }
01743 else
01744 {
01745 g_set_error (err, META_THEME_ERROR,
01746 META_THEME_ERROR_FAILED,
01747 _("Coordinate expression contained unknown operator at the start of this text: \"%s\""),
01748 p);
01749
01750 goto error;
01751 }
01752 break;
01753
01754 case '(':
01755 next->type = POS_TOKEN_OPEN_PAREN;
01756 ++n_tokens;
01757 break;
01758
01759 case ')':
01760 next->type = POS_TOKEN_CLOSE_PAREN;
01761 ++n_tokens;
01762 break;
01763
01764 case ' ':
01765 case '\t':
01766 case '\n':
01767 break;
01768
01769 default:
01770 if (IS_VARIABLE_CHAR (*p))
01771 {
01772
01773 const char *start = p;
01774 while (*p && IS_VARIABLE_CHAR (*p))
01775 ++p;
01776 g_assert (p != start);
01777 next->type = POS_TOKEN_VARIABLE;
01778 next->d.v.name = g_strndup (start, p - start);
01779 ++n_tokens;
01780 --p;
01781 }
01782 else
01783 {
01784
01785 const char *end;
01786
01787 if (!parse_number (p, &end, next, err))
01788 goto error;
01789
01790 ++n_tokens;
01791 p = end - 1;
01792 }
01793
01794 break;
01795 }
01796
01797 ++p;
01798 }
01799
01800 if (n_tokens == 0)
01801 {
01802 g_set_error (err, META_THEME_ERROR,
01803 META_THEME_ERROR_FAILED,
01804 _("Coordinate expression was empty or not understood"));
01805
01806 goto error;
01807 }
01808
01809 *tokens_p = tokens;
01810 *n_tokens_p = n_tokens;
01811
01812 return TRUE;
01813
01814 error:
01815 g_assert (err == NULL || *err != NULL);
01816
01817 free_tokens (tokens, n_tokens);
01818 return FALSE;
01819 }
01820
01825 typedef enum
01826 {
01827 POS_EXPR_INT,
01828 POS_EXPR_DOUBLE,
01829 POS_EXPR_OPERATOR
01830 } PosExprType;
01831
01842 typedef struct
01843 {
01844 PosExprType type;
01845 union
01846 {
01847 double double_val;
01848 int int_val;
01849 char operator;
01850 } d;
01851 } PosExpr;
01852
01853 #if 0
01854 static void
01855 debug_print_exprs (PosExpr *exprs,
01856 int n_exprs)
01857 {
01858 int i;
01859
01860 for (i = 0; i < n_exprs; i++)
01861 {
01862 switch (exprs[i].type)
01863 {
01864 case POS_EXPR_INT:
01865 g_print (" %d", exprs[i].d.int_val);
01866 break;
01867 case POS_EXPR_DOUBLE:
01868 g_print (" %g", exprs[i].d.double_val);
01869 break;
01870 case POS_EXPR_OPERATOR:
01871 g_print (" %s", op_name (exprs[i].d.operator));
01872 break;
01873 }
01874 }
01875 g_print ("\n");
01876 }
01877 #endif
01878
01879 static gboolean
01880 do_operation (PosExpr *a,
01881 PosExpr *b,
01882 PosOperatorType op,
01883 GError **err)
01884 {
01885
01886 if (a->type == POS_EXPR_DOUBLE ||
01887 b->type == POS_EXPR_DOUBLE)
01888 {
01889 if (a->type != POS_EXPR_DOUBLE)
01890 {
01891 a->type = POS_EXPR_DOUBLE;
01892 a->d.double_val = a->d.int_val;
01893 }
01894 if (b->type != POS_EXPR_DOUBLE)
01895 {
01896 b->type = POS_EXPR_DOUBLE;
01897 b->d.double_val = b->d.int_val;
01898 }
01899 }
01900
01901 g_assert (a->type == b->type);
01902
01903 if (a->type == POS_EXPR_INT)
01904 {
01905 switch (op)
01906 {
01907 case POS_OP_MULTIPLY:
01908 a->d.int_val = a->d.int_val * b->d.int_val;
01909 break;
01910 case POS_OP_DIVIDE:
01911 if (b->d.int_val == 0)
01912 {
01913 g_set_error (err, META_THEME_ERROR,
01914 META_THEME_ERROR_DIVIDE_BY_ZERO,
01915 _("Coordinate expression results in division by zero"));
01916 return FALSE;
01917 }
01918 a->d.int_val = a->d.int_val / b->d.int_val;
01919 break;
01920 case POS_OP_MOD:
01921 if (b->d.int_val == 0)
01922 {
01923 g_set_error (err, META_THEME_ERROR,
01924 META_THEME_ERROR_DIVIDE_BY_ZERO,
01925 _("Coordinate expression results in division by zero"));
01926 return FALSE;
01927 }
01928 a->d.int_val = a->d.int_val % b->d.int_val;
01929 break;
01930 case POS_OP_ADD:
01931 a->d.int_val = a->d.int_val + b->d.int_val;
01932 break;
01933 case POS_OP_SUBTRACT:
01934 a->d.int_val = a->d.int_val - b->d.int_val;
01935 break;
01936 case POS_OP_MAX:
01937 a->d.int_val = MAX (a->d.int_val, b->d.int_val);
01938 break;
01939 case POS_OP_MIN:
01940 a->d.int_val = MIN (a->d.int_val, b->d.int_val);
01941 break;
01942 case POS_OP_NONE:
01943 g_assert_not_reached ();
01944 break;
01945 }
01946 }
01947 else if (a->type == POS_EXPR_DOUBLE)
01948 {
01949 switch (op)
01950 {
01951 case POS_OP_MULTIPLY:
01952 a->d.double_val = a->d.double_val * b->d.double_val;
01953 break;
01954 case POS_OP_DIVIDE:
01955 if (b->d.double_val == 0.0)
01956 {
01957 g_set_error (err, META_THEME_ERROR,
01958 META_THEME_ERROR_DIVIDE_BY_ZERO,
01959 _("Coordinate expression results in division by zero"));
01960 return FALSE;
01961 }
01962 a->d.double_val = a->d.double_val / b->d.double_val;
01963 break;
01964 case POS_OP_MOD:
01965 g_set_error (err, META_THEME_ERROR,
01966 META_THEME_ERROR_MOD_ON_FLOAT,
01967 _("Coordinate expression tries to use mod operator on a floating-point number"));
01968 return FALSE;
01969 case POS_OP_ADD:
01970 a->d.double_val = a->d.double_val + b->d.double_val;
01971 break;
01972 case POS_OP_SUBTRACT:
01973 a->d.double_val = a->d.double_val - b->d.double_val;
01974 break;
01975 case POS_OP_MAX:
01976 a->d.double_val = MAX (a->d.double_val, b->d.double_val);
01977 break;
01978 case POS_OP_MIN:
01979 a->d.double_val = MIN (a->d.double_val, b->d.double_val);
01980 break;
01981 case POS_OP_NONE:
01982 g_assert_not_reached ();
01983 break;
01984 }
01985 }
01986 else
01987 g_assert_not_reached ();
01988
01989 return TRUE;
01990 }
01991
01992 static gboolean
01993 do_operations (PosExpr *exprs,
01994 int *n_exprs,
01995 int precedence,
01996 GError **err)
01997 {
01998 int i;
01999
02000 #if 0
02001 g_print ("Doing prec %d ops on %d exprs\n", precedence, *n_exprs);
02002 debug_print_exprs (exprs, *n_exprs);
02003 #endif
02004
02005 i = 1;
02006 while (i < *n_exprs)
02007 {
02008 gboolean compress;
02009
02010
02011
02012
02013
02014
02015
02016
02017
02018
02019 if (exprs[i-1].type == POS_EXPR_OPERATOR)
02020 {
02021 g_set_error (err, META_THEME_ERROR,
02022 META_THEME_ERROR_FAILED,
02023 _("Coordinate expression has an operator \"%s\" where an operand was expected"),
02024 op_name (exprs[i-1].d.operator));
02025 return FALSE;
02026 }
02027
02028 if (exprs[i].type != POS_EXPR_OPERATOR)
02029 {
02030 g_set_error (err, META_THEME_ERROR,
02031 META_THEME_ERROR_FAILED,
02032 _("Coordinate expression had an operand where an operator was expected"));
02033 return FALSE;
02034 }
02035
02036 if (i == (*n_exprs - 1))
02037 {
02038 g_set_error (err, META_THEME_ERROR,
02039 META_THEME_ERROR_FAILED,
02040 _("Coordinate expression ended with an operator instead of an operand"));
02041 return FALSE;
02042 }
02043
02044 g_assert ((i+1) < *n_exprs);
02045
02046 if (exprs[i+1].type == POS_EXPR_OPERATOR)
02047 {
02048 g_set_error (err, META_THEME_ERROR,
02049 META_THEME_ERROR_FAILED,
02050 _("Coordinate expression has operator \"%c\" following operator \"%c\" with no operand in between"),
02051 exprs[i+1].d.operator,
02052 exprs[i].d.operator);
02053 return FALSE;
02054 }
02055
02056 compress = FALSE;
02057
02058 switch (precedence)
02059 {
02060 case 2:
02061 switch (exprs[i].d.operator)
02062 {
02063 case POS_OP_DIVIDE:
02064 case POS_OP_MOD:
02065 case POS_OP_MULTIPLY:
02066 compress = TRUE;
02067 if (!do_operation (&exprs[i-1], &exprs[i+1],
02068 exprs[i].d.operator,
02069 err))
02070 return FALSE;
02071 break;
02072 }
02073 break;
02074 case 1:
02075 switch (exprs[i].d.operator)
02076 {
02077 case POS_OP_ADD:
02078 case POS_OP_SUBTRACT:
02079 compress = TRUE;
02080 if (!do_operation (&exprs[i-1], &exprs[i+1],
02081 exprs[i].d.operator,
02082 err))
02083 return FALSE;
02084 break;
02085 }
02086 break;
02087
02088 case 0:
02089 switch (exprs[i].d.operator)
02090 {
02091 case POS_OP_MAX:
02092 case POS_OP_MIN:
02093 compress = TRUE;
02094 if (!do_operation (&exprs[i-1], &exprs[i+1],
02095 exprs[i].d.operator,
02096 err))
02097 return FALSE;
02098 break;
02099 }
02100 break;
02101 }
02102
02103 if (compress)
02104 {
02105
02106
02107
02108
02109
02110
02111
02112 if ((i+2) < *n_exprs)
02113 {
02114 g_memmove (&exprs[i], &exprs[i+2],
02115 sizeof (PosExpr) * (*n_exprs - i - 2));
02116 }
02117
02118 *n_exprs -= 2;
02119 }
02120 else
02121 {
02122
02123 i += 2;
02124 }
02125 }
02126
02127 return TRUE;
02128 }
02129
02155 static gboolean
02156 pos_eval_get_variable (PosToken *t,
02157 int *result,
02158 const MetaPositionExprEnv *env,
02159 GError **err)
02160 {
02161 if (env->theme)
02162 {
02163 if (t->d.v.name_quark == env->theme->quark_width)
02164 *result = env->rect.width;
02165 else if (t->d.v.name_quark == env->theme->quark_height)
02166 *result = env->rect.height;
02167 else if (env->object_width >= 0 &&
02168 t->d.v.name_quark == env->theme->quark_object_width)
02169 *result = env->object_width;
02170 else if (env->object_height >= 0 &&
02171 t->d.v.name_quark == env->theme->quark_object_height)
02172 *result = env->object_height;
02173 else if (t->d.v.name_quark == env->theme->quark_left_width)
02174 *result = env->left_width;
02175 else if (t->d.v.name_quark == env->theme->quark_right_width)
02176 *result = env->right_width;
02177 else if (t->d.v.name_quark == env->theme->quark_top_height)
02178 *result = env->top_height;
02179 else if (t->d.v.name_quark == env->theme->quark_bottom_height)
02180 *result = env->bottom_height;
02181 else if (t->d.v.name_quark == env->theme->quark_mini_icon_width)
02182 *result = env->mini_icon_width;
02183 else if (t->d.v.name_quark == env->theme->quark_mini_icon_height)
02184 *result = env->mini_icon_height;
02185 else if (t->d.v.name_quark == env->theme->quark_icon_width)
02186 *result = env->icon_width;
02187 else if (t->d.v.name_quark == env->theme->quark_icon_height)
02188 *result = env->icon_height;
02189 else if (t->d.v.name_quark == env->theme->quark_title_width)
02190 *result = env->title_width;
02191 else if (t->d.v.name_quark == env->theme->quark_title_height)
02192 *result = env->title_height;
02193 else
02194 {
02195 g_set_error (err, META_THEME_ERROR,
02196 META_THEME_ERROR_UNKNOWN_VARIABLE,
02197 _("Coordinate expression had unknown variable or constant \"%s\""),
02198 t->d.v.name);
02199 return FALSE;
02200 }
02201 }
02202 else
02203 {
02204 if (strcmp (t->d.v.name, "width") == 0)
02205 *result = env->rect.width;
02206 else if (strcmp (t->d.v.name, "height") == 0)
02207 *result = env->rect.height;
02208 else if (env->object_width >= 0 &&
02209 strcmp (t->d.v.name, "object_width") == 0)
02210 *result = env->object_width;
02211 else if (env->object_height >= 0 &&
02212 strcmp (t->d.v.name, "object_height") == 0)
02213 *result = env->object_height;
02214 else if (strcmp (t->d.v.name, "left_width") == 0)
02215 *result = env->left_width;
02216 else if (strcmp (t->d.v.name, "right_width") == 0)
02217 *result = env->right_width;
02218 else if (strcmp (t->d.v.name, "top_height") == 0)
02219 *result = env->top_height;
02220 else if (strcmp (t->d.v.name, "bottom_height") == 0)
02221 *result = env->bottom_height;
02222 else if (strcmp (t->d.v.name, "mini_icon_width") == 0)
02223 *result = env->mini_icon_width;
02224 else if (strcmp (t->d.v.name, "mini_icon_height") == 0)
02225 *result = env->mini_icon_height;
02226 else if (strcmp (t->d.v.name, "icon_width") == 0)
02227 *result = env->icon_width;
02228 else if (strcmp (t->d.v.name, "icon_height") == 0)
02229 *result = env->icon_height;
02230 else if (strcmp (t->d.v.name, "title_width") == 0)
02231 *result = env->title_width;
02232 else if (strcmp (t->d.v.name, "title_height") == 0)
02233 *result = env->title_height;
02234 else
02235 {
02236 g_set_error (err, META_THEME_ERROR,
02237 META_THEME_ERROR_UNKNOWN_VARIABLE,
02238 _("Coordinate expression had unknown variable or constant \"%s\""),
02239 t->d.v.name);
02240 return FALSE;
02241 }
02242 }
02243
02244 return TRUE;
02245 }
02246
02261 static gboolean
02262 pos_eval_helper (PosToken *tokens,
02263 int n_tokens,
02264 const MetaPositionExprEnv *env,
02265 PosExpr *result,
02266 GError **err)
02267 {
02268
02269 #define MAX_EXPRS 32
02270 int paren_level;
02271 int first_paren;
02272 int i;
02273 PosExpr exprs[MAX_EXPRS];
02274 int n_exprs;
02275 int precedence;
02276
02277
02278
02279
02280
02281 first_paren = 0;
02282 paren_level = 0;
02283 n_exprs = 0;
02284 for (i = 0; i < n_tokens; i++)
02285 {
02286 PosToken *t = &tokens[i];
02287
02288 if (n_exprs >= MAX_EXPRS)
02289 {
02290 g_set_error (err, META_THEME_ERROR,
02291 META_THEME_ERROR_FAILED,
02292 _("Coordinate expression parser overflowed its buffer."));
02293 return FALSE;
02294 }
02295
02296 if (paren_level == 0)
02297 {
02298 switch (t->type)
02299 {
02300 case POS_TOKEN_INT:
02301 exprs[n_exprs].type = POS_EXPR_INT;
02302 exprs[n_exprs].d.int_val = t->d.i.val;
02303 ++n_exprs;
02304 break;
02305
02306 case POS_TOKEN_DOUBLE:
02307 exprs[n_exprs].type = POS_EXPR_DOUBLE;
02308 exprs[n_exprs].d.double_val = t->d.d.val;
02309 ++n_exprs;
02310 break;
02311
02312 case POS_TOKEN_OPEN_PAREN:
02313 ++paren_level;
02314 if (paren_level == 1)
02315 first_paren = i;
02316 break;
02317
02318 case POS_TOKEN_CLOSE_PAREN:
02319 g_set_error (err, META_THEME_ERROR,
02320 META_THEME_ERROR_BAD_PARENS,
02321 _("Coordinate expression had a close parenthesis with no open parenthesis"));
02322 return FALSE;
02323
02324 case POS_TOKEN_VARIABLE:
02325 exprs[n_exprs].type = POS_EXPR_INT;
02326
02327
02328
02329
02330
02331 if (!pos_eval_get_variable (t, &exprs[n_exprs].d.int_val, env, err))
02332 return FALSE;
02333
02334 ++n_exprs;
02335 break;
02336
02337 case POS_TOKEN_OPERATOR:
02338 exprs[n_exprs].type = POS_EXPR_OPERATOR;
02339 exprs[n_exprs].d.operator = t->d.o.op;
02340 ++n_exprs;
02341 break;
02342 }
02343 }
02344 else
02345 {
02346 g_assert (paren_level > 0);
02347
02348 switch (t->type)
02349 {
02350 case POS_TOKEN_INT:
02351 case POS_TOKEN_DOUBLE:
02352 case POS_TOKEN_VARIABLE:
02353 case POS_TOKEN_OPERATOR:
02354 break;
02355
02356 case POS_TOKEN_OPEN_PAREN:
02357 ++paren_level;
02358 break;
02359
02360 case POS_TOKEN_CLOSE_PAREN:
02361 if (paren_level == 1)
02362 {
02363
02364 if (!pos_eval_helper (&tokens[first_paren+1],
02365 i - first_paren - 1,
02366 env,
02367 &exprs[n_exprs],
02368 err))
02369 return FALSE;
02370
02371 ++n_exprs;
02372 }
02373
02374 --paren_level;
02375 break;
02376
02377 }
02378 }
02379 }
02380
02381 if (paren_level > 0)
02382 {
02383 g_set_error (err, META_THEME_ERROR,
02384 META_THEME_ERROR_BAD_PARENS,
02385 _("Coordinate expression had an open parenthesis with no close parenthesis"));
02386 return FALSE;
02387 }
02388
02389
02390
02391
02392 if (n_exprs == 0)
02393 {
02394 g_set_error (err, META_THEME_ERROR,
02395 META_THEME_ERROR_FAILED,
02396 _("Coordinate expression doesn't seem to have any operators or operands"));
02397 return FALSE;
02398 }
02399
02400
02401 precedence = 2;
02402 while (precedence >= 0)
02403 {
02404 if (!do_operations (exprs, &n_exprs, precedence, err))
02405 return FALSE;
02406 --precedence;
02407 }
02408
02409 g_assert (n_exprs == 1);
02410
02411 *result = *exprs;
02412
02413 return TRUE;
02414 }
02415
02416
02417
02418
02419
02420
02421
02438 static gboolean
02439 pos_eval (MetaDrawSpec *spec,
02440 const MetaPositionExprEnv *env,
02441 int *val_p,
02442 GError **err)
02443 {
02444 PosExpr expr;
02445
02446 *val_p = 0;
02447
02448 if (pos_eval_helper (spec->tokens, spec->n_tokens, env, &expr, err))
02449 {
02450 switch (expr.type)
02451 {
02452 case POS_EXPR_INT:
02453 *val_p = expr.d.int_val;
02454 break;
02455 case POS_EXPR_DOUBLE:
02456 *val_p = expr.d.double_val;
02457 break;
02458 case POS_EXPR_OPERATOR:
02459 g_assert_not_reached ();
02460 break;
02461 }
02462 return TRUE;
02463 }
02464 else
02465 {
02466 return FALSE;
02467 }
02468 }
02469
02470
02471
02472
02473
02474 gboolean
02475 meta_parse_position_expression (MetaDrawSpec *spec,
02476 const MetaPositionExprEnv *env,
02477 int *x_return,
02478 int *y_return,
02479 GError **err)
02480 {
02481
02482
02483
02484
02485
02486
02487 int val;
02488
02489 if (spec->constant)
02490 val = spec->value;
02491 else
02492 {
02493 if (pos_eval (spec, env, &spec->value, err) == FALSE)
02494 {
02495 g_assert (err == NULL || *err != NULL);
02496 return FALSE;
02497 }
02498
02499 val = spec->value;
02500 }
02501
02502 if (x_return)
02503 *x_return = env->rect.x + val;
02504 if (y_return)
02505 *y_return = env->rect.y + val;
02506
02507 return TRUE;
02508 }
02509
02510
02511 gboolean
02512 meta_parse_size_expression (MetaDrawSpec *spec,
02513 const MetaPositionExprEnv *env,
02514 int *val_return,
02515 GError **err)
02516 {
02517 int val;
02518
02519 if (spec->constant)
02520 val = spec->value;
02521 else
02522 {
02523 if (pos_eval (spec, env, &spec->value, err) == FALSE)
02524 {
02525 g_assert (err == NULL || *err != NULL);
02526 return FALSE;
02527 }
02528
02529 val = spec->value;
02530 }
02531
02532 if (val_return)
02533 *val_return = MAX (val, 1);
02534
02535 return TRUE;
02536 }
02537
02538
02539
02540
02541
02542
02543
02544 gboolean
02545 meta_theme_replace_constants (MetaTheme *theme,
02546 PosToken *tokens,
02547 int n_tokens,
02548 GError **err)
02549 {
02550 int i;
02551 double dval;
02552 int ival;
02553 gboolean is_constant = TRUE;
02554
02555
02556 for (i = 0; i < n_tokens; i++)
02557 {
02558 PosToken *t = &tokens[i];
02559
02560 if (t->type == POS_TOKEN_VARIABLE)
02561 {
02562 if (meta_theme_lookup_int_constant (theme, t->d.v.name, &ival))
02563 {
02564 t->type = POS_TOKEN_INT;
02565 t->d.i.val = ival;
02566 }
02567 else if (meta_theme_lookup_float_constant (theme, t->d.v.name, &dval))
02568 {
02569 t->type = POS_TOKEN_DOUBLE;
02570 t->d.d.val = dval;
02571 }
02572 else
02573 {
02574
02575
02576
02577
02578 t->d.v.name_quark = g_quark_from_string (t->d.v.name);
02579 is_constant = FALSE;
02580 }
02581 }
02582 }
02583
02584 return is_constant;
02585 }
02586
02587 static int
02588 parse_x_position_unchecked (MetaDrawSpec *spec,
02589 const MetaPositionExprEnv *env)
02590 {
02591 int retval;
02592 GError *error;
02593
02594 retval = 0;
02595 error = NULL;
02596 if (!meta_parse_position_expression (spec, env, &retval, NULL, &error))
02597 {
02598 meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
02599 error->message);
02600
02601 g_error_free (error);
02602 }
02603
02604 return retval;
02605 }
02606
02607 static int
02608 parse_y_position_unchecked (MetaDrawSpec *spec,
02609 const MetaPositionExprEnv *env)
02610 {
02611 int retval;
02612 GError *error;
02613
02614 retval = 0;
02615 error = NULL;
02616 if (!meta_parse_position_expression (spec, env, NULL, &retval, &error))
02617 {
02618 meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
02619 error->message);
02620
02621 g_error_free (error);
02622 }
02623
02624 return retval;
02625 }
02626
02627 static int
02628 parse_size_unchecked (MetaDrawSpec *spec,
02629 MetaPositionExprEnv *env)
02630 {
02631 int retval;
02632 GError *error;
02633
02634 retval = 0;
02635 error = NULL;
02636 if (!meta_parse_size_expression (spec, env, &retval, &error))
02637 {
02638 meta_warning (_("Theme contained an expression that resulted in an error: %s\n"),
02639 error->message);
02640
02641 g_error_free (error);
02642 }
02643
02644 return retval;
02645 }
02646
02647 void
02648 meta_draw_spec_free (MetaDrawSpec *spec)
02649 {
02650 free_tokens (spec->tokens, spec->n_tokens);
02651 g_slice_free (MetaDrawSpec, spec);
02652 }
02653
02654 MetaDrawSpec *
02655 meta_draw_spec_new (MetaTheme *theme,
02656 const char *expr,
02657 GError **error)
02658 {
02659 MetaDrawSpec *spec;
02660
02661 spec = g_slice_new0 (MetaDrawSpec);
02662
02663 pos_tokenize (expr, &spec->tokens, &spec->n_tokens, NULL);
02664
02665 spec->constant = meta_theme_replace_constants (theme, spec->tokens,
02666 spec->n_tokens, NULL);
02667 if (spec->constant)
02668 {
02669 gboolean result;
02670
02671 result = pos_eval (spec, NULL, &spec->value, error);
02672 if (result == FALSE)
02673 {
02674 meta_draw_spec_free (spec);
02675 return NULL;
02676 }
02677 }
02678
02679 return spec;
02680 }
02681
02682 MetaDrawOp*
02683 meta_draw_op_new (MetaDrawType type)
02684 {
02685 MetaDrawOp *op;
02686 MetaDrawOp dummy;
02687 int size;
02688
02689 size = G_STRUCT_OFFSET (MetaDrawOp, data);
02690
02691 switch (type)
02692 {
02693 case META_DRAW_LINE:
02694 size += sizeof (dummy.data.line);
02695 break;
02696
02697 case META_DRAW_RECTANGLE:
02698 size += sizeof (dummy.data.rectangle);
02699 break;
02700
02701 case META_DRAW_ARC:
02702 size += sizeof (dummy.data.arc);
02703 break;
02704
02705 case META_DRAW_CLIP:
02706 size += sizeof (dummy.data.clip);
02707 break;
02708
02709 case META_DRAW_TINT:
02710 size += sizeof (dummy.data.tint);
02711 break;
02712
02713 case META_DRAW_GRADIENT:
02714 size += sizeof (dummy.data.gradient);
02715 break;
02716
02717 case META_DRAW_IMAGE:
02718 size += sizeof (dummy.data.image);
02719 break;
02720
02721 case META_DRAW_GTK_ARROW:
02722 size += sizeof (dummy.data.gtk_arrow);
02723 break;
02724
02725 case META_DRAW_GTK_BOX:
02726 size += sizeof (dummy.data.gtk_box);
02727 break;
02728
02729 case META_DRAW_GTK_VLINE:
02730 size += sizeof (dummy.data.gtk_vline);
02731 break;
02732
02733 case META_DRAW_ICON:
02734 size += sizeof (dummy.data.icon);
02735 break;
02736
02737 case META_DRAW_TITLE:
02738 size += sizeof (dummy.data.title);
02739 break;
02740 case META_DRAW_OP_LIST:
02741 size += sizeof (dummy.data.op_list);
02742 break;
02743 case META_DRAW_TILE:
02744 size += sizeof (dummy.data.tile);
02745 break;
02746 }
02747
02748 op = g_malloc0 (size);
02749
02750 op->type = type;
02751
02752 return op;
02753 }
02754
02755 void
02756 meta_draw_op_free (MetaDrawOp *op)
02757 {
02758 g_return_if_fail (op != NULL);
02759
02760 switch (op->type)
02761 {
02762 case META_DRAW_LINE:
02763 if (op->data.line.color_spec)
02764 meta_color_spec_free (op->data.line.color_spec);
02765
02766 meta_draw_spec_free (op->data.line.x1);
02767 meta_draw_spec_free (op->data.line.y1);
02768 meta_draw_spec_free (op->data.line.x2);
02769 meta_draw_spec_free (op->data.line.y2);
02770 break;
02771
02772 case META_DRAW_RECTANGLE:
02773 if (op->data.rectangle.color_spec)
02774 g_free (op->data.rectangle.color_spec);
02775
02776 meta_draw_spec_free (op->data.rectangle.x);
02777 meta_draw_spec_free (op->data.rectangle.y);
02778 meta_draw_spec_free (op->data.rectangle.width);
02779 meta_draw_spec_free (op->data.rectangle.height);
02780 break;
02781
02782 case META_DRAW_ARC:
02783 if (op->data.arc.color_spec)
02784 g_free (op->data.arc.color_spec);
02785
02786 meta_draw_spec_free (op->data.arc.x);
02787 meta_draw_spec_free (op->data.arc.y);
02788 meta_draw_spec_free (op->data.arc.width);
02789 meta_draw_spec_free (op->data.arc.height);
02790 break;
02791
02792 case META_DRAW_CLIP:
02793 meta_draw_spec_free (op->data.clip.x);
02794 meta_draw_spec_free (op->data.clip.y);
02795 meta_draw_spec_free (op->data.clip.width);
02796 meta_draw_spec_free (op->data.clip.height);
02797 break;
02798
02799 case META_DRAW_TINT:
02800 if (op->data.tint.color_spec)
02801 meta_color_spec_free (op->data.tint.color_spec);
02802
02803 if (op->data.tint.alpha_spec)
02804 meta_alpha_gradient_spec_free (op->data.tint.alpha_spec);
02805
02806 meta_draw_spec_free (op->data.tint.x);
02807 meta_draw_spec_free (op->data.tint.y);
02808 meta_draw_spec_free (op->data.tint.width);
02809 meta_draw_spec_free (op->data.tint.height);
02810 break;
02811
02812 case META_DRAW_GRADIENT:
02813 if (op->data.gradient.gradient_spec)
02814 meta_gradient_spec_free (op->data.gradient.gradient_spec);
02815
02816 if (op->data.gradient.alpha_spec)
02817 meta_alpha_gradient_spec_free (op->data.gradient.alpha_spec);
02818
02819 meta_draw_spec_free (op->data.gradient.x);
02820 meta_draw_spec_free (op->data.gradient.y);
02821 meta_draw_spec_free (op->data.gradient.width);
02822 meta_draw_spec_free (op->data.gradient.height);
02823 break;
02824
02825 case META_DRAW_IMAGE:
02826 if (op->data.image.alpha_spec)
02827 meta_alpha_gradient_spec_free (op->data.image.alpha_spec);
02828
02829 if (op->data.image.pixbuf)
02830 g_object_unref (G_OBJECT (op->data.image.pixbuf));
02831
02832 if (op->data.image.colorize_spec)
02833 meta_color_spec_free (op->data.image.colorize_spec);
02834
02835 if (op->data.image.colorize_cache_pixbuf)
02836 g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
02837
02838 meta_draw_spec_free (op->data.image.x);
02839 meta_draw_spec_free (op->data.image.y);
02840 meta_draw_spec_free (op->data.image.width);
02841 meta_draw_spec_free (op->data.image.height);
02842 break;
02843
02844 case META_DRAW_GTK_ARROW:
02845 meta_draw_spec_free (op->data.gtk_arrow.x);
02846 meta_draw_spec_free (op->data.gtk_arrow.y);
02847 meta_draw_spec_free (op->data.gtk_arrow.width);
02848 meta_draw_spec_free (op->data.gtk_arrow.height);
02849 break;
02850
02851 case META_DRAW_GTK_BOX:
02852 meta_draw_spec_free (op->data.gtk_box.x);
02853 meta_draw_spec_free (op->data.gtk_box.y);
02854 meta_draw_spec_free (op->data.gtk_box.width);
02855 meta_draw_spec_free (op->data.gtk_box.height);
02856 break;
02857
02858 case META_DRAW_GTK_VLINE:
02859 meta_draw_spec_free (op->data.gtk_vline.x);
02860 meta_draw_spec_free (op->data.gtk_vline.y1);
02861 meta_draw_spec_free (op->data.gtk_vline.y2);
02862 break;
02863
02864 case META_DRAW_ICON:
02865 if (op->data.icon.alpha_spec)
02866 meta_alpha_gradient_spec_free (op->data.icon.alpha_spec);
02867
02868 meta_draw_spec_free (op->data.icon.x);
02869 meta_draw_spec_free (op->data.icon.y);
02870 meta_draw_spec_free (op->data.icon.width);
02871 meta_draw_spec_free (op->data.icon.height);
02872 break;
02873
02874 case META_DRAW_TITLE:
02875 if (op->data.title.color_spec)
02876 meta_color_spec_free (op->data.title.color_spec);
02877
02878 meta_draw_spec_free (op->data.title.x);
02879 meta_draw_spec_free (op->data.title.y);
02880 break;
02881
02882 case META_DRAW_OP_LIST:
02883 if (op->data.op_list.op_list)
02884 meta_draw_op_list_unref (op->data.op_list.op_list);
02885
02886 meta_draw_spec_free (op->data.op_list.x);
02887 meta_draw_spec_free (op->data.op_list.y);
02888 meta_draw_spec_free (op->data.op_list.width);
02889 meta_draw_spec_free (op->data.op_list.height);
02890 break;
02891
02892 case META_DRAW_TILE:
02893 if (op->data.tile.op_list)
02894 meta_draw_op_list_unref (op->data.tile.op_list);
02895
02896 meta_draw_spec_free (op->data.tile.x);
02897 meta_draw_spec_free (op->data.tile.y);
02898 meta_draw_spec_free (op->data.tile.width);
02899 meta_draw_spec_free (op->data.tile.height);
02900 meta_draw_spec_free (op->data.tile.tile_xoffset);
02901 meta_draw_spec_free (op->data.tile.tile_yoffset);
02902 meta_draw_spec_free (op->data.tile.tile_width);
02903 meta_draw_spec_free (op->data.tile.tile_height);
02904 break;
02905 }
02906
02907 g_free (op);
02908 }
02909
02910 static GdkGC*
02911 get_gc_for_primitive (GtkWidget *widget,
02912 GdkDrawable *drawable,
02913 MetaColorSpec *color_spec,
02914 const GdkRectangle *clip,
02915 int line_width)
02916 {
02917 GdkGC *gc;
02918 GdkGCValues values;
02919 GdkColor color;
02920
02921 meta_color_spec_render (color_spec, widget, &color);
02922
02923 values.foreground = color;
02924
02925 gdk_rgb_find_color (gdk_drawable_get_colormap (drawable),
02926 &values.foreground);
02927
02928 values.line_width = line_width;
02929
02930 gc = gdk_gc_new_with_values (drawable, &values,
02931 GDK_GC_FOREGROUND | GDK_GC_LINE_WIDTH);
02932
02933 if (clip)
02934 gdk_gc_set_clip_rectangle (gc,
02935 (GdkRectangle*) clip);
02936
02937 return gc;
02938 }
02939
02940 static GdkPixbuf*
02941 apply_alpha (GdkPixbuf *pixbuf,
02942 MetaAlphaGradientSpec *spec,
02943 gboolean force_copy)
02944 {
02945 GdkPixbuf *new_pixbuf;
02946 gboolean needs_alpha;
02947
02948 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
02949
02950 needs_alpha = spec && (spec->n_alphas > 1 ||
02951 spec->alphas[0] != 0xff);
02952
02953 if (!needs_alpha)
02954 return pixbuf;
02955
02956 if (!gdk_pixbuf_get_has_alpha (pixbuf))
02957 {
02958 new_pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
02959 g_object_unref (G_OBJECT (pixbuf));
02960 pixbuf = new_pixbuf;
02961 }
02962 else if (force_copy)
02963 {
02964 new_pixbuf = gdk_pixbuf_copy (pixbuf);
02965 g_object_unref (G_OBJECT (pixbuf));
02966 pixbuf = new_pixbuf;
02967 }
02968
02969 g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
02970
02971 meta_gradient_add_alpha (pixbuf, spec->alphas, spec->n_alphas, spec->type);
02972
02973 return pixbuf;
02974 }
02975
02976 static void
02977 render_pixbuf (GdkDrawable *drawable,
02978 const GdkRectangle *clip,
02979 GdkPixbuf *pixbuf,
02980 int x,
02981 int y)
02982 {
02983
02984
02985
02986
02987
02988
02989
02990
02991
02992 GdkRectangle pixbuf_rect;
02993 GdkRectangle draw_rect;
02994
02995 pixbuf_rect.x = x;
02996 pixbuf_rect.y = y;
02997 pixbuf_rect.width = gdk_pixbuf_get_width (pixbuf);
02998 pixbuf_rect.height = gdk_pixbuf_get_height (pixbuf);
02999
03000 if (clip)
03001 {
03002 if (!gdk_rectangle_intersect ((GdkRectangle*)clip,
03003 &pixbuf_rect, &draw_rect))
03004 return;
03005 }
03006 else
03007 {
03008 draw_rect = pixbuf_rect;
03009 }
03010
03011 gdk_draw_pixbuf (drawable,
03012 NULL,
03013 pixbuf,
03014 draw_rect.x - pixbuf_rect.x,
03015 draw_rect.y - pixbuf_rect.y,
03016 draw_rect.x, draw_rect.y,
03017 draw_rect.width,
03018 draw_rect.height,
03019 GDK_RGB_DITHER_NORMAL,
03020 draw_rect.x - pixbuf_rect.x,
03021 draw_rect.y - pixbuf_rect.y);
03022 }
03023
03024 static GdkPixbuf*
03025 pixbuf_tile (GdkPixbuf *tile,
03026 int width,
03027 int height)
03028 {
03029 GdkPixbuf *pixbuf;
03030 int tile_width;
03031 int tile_height;
03032 int i, j;
03033
03034 tile_width = gdk_pixbuf_get_width (tile);
03035 tile_height = gdk_pixbuf_get_height (tile);
03036
03037 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
03038 gdk_pixbuf_get_has_alpha (tile),
03039 8, width, height);
03040
03041 i = 0;
03042 while (i < width)
03043 {
03044 j = 0;
03045 while (j < height)
03046 {
03047 int w, h;
03048
03049 w = MIN (tile_width, width - i);
03050 h = MIN (tile_height, height - j);
03051
03052 gdk_pixbuf_copy_area (tile,
03053 0, 0,
03054 w, h,
03055 pixbuf,
03056 i, j);
03057
03058 j += tile_height;
03059 }
03060
03061 i += tile_width;
03062 }
03063
03064 return pixbuf;
03065 }
03066
03067 static GdkPixbuf *
03068 replicate_rows (GdkPixbuf *src,
03069 int src_x,
03070 int src_y,
03071 int width,
03072 int height)
03073 {
03074 unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
03075 unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
03076 unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
03077 * n_channels);
03078 unsigned char *dest_pixels;
03079 GdkPixbuf *result;
03080 unsigned int dest_rowstride;
03081 int i;
03082
03083 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
03084 width, height);
03085 dest_rowstride = gdk_pixbuf_get_rowstride (result);
03086 dest_pixels = gdk_pixbuf_get_pixels (result);
03087
03088 for (i = 0; i < height; i++)
03089 memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
03090
03091 return result;
03092 }
03093
03094 static GdkPixbuf *
03095 replicate_cols (GdkPixbuf *src,
03096 int src_x,
03097 int src_y,
03098 int width,
03099 int height)
03100 {
03101 unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
03102 unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
03103 unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
03104 * n_channels);
03105 unsigned char *dest_pixels;
03106 GdkPixbuf *result;
03107 unsigned int dest_rowstride;
03108 int i, j;
03109
03110 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
03111 width, height);
03112 dest_rowstride = gdk_pixbuf_get_rowstride (result);
03113 dest_pixels = gdk_pixbuf_get_pixels (result);
03114
03115 for (i = 0; i < height; i++)
03116 {
03117 unsigned char *p = dest_pixels + dest_rowstride * i;
03118 unsigned char *q = pixels + src_rowstride * i;
03119
03120 unsigned char r = *(q++);
03121 unsigned char g = *(q++);
03122 unsigned char b = *(q++);
03123
03124 if (n_channels == 4)
03125 {
03126 unsigned char a;
03127
03128 a = *(q++);
03129
03130 for (j = 0; j < width; j++)
03131 {
03132 *(p++) = r;
03133 *(p++) = g;
03134 *(p++) = b;
03135 *(p++) = a;
03136 }
03137 }
03138 else
03139 {
03140 for (j = 0; j < width; j++)
03141 {
03142 *(p++) = r;
03143 *(p++) = g;
03144 *(p++) = b;
03145 }
03146 }
03147 }
03148
03149 return result;
03150 }
03151
03152 static GdkPixbuf*
03153 scale_and_alpha_pixbuf (GdkPixbuf *src,
03154 MetaAlphaGradientSpec *alpha_spec,
03155 MetaImageFillType fill_type,
03156 int width,
03157 int height,
03158 gboolean vertical_stripes,
03159 gboolean horizontal_stripes)
03160 {
03161 GdkPixbuf *pixbuf;
03162 GdkPixbuf *temp_pixbuf;
03163
03164 pixbuf = NULL;
03165
03166 pixbuf = src;
03167
03168 if (gdk_pixbuf_get_width (pixbuf) == width &&
03169 gdk_pixbuf_get_height (pixbuf) == height)
03170 {
03171 g_object_ref (G_OBJECT (pixbuf));
03172 }
03173 else
03174 {
03175 if (fill_type == META_IMAGE_FILL_TILE)
03176 {
03177 pixbuf = pixbuf_tile (pixbuf, width, height);
03178 }
03179 else
03180 {
03181 int src_h, src_w, dest_h, dest_w;
03182 src_h = gdk_pixbuf_get_height (src);
03183 src_w = gdk_pixbuf_get_width (src);
03184
03185
03186
03187
03188 if (horizontal_stripes)
03189 {
03190 dest_w = gdk_pixbuf_get_width (src);
03191 dest_h = height;
03192 }
03193 else if (vertical_stripes)
03194 {
03195 dest_w = width;
03196 dest_h = gdk_pixbuf_get_height (src);
03197 }
03198
03199 else
03200 {
03201 dest_w = width;
03202 dest_h = height;
03203 }
03204
03205 if (dest_w == src_w && dest_h == src_h)
03206 {
03207 temp_pixbuf = src;
03208 g_object_ref (G_OBJECT (temp_pixbuf));
03209 }
03210 else
03211 {
03212 temp_pixbuf = gdk_pixbuf_scale_simple (src,
03213 dest_w, dest_h,
03214 GDK_INTERP_BILINEAR);
03215 }
03216
03217
03218
03219
03220 if (horizontal_stripes)
03221 {
03222 pixbuf = replicate_cols (temp_pixbuf, 0, 0, width, height);
03223 g_object_unref (G_OBJECT (temp_pixbuf));
03224 }
03225 else if (vertical_stripes)
03226 {
03227 pixbuf = replicate_rows (temp_pixbuf, 0, 0, width, height);
03228 g_object_unref (G_OBJECT (temp_pixbuf));
03229 }
03230 else
03231 {
03232 pixbuf = temp_pixbuf;
03233 }
03234 }
03235 }
03236
03237 if (pixbuf)
03238 pixbuf = apply_alpha (pixbuf, alpha_spec, pixbuf == src);
03239
03240 return pixbuf;
03241 }
03242
03243 static GdkPixbuf*
03244 draw_op_as_pixbuf (const MetaDrawOp *op,
03245 GtkWidget *widget,
03246 const MetaDrawInfo *info,
03247 int width,
03248 int height)
03249 {
03250
03251
03252
03253
03254 GdkPixbuf *pixbuf;
03255
03256 pixbuf = NULL;
03257
03258 switch (op->type)
03259 {
03260 case META_DRAW_LINE:
03261 break;
03262
03263 case META_DRAW_RECTANGLE:
03264 if (op->data.rectangle.filled)
03265 {
03266 GdkColor color;
03267
03268 meta_color_spec_render (op->data.rectangle.color_spec,
03269 widget,
03270 &color);
03271
03272 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
03273 FALSE,
03274 8, width, height);
03275
03276 gdk_pixbuf_fill (pixbuf, GDK_COLOR_RGBA (color));
03277 }
03278 break;
03279
03280 case META_DRAW_ARC:
03281 break;
03282
03283 case META_DRAW_CLIP:
03284 break;
03285
03286 case META_DRAW_TINT:
03287 {
03288 GdkColor color;
03289 guint32 rgba;
03290 gboolean has_alpha;
03291
03292 meta_color_spec_render (op->data.rectangle.color_spec,
03293 widget,
03294 &color);
03295
03296 has_alpha =
03297 op->data.tint.alpha_spec &&
03298 (op->data.tint.alpha_spec->n_alphas > 1 ||
03299 op->data.tint.alpha_spec->alphas[0] != 0xff);
03300
03301 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
03302 has_alpha,
03303 8, width, height);
03304
03305 if (!has_alpha)
03306 {
03307 rgba = GDK_COLOR_RGBA (color);
03308
03309 gdk_pixbuf_fill (pixbuf, rgba);
03310 }
03311 else if (op->data.tint.alpha_spec->n_alphas == 1)
03312 {
03313 rgba = GDK_COLOR_RGBA (color);
03314 rgba &= ~0xff;
03315 rgba |= op->data.tint.alpha_spec->alphas[0];
03316
03317 gdk_pixbuf_fill (pixbuf, rgba);
03318 }
03319 else
03320 {
03321 rgba = GDK_COLOR_RGBA (color);
03322
03323 gdk_pixbuf_fill (pixbuf, rgba);
03324
03325 meta_gradient_add_alpha (pixbuf,
03326 op->data.tint.alpha_spec->alphas,
03327 op->data.tint.alpha_spec->n_alphas,
03328 op->data.tint.alpha_spec->type);
03329 }
03330 }
03331 break;
03332
03333 case META_DRAW_GRADIENT:
03334 {
03335 pixbuf = meta_gradient_spec_render (op->data.gradient.gradient_spec,
03336 widget, width, height);
03337
03338 pixbuf = apply_alpha (pixbuf,
03339 op->data.gradient.alpha_spec,
03340 FALSE);
03341 }
03342 break;
03343
03344
03345 case META_DRAW_IMAGE:
03346 {
03347 if (op->data.image.colorize_spec)
03348 {
03349 GdkColor color;
03350
03351 meta_color_spec_render (op->data.image.colorize_spec,
03352 widget, &color);
03353
03354 if (op->data.image.colorize_cache_pixbuf == NULL ||
03355 op->data.image.colorize_cache_pixel != GDK_COLOR_RGB (color))
03356 {
03357 if (op->data.image.colorize_cache_pixbuf)
03358 g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
03359
03360
03361 ((MetaDrawOp*)op)->data.image.colorize_cache_pixbuf =
03362 colorize_pixbuf (op->data.image.pixbuf,
03363 &color);
03364 ((MetaDrawOp*)op)->data.image.colorize_cache_pixel =
03365 GDK_COLOR_RGB (color);
03366 }
03367
03368 if (op->data.image.colorize_cache_pixbuf)
03369 {
03370 pixbuf = scale_and_alpha_pixbuf (op->data.image.colorize_cache_pixbuf,
03371 op->data.image.alpha_spec,
03372 op->data.image.fill_type,
03373 width, height,
03374 op->data.image.vertical_stripes,
03375 op->data.image.horizontal_stripes);
03376 }
03377 }
03378 else
03379 {
03380 pixbuf = scale_and_alpha_pixbuf (op->data.image.pixbuf,
03381 op->data.image.alpha_spec,
03382 op->data.image.fill_type,
03383 width, height,
03384 op->data.image.vertical_stripes,
03385 op->data.image.horizontal_stripes);
03386 }
03387 break;
03388 }
03389
03390 case META_DRAW_GTK_ARROW:
03391 case META_DRAW_GTK_BOX:
03392 case META_DRAW_GTK_VLINE:
03393 break;
03394
03395 case META_DRAW_ICON:
03396 if (info->mini_icon &&
03397 width <= gdk_pixbuf_get_width (info->mini_icon) &&
03398 height <= gdk_pixbuf_get_height (info->mini_icon))
03399 pixbuf = scale_and_alpha_pixbuf (info->mini_icon,
03400 op->data.icon.alpha_spec,
03401 op->data.icon.fill_type,
03402 width, height,
03403 FALSE, FALSE);
03404 else if (info->icon)
03405 pixbuf = scale_and_alpha_pixbuf (info->icon,
03406 op->data.icon.alpha_spec,
03407 op->data.icon.fill_type,
03408 width, height,
03409 FALSE, FALSE);
03410 break;
03411
03412 case META_DRAW_TITLE:
03413 break;
03414
03415 case META_DRAW_OP_LIST:
03416 break;
03417
03418 case META_DRAW_TILE:
03419 break;
03420 }
03421
03422 return pixbuf;
03423 }
03424
03425 static void
03426 fill_env (MetaPositionExprEnv *env,
03427 const MetaDrawInfo *info,
03428 MetaRectangle logical_region)
03429 {
03430
03431
03432 env->rect = logical_region;
03433 env->object_width = -1;
03434 env->object_height = -1;
03435 if (info->fgeom)
03436 {
03437 env->left_width = info->fgeom->left_width;
03438 env->right_width = info->fgeom->right_width;
03439 env->top_height = info->fgeom->top_height;
03440 env->bottom_height = info->fgeom->bottom_height;
03441 }
03442 else
03443 {
03444 env->left_width = 0;
03445 env->right_width = 0;
03446 env->top_height = 0;
03447 env->bottom_height = 0;
03448 }
03449
03450 env->mini_icon_width = info->mini_icon ? gdk_pixbuf_get_width (info->mini_icon) : 0;
03451 env->mini_icon_height = info->mini_icon ? gdk_pixbuf_get_height (info->mini_icon) : 0;
03452 env->icon_width = info->icon ? gdk_pixbuf_get_width (info->icon) : 0;
03453 env->icon_height = info->icon ? gdk_pixbuf_get_height (info->icon) : 0;
03454
03455 env->title_width = info->title_layout_width;
03456 env->title_height = info->title_layout_height;
03457 env->theme = meta_current_theme;
03458 }
03459
03460 static void
03461 meta_draw_op_draw_with_env (const MetaDrawOp *op,
03462 GtkWidget *widget,
03463 GdkDrawable *drawable,
03464 const GdkRectangle *clip,
03465 const MetaDrawInfo *info,
03466 MetaRectangle rect,
03467 MetaPositionExprEnv *env)
03468 {
03469 GdkGC *gc;
03470
03471 switch (op->type)
03472 {
03473 case META_DRAW_LINE:
03474 {
03475 int x1, x2, y1, y2;
03476
03477 gc = get_gc_for_primitive (widget, drawable,
03478 op->data.line.color_spec,
03479 clip,
03480 op->data.line.width);
03481
03482 if (op->data.line.dash_on_length > 0 &&
03483 op->data.line.dash_off_length > 0)
03484 {
03485 gint8 dash_list[2];
03486 dash_list[0] = op->data.line.dash_on_length;
03487 dash_list[1] = op->data.line.dash_off_length;
03488 gdk_gc_set_dashes (gc, 0, dash_list, 2);
03489 }
03490
03491 x1 = parse_x_position_unchecked (op->data.line.x1, env);
03492 y1 = parse_y_position_unchecked (op->data.line.y1, env);
03493 x2 = parse_x_position_unchecked (op->data.line.x2, env);
03494 y2 = parse_y_position_unchecked (op->data.line.y2, env);
03495
03496 gdk_draw_line (drawable, gc, x1, y1, x2, y2);
03497
03498 g_object_unref (G_OBJECT (gc));
03499 }
03500 break;
03501
03502 case META_DRAW_RECTANGLE:
03503 {
03504 int rx, ry, rwidth, rheight;
03505
03506 gc = get_gc_for_primitive (widget, drawable,
03507 op->data.rectangle.color_spec,
03508 clip, 0);
03509
03510 rx = parse_x_position_unchecked (op->data.rectangle.x, env);
03511 ry = parse_y_position_unchecked (op->data.rectangle.y, env);
03512 rwidth = parse_size_unchecked (op->data.rectangle.width, env);
03513 rheight = parse_size_unchecked (op->data.rectangle.height, env);
03514
03515 gdk_draw_rectangle (drawable, gc,
03516 op->data.rectangle.filled,
03517 rx, ry, rwidth, rheight);
03518
03519 g_object_unref (G_OBJECT (gc));
03520 }
03521 break;
03522
03523 case META_DRAW_ARC:
03524 {
03525 int rx, ry, rwidth, rheight;
03526
03527 gc = get_gc_for_primitive (widget, drawable,
03528 op->data.arc.color_spec,
03529 clip, 0);
03530
03531 rx = parse_x_position_unchecked (op->data.arc.x, env);
03532 ry = parse_y_position_unchecked (op->data.arc.y, env);
03533 rwidth = parse_size_unchecked (op->data.arc.width, env);
03534 rheight = parse_size_unchecked (op->data.arc.height, env);
03535
03536 gdk_draw_arc (drawable,
03537 gc,
03538 op->data.arc.filled,
03539 rx, ry, rwidth, rheight,
03540 op->data.arc.start_angle * (360.0 * 64.0) -
03541 (90.0 * 64.0),
03542 op->data.arc.extent_angle * (360.0 * 64.0));
03543
03544 g_object_unref (G_OBJECT (gc));
03545 }
03546 break;
03547
03548 case META_DRAW_CLIP:
03549 break;
03550
03551 case META_DRAW_TINT:
03552 {
03553 int rx, ry, rwidth, rheight;
03554 gboolean needs_alpha;
03555
03556 needs_alpha = op->data.tint.alpha_spec &&
03557 (op->data.tint.alpha_spec->n_alphas > 1 ||
03558 op->data.tint.alpha_spec->alphas[0] != 0xff);
03559
03560 rx = parse_x_position_unchecked (op->data.tint.x, env);
03561 ry = parse_y_position_unchecked (op->data.tint.y, env);
03562 rwidth = parse_size_unchecked (op->data.tint.width, env);
03563 rheight = parse_size_unchecked (op->data.tint.height, env);
03564
03565 if (!needs_alpha)
03566 {
03567 gc = get_gc_for_primitive (widget, drawable,
03568 op->data.tint.color_spec,
03569 clip, 0);
03570
03571 gdk_draw_rectangle (drawable, gc,
03572 TRUE,
03573 rx, ry, rwidth, rheight);
03574
03575 g_object_unref (G_OBJECT (gc));
03576 }
03577 else
03578 {
03579 GdkPixbuf *pixbuf;
03580
03581 pixbuf = draw_op_as_pixbuf (op, widget, info,
03582 rwidth, rheight);
03583
03584 if (pixbuf)
03585 {
03586 render_pixbuf (drawable, clip, pixbuf, rx, ry);
03587
03588 g_object_unref (G_OBJECT (pixbuf));
03589 }
03590 }
03591 }
03592 break;
03593
03594 case META_DRAW_GRADIENT:
03595 {
03596 int rx, ry, rwidth, rheight;
03597 GdkPixbuf *pixbuf;
03598
03599 rx = parse_x_position_unchecked (op->data.gradient.x, env);
03600 ry = parse_y_position_unchecked (op->data.gradient.y, env);
03601 rwidth = parse_size_unchecked (op->data.gradient.width, env);
03602 rheight = parse_size_unchecked (op->data.gradient.height, env);
03603
03604 pixbuf = draw_op_as_pixbuf (op, widget, info,
03605 rwidth, rheight);
03606
03607 if (pixbuf)
03608 {
03609 render_pixbuf (drawable, clip, pixbuf, rx, ry);
03610
03611 g_object_unref (G_OBJECT (pixbuf));
03612 }
03613 }
03614 break;
03615
03616 case META_DRAW_IMAGE:
03617 {
03618 int rx, ry, rwidth, rheight;
03619 GdkPixbuf *pixbuf;
03620
03621 if (op->data.image.pixbuf)
03622 {
03623 env->object_width = gdk_pixbuf_get_width (op->data.image.pixbuf);
03624 env->object_height = gdk_pixbuf_get_height (op->data.image.pixbuf);
03625 }
03626
03627 rwidth = parse_size_unchecked (op->data.image.width, env);
03628 rheight = parse_size_unchecked (op->data.image.height, env);
03629
03630 pixbuf = draw_op_as_pixbuf (op, widget, info,
03631 rwidth, rheight);
03632
03633 if (pixbuf)
03634 {
03635 rx = parse_x_position_unchecked (op->data.image.x, env);
03636 ry = parse_y_position_unchecked (op->data.image.y, env);
03637
03638 render_pixbuf (drawable, clip, pixbuf, rx, ry);
03639
03640 g_object_unref (G_OBJECT (pixbuf));
03641 }
03642 }
03643 break;
03644
03645 case META_DRAW_GTK_ARROW:
03646 {
03647 int rx, ry, rwidth, rheight;
03648
03649 rx = parse_x_position_unchecked (op->data.gtk_arrow.x, env);
03650 ry = parse_y_position_unchecked (op->data.gtk_arrow.y, env);
03651 rwidth = parse_size_unchecked (op->data.gtk_arrow.width, env);
03652 rheight = parse_size_unchecked (op->data.gtk_arrow.height, env);
03653
03654 gtk_paint_arrow (widget->style,
03655 drawable,
03656 op->data.gtk_arrow.state,
03657 op->data.gtk_arrow.shadow,
03658 (GdkRectangle*) clip,
03659 widget,
03660 "metacity",
03661 op->data.gtk_arrow.arrow,
03662 op->data.gtk_arrow.filled,
03663 rx, ry, rwidth, rheight);
03664 }
03665 break;
03666
03667 case META_DRAW_GTK_BOX:
03668 {
03669 int rx, ry, rwidth, rheight;
03670
03671 rx = parse_x_position_unchecked (op->data.gtk_box.x, env);
03672 ry = parse_y_position_unchecked (op->data.gtk_box.y, env);
03673 rwidth = parse_size_unchecked (op->data.gtk_box.width, env);
03674 rheight = parse_size_unchecked (op->data.gtk_box.height, env);
03675
03676 gtk_paint_box (widget->style,
03677 drawable,
03678 op->data.gtk_box.state,
03679 op->data.gtk_box.shadow,
03680 (GdkRectangle*) clip,
03681 widget,
03682 "metacity",
03683 rx, ry, rwidth, rheight);
03684 }
03685 break;
03686
03687 case META_DRAW_GTK_VLINE:
03688 {
03689 int rx, ry1, ry2;
03690
03691 rx = parse_x_position_unchecked (op->data.gtk_vline.x, env);
03692 ry1 = parse_y_position_unchecked (op->data.gtk_vline.y1, env);
03693 ry2 = parse_y_position_unchecked (op->data.gtk_vline.y2, env);
03694
03695 gtk_paint_vline (widget->style,
03696 drawable,
03697 op->data.gtk_vline.state,
03698 (GdkRectangle*) clip,
03699 widget,
03700 "metacity",
03701 ry1, ry2, rx);
03702 }
03703 break;
03704
03705 case META_DRAW_ICON:
03706 {
03707 int rx, ry, rwidth, rheight;
03708 GdkPixbuf *pixbuf;
03709
03710 rwidth = parse_size_unchecked (op->data.icon.width, env);
03711 rheight = parse_size_unchecked (op->data.icon.height, env);
03712
03713 pixbuf = draw_op_as_pixbuf (op, widget, info,
03714 rwidth, rheight);
03715
03716 if (pixbuf)
03717 {
03718 rx = parse_x_position_unchecked (op->data.icon.x, env);
03719 ry = parse_y_position_unchecked (op->data.icon.y, env);
03720
03721 render_pixbuf (drawable, clip, pixbuf, rx, ry);
03722
03723 g_object_unref (G_OBJECT (pixbuf));
03724 }
03725 }
03726 break;
03727
03728 case META_DRAW_TITLE:
03729 if (info->title_layout)
03730 {
03731 int rx, ry;
03732
03733 gc = get_gc_for_primitive (widget, drawable,
03734 op->data.title.color_spec,
03735 clip, 0);
03736
03737 rx = parse_x_position_unchecked (op->data.title.x, env);
03738 ry = parse_y_position_unchecked (op->data.title.y, env);
03739
03740 gdk_draw_layout (drawable, gc,
03741 rx, ry,
03742 info->title_layout);
03743
03744 g_object_unref (G_OBJECT (gc));
03745 }
03746 break;
03747
03748 case META_DRAW_OP_LIST:
03749 {
03750 MetaRectangle d_rect;
03751
03752 d_rect.x = parse_x_position_unchecked (op->data.op_list.x, env);
03753 d_rect.y = parse_y_position_unchecked (op->data.op_list.y, env);
03754 d_rect.width = parse_size_unchecked (op->data.op_list.width, env);
03755 d_rect.height = parse_size_unchecked (op->data.op_list.height, env);
03756
03757 meta_draw_op_list_draw (op->data.op_list.op_list,
03758 widget, drawable, clip, info,
03759 d_rect);
03760 }
03761 break;
03762
03763 case META_DRAW_TILE:
03764 {
03765 int rx, ry, rwidth, rheight;
03766 int tile_xoffset, tile_yoffset;
03767 GdkRectangle new_clip;
03768 MetaRectangle tile;
03769
03770 rx = parse_x_position_unchecked (op->data.tile.x, env);
03771 ry = parse_y_position_unchecked (op->data.tile.y, env);
03772 rwidth = parse_size_unchecked (op->data.tile.width, env);
03773 rheight = parse_size_unchecked (op->data.tile.height, env);
03774
03775 new_clip.x = rx;
03776 new_clip.y = ry;
03777 new_clip.width = rwidth;
03778 new_clip.height = rheight;
03779
03780 if (clip == NULL || gdk_rectangle_intersect ((GdkRectangle*)clip, &new_clip,
03781 &new_clip))
03782 {
03783 tile_xoffset = parse_x_position_unchecked (op->data.tile.tile_xoffset, env);
03784 tile_yoffset = parse_y_position_unchecked (op->data.tile.tile_yoffset, env);
03785
03786 tile_xoffset -= rect.x;
03787 tile_yoffset -= rect.y;
03788
03789 tile.width = parse_size_unchecked (op->data.tile.tile_width, env);
03790 tile.height = parse_size_unchecked (op->data.tile.tile_height, env);
03791
03792 tile.x = rx - tile_xoffset;
03793
03794 while (tile.x < (rx + rwidth))
03795 {
03796 tile.y = ry - tile_yoffset;
03797 while (tile.y < (ry + rheight))
03798 {
03799 meta_draw_op_list_draw (op->data.tile.op_list,
03800 widget, drawable, &new_clip, info,
03801 tile);
03802
03803 tile.y += tile.height;
03804 }
03805
03806 tile.x += tile.width;
03807 }
03808 }
03809 }
03810 break;
03811 }
03812 }
03813
03814 void
03815 meta_draw_op_draw (const MetaDrawOp *op,
03816 GtkWidget *widget,
03817 GdkDrawable *drawable,
03818 const GdkRectangle *clip,
03819 const MetaDrawInfo *info,
03820 MetaRectangle logical_region)
03821 {
03822 MetaPositionExprEnv env;
03823
03824 fill_env (&env, info, logical_region);
03825
03826 meta_draw_op_draw_with_env (op, widget, drawable, clip,
03827 info, logical_region,
03828 &env);
03829
03830 }
03831
03832 MetaDrawOpList*
03833 meta_draw_op_list_new (int n_preallocs)
03834 {
03835 MetaDrawOpList *op_list;
03836
03837 g_return_val_if_fail (n_preallocs >= 0, NULL);
03838
03839 op_list = g_new (MetaDrawOpList, 1);
03840
03841 op_list->refcount = 1;
03842 op_list->n_allocated = n_preallocs;
03843 op_list->ops = g_new (MetaDrawOp*, op_list->n_allocated);
03844 op_list->n_ops = 0;
03845
03846 return op_list;
03847 }
03848
03849 void
03850 meta_draw_op_list_ref (MetaDrawOpList *op_list)
03851 {
03852 g_return_if_fail (op_list != NULL);
03853
03854 op_list->refcount += 1;
03855 }
03856
03857 void
03858 meta_draw_op_list_unref (MetaDrawOpList *op_list)
03859 {
03860 g_return_if_fail (op_list != NULL);
03861 g_return_if_fail (op_list->refcount > 0);
03862
03863 op_list->refcount -= 1;
03864
03865 if (op_list->refcount == 0)
03866 {
03867 int i;
03868
03869 for (i = 0; i < op_list->n_ops; i++)
03870 meta_draw_op_free (op_list->ops[i]);
03871
03872 g_free (op_list->ops);
03873
03874 DEBUG_FILL_STRUCT (op_list);
03875 g_free (op_list);
03876 }
03877 }
03878
03879 void
03880 meta_draw_op_list_draw (const MetaDrawOpList *op_list,
03881 GtkWidget *widget,
03882 GdkDrawable *drawable,
03883 const GdkRectangle *clip,
03884 const MetaDrawInfo *info,
03885 MetaRectangle rect)
03886 {
03887 int i;
03888 GdkRectangle active_clip;
03889 GdkRectangle orig_clip;
03890 MetaPositionExprEnv env;
03891
03892 if (op_list->n_ops == 0)
03893 return;
03894
03895 fill_env (&env, info, rect);
03896
03897
03898
03899
03900
03901
03902
03903
03904
03905
03906
03907
03908 if (clip)
03909 {
03910 orig_clip = *clip;
03911 }
03912 else
03913 {
03914 orig_clip.x = rect.x;
03915 orig_clip.y = rect.y;
03916 orig_clip.width = rect.width;
03917 orig_clip.height = rect.height;
03918 }
03919
03920 active_clip = orig_clip;
03921
03922 for (i = 0; i < op_list->n_ops; i++)
03923 {
03924 MetaDrawOp *op = op_list->ops[i];
03925
03926 if (op->type == META_DRAW_CLIP)
03927 {
03928 active_clip.x = parse_x_position_unchecked (op->data.clip.x, &env);
03929 active_clip.y = parse_y_position_unchecked (op->data.clip.y, &env);
03930 active_clip.width = parse_size_unchecked (op->data.clip.width, &env);
03931 active_clip.height = parse_size_unchecked (op->data.clip.height, &env);
03932
03933 gdk_rectangle_intersect (&orig_clip, &active_clip, &active_clip);
03934 }
03935 else if (active_clip.width > 0 &&
03936 active_clip.height > 0)
03937 {
03938 meta_draw_op_draw_with_env (op,
03939 widget, drawable, &active_clip, info,
03940 rect,
03941 &env);
03942 }
03943 }
03944 }
03945
03946 void
03947 meta_draw_op_list_append (MetaDrawOpList *op_list,
03948 MetaDrawOp *op)
03949 {
03950 if (op_list->n_ops == op_list->n_allocated)
03951 {
03952 op_list->n_allocated *= 2;
03953 op_list->ops = g_renew (MetaDrawOp*, op_list->ops, op_list->n_allocated);
03954 }
03955
03956 op_list->ops[op_list->n_ops] = op;
03957 op_list->n_ops += 1;
03958 }
03959
03960 gboolean
03961 meta_draw_op_list_validate (MetaDrawOpList *op_list,
03962 GError **error)
03963 {
03964 g_return_val_if_fail (op_list != NULL, FALSE);
03965
03966
03967
03968 return TRUE;
03969 }
03970
03971
03972
03973
03974
03975 gboolean
03976 meta_draw_op_list_contains (MetaDrawOpList *op_list,
03977 MetaDrawOpList *child)
03978 {
03979 int i;
03980
03981
03982
03983 for (i = 0; i < op_list->n_ops; i++)
03984 {
03985 if (op_list->ops[i]->type == META_DRAW_OP_LIST)
03986 {
03987 if (op_list->ops[i]->data.op_list.op_list == child)
03988 return TRUE;
03989
03990 if (meta_draw_op_list_contains (op_list->ops[i]->data.op_list.op_list,
03991 child))
03992 return TRUE;
03993 }
03994 else if (op_list->ops[i]->type == META_DRAW_TILE)
03995 {
03996 if (op_list->ops[i]->data.tile.op_list == child)
03997 return TRUE;
03998
03999 if (meta_draw_op_list_contains (op_list->ops[i]->data.tile.op_list,
04000 child))
04001 return TRUE;
04002 }
04003 }
04004
04005 return FALSE;
04006 }
04007
04017 MetaFrameStyle*
04018 meta_frame_style_new (MetaFrameStyle *parent)
04019 {
04020 MetaFrameStyle *style;
04021
04022 style = g_new0 (MetaFrameStyle, 1);
04023
04024 style->refcount = 1;
04025
04026
04027 style->window_background_alpha = 255;
04028
04029 style->parent = parent;
04030 if (parent)
04031 meta_frame_style_ref (parent);
04032
04033 return style;
04034 }
04035
04042 void
04043 meta_frame_style_ref (MetaFrameStyle *style)
04044 {
04045 g_return_if_fail (style != NULL);
04046
04047 style->refcount += 1;
04048 }
04049
04050 static void
04051 free_button_ops (MetaDrawOpList *op_lists[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST])
04052 {
04053 int i, j;
04054
04055 for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
04056 for (j = 0; j < META_BUTTON_STATE_LAST; j++)
04057 if (op_lists[i][j])
04058 meta_draw_op_list_unref (op_lists[i][j]);
04059 }
04060
04061 void
04062 meta_frame_style_unref (MetaFrameStyle *style)
04063 {
04064 g_return_if_fail (style != NULL);
04065 g_return_if_fail (style->refcount > 0);
04066
04067 style->refcount -= 1;
04068
04069 if (style->refcount == 0)
04070 {
04071 int i;
04072
04073 free_button_ops (style->buttons);
04074
04075 for (i = 0; i < META_FRAME_PIECE_LAST; i++)
04076 if (style->pieces[i])
04077 meta_draw_op_list_unref (style->pieces[i]);
04078
04079 if (style->layout)
04080 meta_frame_layout_unref (style->layout);
04081
04082 if (style->window_background_color)
04083 meta_color_spec_free (style->window_background_color);
04084
04085
04086 if (style->parent)
04087 meta_frame_style_unref (style->parent);
04088
04089 DEBUG_FILL_STRUCT (style);
04090 g_free (style);
04091 }
04092 }
04093
04094 static MetaDrawOpList*
04095 get_button (MetaFrameStyle *style,
04096 MetaButtonType type,
04097 MetaButtonState state)
04098 {
04099 MetaDrawOpList *op_list;
04100 MetaFrameStyle *parent;
04101
04102 parent = style;
04103 op_list = NULL;
04104 while (parent && op_list == NULL)
04105 {
04106 op_list = parent->buttons[type][state];
04107 parent = parent->parent;
04108 }
04109
04110
04111
04112
04113
04114 if (op_list == NULL &&
04115 (type == META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND ||
04116 type == META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND))
04117 return get_button (style, META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND,
04118 state);
04119
04120 if (op_list == NULL &&
04121 (type == META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND ||
04122 type == META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND))
04123 return get_button (style, META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND,
04124 state);
04125
04126
04127 if (op_list == NULL &&
04128 state == META_BUTTON_STATE_PRELIGHT)
04129 return get_button (style, type, META_BUTTON_STATE_NORMAL);
04130
04131 return op_list;
04132 }
04133
04134 gboolean
04135 meta_frame_style_validate (MetaFrameStyle *style,
04136 guint current_theme_version,
04137 GError **error)
04138 {
04139 int i, j;
04140
04141 g_return_val_if_fail (style != NULL, FALSE);
04142 g_return_val_if_fail (style->layout != NULL, FALSE);
04143
04144 for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
04145 {
04146
04147 if (i >= META_BUTTON_TYPE_CLOSE)
04148 {
04149 for (j = 0; j < META_BUTTON_STATE_LAST; j++)
04150 {
04151 if (get_button (style, i, j) == NULL &&
04152 meta_theme_earliest_version_with_button (i) <= current_theme_version
04153 )
04154 {
04155 g_set_error (error, META_THEME_ERROR,
04156 META_THEME_ERROR_FAILED,
04157 _("<button function=\"%s\" state=\"%s\" draw_ops=\"whatever\"/> must be specified for this frame style"),
04158 meta_button_type_to_string (i),
04159 meta_button_state_to_string (j));
04160 return FALSE;
04161 }
04162 }
04163 }
04164 }
04165
04166 return TRUE;
04167 }
04168
04169 static void
04170 button_rect (MetaButtonType type,
04171 const MetaFrameGeometry *fgeom,
04172 int middle_background_offset,
04173 GdkRectangle *rect)
04174 {
04175 switch (type)
04176 {
04177 case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
04178 *rect = fgeom->left_left_background;
04179 break;
04180
04181 case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
04182 *rect = fgeom->left_middle_backgrounds[middle_background_offset];
04183 break;
04184
04185 case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
04186 *rect = fgeom->left_right_background;
04187 break;
04188
04189 case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
04190 *rect = fgeom->right_left_background;
04191 break;
04192
04193 case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
04194 *rect = fgeom->right_middle_backgrounds[middle_background_offset];
04195 break;
04196
04197 case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
04198 *rect = fgeom->right_right_background;
04199 break;
04200
04201 case META_BUTTON_TYPE_CLOSE:
04202 *rect = fgeom->close_rect.visible;
04203 break;
04204
04205 case META_BUTTON_TYPE_SHADE:
04206 *rect = fgeom->shade_rect.visible;
04207 break;
04208
04209 case META_BUTTON_TYPE_UNSHADE:
04210 *rect = fgeom->unshade_rect.visible;
04211 break;
04212
04213 case META_BUTTON_TYPE_ABOVE:
04214 *rect = fgeom->above_rect.visible;
04215 break;
04216
04217 case META_BUTTON_TYPE_UNABOVE:
04218 *rect = fgeom->unabove_rect.visible;
04219 break;
04220
04221 case META_BUTTON_TYPE_STICK:
04222 *rect = fgeom->stick_rect.visible;
04223 break;
04224
04225 case META_BUTTON_TYPE_UNSTICK:
04226 *rect = fgeom->unstick_rect.visible;
04227 break;
04228
04229 case META_BUTTON_TYPE_MAXIMIZE:
04230 *rect = fgeom->max_rect.visible;
04231 break;
04232
04233 case META_BUTTON_TYPE_MINIMIZE:
04234 *rect = fgeom->min_rect.visible;
04235 break;
04236
04237 case META_BUTTON_TYPE_MENU:
04238 *rect = fgeom->menu_rect.visible;
04239 break;
04240
04241 case META_BUTTON_TYPE_LAST:
04242 g_assert_not_reached ();
04243 break;
04244 }
04245 }
04246
04247 void
04248 meta_frame_style_draw (MetaFrameStyle *style,
04249 GtkWidget *widget,
04250 GdkDrawable *drawable,
04251 int x_offset,
04252 int y_offset,
04253 const GdkRectangle *clip,
04254 const MetaFrameGeometry *fgeom,
04255 int client_width,
04256 int client_height,
04257 PangoLayout *title_layout,
04258 int text_height,
04259 MetaButtonState button_states[META_BUTTON_TYPE_LAST],
04260 GdkPixbuf *mini_icon,
04261 GdkPixbuf *icon)
04262 {
04263 int i, j;
04264 GdkRectangle titlebar_rect;
04265 GdkRectangle left_titlebar_edge;
04266 GdkRectangle right_titlebar_edge;
04267 GdkRectangle bottom_titlebar_edge;
04268 GdkRectangle top_titlebar_edge;
04269 GdkRectangle left_edge, right_edge, bottom_edge;
04270 PangoRectangle extents;
04271 MetaDrawInfo draw_info;
04272
04273 titlebar_rect.x = 0;
04274 titlebar_rect.y = 0;
04275 titlebar_rect.width = fgeom->width;
04276 titlebar_rect.height = fgeom->top_height;
04277
04278 left_titlebar_edge.x = titlebar_rect.x;
04279 left_titlebar_edge.y = titlebar_rect.y + fgeom->top_titlebar_edge;
04280 left_titlebar_edge.width = fgeom->left_titlebar_edge;
04281 left_titlebar_edge.height = titlebar_rect.height - fgeom->top_titlebar_edge - fgeom->bottom_titlebar_edge;
04282
04283 right_titlebar_edge.y = left_titlebar_edge.y;
04284 right_titlebar_edge.height = left_titlebar_edge.height;
04285 right_titlebar_edge.width = fgeom->right_titlebar_edge;
04286 right_titlebar_edge.x = titlebar_rect.x + titlebar_rect.width - right_titlebar_edge.width;
04287
04288 top_titlebar_edge.x = titlebar_rect.x;
04289 top_titlebar_edge.y = titlebar_rect.y;
04290 top_titlebar_edge.width = titlebar_rect.width;
04291 top_titlebar_edge.height = fgeom->top_titlebar_edge;
04292
04293 bottom_titlebar_edge.x = titlebar_rect.x;
04294 bottom_titlebar_edge.width = titlebar_rect.width;
04295 bottom_titlebar_edge.height = fgeom->bottom_titlebar_edge;
04296 bottom_titlebar_edge.y = titlebar_rect.y + titlebar_rect.height - bottom_titlebar_edge.height;
04297
04298 left_edge.x = 0;
04299 left_edge.y = fgeom->top_height;
04300 left_edge.width = fgeom->left_width;
04301 left_edge.height = fgeom->height - fgeom->top_height - fgeom->bottom_height;
04302
04303 right_edge.x = fgeom->width - fgeom->right_width;
04304 right_edge.y = fgeom->top_height;
04305 right_edge.width = fgeom->right_width;
04306 right_edge.height = fgeom->height - fgeom->top_height - fgeom->bottom_height;
04307
04308 bottom_edge.x = 0;
04309 bottom_edge.y = fgeom->height - fgeom->bottom_height;
04310 bottom_edge.width = fgeom->width;
04311 bottom_edge.height = fgeom->bottom_height;
04312
04313 if (title_layout)
04314 pango_layout_get_pixel_extents (title_layout,
04315 NULL, &extents);
04316
04317 draw_info.mini_icon = mini_icon;
04318 draw_info.icon = icon;
04319 draw_info.title_layout = title_layout;
04320 draw_info.title_layout_width = title_layout ? extents.width : 0;
04321 draw_info.title_layout_height = title_layout ? extents.height : 0;
04322 draw_info.fgeom = fgeom;
04323
04324
04325 i = 0;
04326 while (i < META_FRAME_PIECE_LAST)
04327 {
04328 GdkRectangle rect;
04329 GdkRectangle combined_clip;
04330
04331 switch ((MetaFramePiece) i)
04332 {
04333 case META_FRAME_PIECE_ENTIRE_BACKGROUND:
04334 rect.x = 0;
04335 rect.y = 0;
04336 rect.width = fgeom->width;
04337 rect.height = fgeom->height;
04338 break;
04339
04340 case META_FRAME_PIECE_TITLEBAR:
04341 rect = titlebar_rect;
04342 break;
04343
04344 case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE:
04345 rect = left_titlebar_edge;
04346 break;
04347
04348 case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE:
04349 rect = right_titlebar_edge;
04350 break;
04351
04352 case META_FRAME_PIECE_TOP_TITLEBAR_EDGE:
04353 rect = top_titlebar_edge;
04354 break;
04355
04356 case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE:
04357 rect = bottom_titlebar_edge;
04358 break;
04359
04360 case META_FRAME_PIECE_TITLEBAR_MIDDLE:
04361 rect.x = left_titlebar_edge.x + left_titlebar_edge.width;
04362 rect.y = top_titlebar_edge.y + top_titlebar_edge.height;
04363 rect.width = titlebar_rect.width - left_titlebar_edge.width -
04364 right_titlebar_edge.width;
04365 rect.height = titlebar_rect.height - top_titlebar_edge.height - bottom_titlebar_edge.height;
04366 break;
04367
04368 case META_FRAME_PIECE_TITLE:
04369 rect = fgeom->title_rect;
04370 break;
04371
04372 case META_FRAME_PIECE_LEFT_EDGE:
04373 rect = left_edge;
04374 break;
04375
04376 case META_FRAME_PIECE_RIGHT_EDGE:
04377 rect = right_edge;
04378 break;
04379
04380 case META_FRAME_PIECE_BOTTOM_EDGE:
04381 rect = bottom_edge;
04382 break;
04383
04384 case META_FRAME_PIECE_OVERLAY:
04385 rect.x = 0;
04386 rect.y = 0;
04387 rect.width = fgeom->width;
04388 rect.height = fgeom->height;
04389 break;
04390
04391 case META_FRAME_PIECE_LAST:
04392 g_assert_not_reached ();
04393 break;
04394 }
04395
04396 rect.x += x_offset;
04397 rect.y += y_offset;
04398
04399 if (clip == NULL)
04400 combined_clip = rect;
04401 else
04402 gdk_rectangle_intersect ((GdkRectangle*) clip,
04403 &rect,
04404 &combined_clip);
04405
04406 if (combined_clip.width > 0 && combined_clip.height > 0)
04407 {
04408 MetaDrawOpList *op_list;
04409 MetaFrameStyle *parent;
04410
04411 parent = style;
04412 op_list = NULL;
04413 while (parent && op_list == NULL)
04414 {
04415 op_list = parent->pieces[i];
04416 parent = parent->parent;
04417 }
04418
04419 if (op_list)
04420 {
04421 MetaRectangle m_rect;
04422 m_rect = meta_rect (rect.x, rect.y, rect.width, rect.height);
04423 meta_draw_op_list_draw (op_list,
04424 widget,
04425 drawable,
04426 &combined_clip,
04427 &draw_info,
04428 m_rect);
04429 }
04430 }
04431
04432
04433
04434 if ((i + 1) == META_FRAME_PIECE_OVERLAY)
04435 {
04436 int middle_bg_offset;
04437
04438 middle_bg_offset = 0;
04439 j = 0;
04440 while (j < META_BUTTON_TYPE_LAST)
04441 {
04442 button_rect (j, fgeom, middle_bg_offset, &rect);
04443
04444 rect.x += x_offset;
04445 rect.y += y_offset;
04446
04447 if (clip == NULL)
04448 combined_clip = rect;
04449 else
04450 gdk_rectangle_intersect ((GdkRectangle*) clip,
04451 &rect,
04452 &combined_clip);
04453
04454 if (combined_clip.width > 0 && combined_clip.height > 0)
04455 {
04456 MetaDrawOpList *op_list;
04457
04458 op_list = get_button (style, j, button_states[j]);
04459
04460 if (op_list)
04461 {
04462 MetaRectangle m_rect;
04463 m_rect = meta_rect (rect.x, rect.y,
04464 rect.width, rect.height);
04465 meta_draw_op_list_draw (op_list,
04466 widget,
04467 drawable,
04468 &combined_clip,
04469 &draw_info,
04470 m_rect);
04471 }
04472 }
04473
04474
04475 if ((j == META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND ||
04476 j == META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND) &&
04477 middle_bg_offset < MAX_MIDDLE_BACKGROUNDS)
04478 {
04479 ++middle_bg_offset;
04480 }
04481 else
04482 {
04483 middle_bg_offset = 0;
04484 ++j;
04485 }
04486 }
04487 }
04488
04489 ++i;
04490 }
04491 }
04492
04493 MetaFrameStyleSet*
04494 meta_frame_style_set_new (MetaFrameStyleSet *parent)
04495 {
04496 MetaFrameStyleSet *style_set;
04497
04498 style_set = g_new0 (MetaFrameStyleSet, 1);
04499
04500 style_set->parent = parent;
04501 if (parent)
04502 meta_frame_style_set_ref (parent);
04503
04504 style_set->refcount = 1;
04505
04506 return style_set;
04507 }
04508
04509 static void
04510 free_focus_styles (MetaFrameStyle *focus_styles[META_FRAME_FOCUS_LAST])
04511 {
04512 int i;
04513
04514 for (i = 0; i < META_FRAME_FOCUS_LAST; i++)
04515 if (focus_styles[i])
04516 meta_frame_style_unref (focus_styles[i]);
04517 }
04518
04519 void
04520 meta_frame_style_set_ref (MetaFrameStyleSet *style_set)
04521 {
04522 g_return_if_fail (style_set != NULL);
04523
04524 style_set->refcount += 1;
04525 }
04526
04527 void
04528 meta_frame_style_set_unref (MetaFrameStyleSet *style_set)
04529 {
04530 g_return_if_fail (style_set != NULL);
04531 g_return_if_fail (style_set->refcount > 0);
04532
04533 style_set->refcount -= 1;
04534
04535 if (style_set->refcount == 0)
04536 {
04537 int i;
04538
04539 for (i = 0; i < META_FRAME_RESIZE_LAST; i++)
04540 {
04541 free_focus_styles (style_set->normal_styles[i]);
04542 free_focus_styles (style_set->shaded_styles[i]);
04543 }
04544
04545 free_focus_styles (style_set->maximized_styles);
04546 free_focus_styles (style_set->maximized_and_shaded_styles);
04547
04548 if (style_set->parent)
04549 meta_frame_style_set_unref (style_set->parent);
04550
04551 DEBUG_FILL_STRUCT (style_set);
04552 g_free (style_set);
04553 }
04554 }
04555
04556
04557 static MetaFrameStyle*
04558 get_style (MetaFrameStyleSet *style_set,
04559 MetaFrameState state,
04560 MetaFrameResize resize,
04561 MetaFrameFocus focus)
04562 {
04563 MetaFrameStyle *style;
04564
04565 style = NULL;
04566
04567 switch (state)
04568 {
04569 case META_FRAME_STATE_NORMAL:
04570 case META_FRAME_STATE_SHADED:
04571 {
04572 if (state == META_FRAME_STATE_SHADED)
04573 style = style_set->shaded_styles[resize][focus];
04574 else
04575 style = style_set->normal_styles[resize][focus];
04576
04577
04578 if (style == NULL && style_set->parent)
04579 style = get_style (style_set->parent, state, resize, focus);
04580
04581
04582 if (style == NULL &&
04583 resize != META_FRAME_RESIZE_BOTH)
04584 style = get_style (style_set, state, META_FRAME_RESIZE_BOTH, focus);
04585 }
04586 break;
04587 default:
04588 {
04589 MetaFrameStyle **styles;
04590
04591 styles = NULL;
04592
04593 switch (state)
04594 {
04595 case META_FRAME_STATE_MAXIMIZED:
04596 styles = style_set->maximized_styles;
04597 break;
04598 case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
04599 styles = style_set->maximized_and_shaded_styles;
04600 break;
04601 case META_FRAME_STATE_NORMAL:
04602 case META_FRAME_STATE_SHADED:
04603 case META_FRAME_STATE_LAST:
04604 g_assert_not_reached ();
04605 break;
04606 }
04607
04608 style = styles[focus];
04609
04610
04611 if (style == NULL && style_set->parent)
04612 style = get_style (style_set->parent, state, resize, focus);
04613 }
04614 }
04615
04616 return style;
04617 }
04618
04619 static gboolean
04620 check_state (MetaFrameStyleSet *style_set,
04621 MetaFrameState state,
04622 GError **error)
04623 {
04624 int i;
04625
04626 for (i = 0; i < META_FRAME_FOCUS_LAST; i++)
04627 {
04628 if (get_style (style_set, state,
04629 META_FRAME_RESIZE_NONE, i) == NULL)
04630 {
04631 g_set_error (error, META_THEME_ERROR,
04632 META_THEME_ERROR_FAILED,
04633 _("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
04634 meta_frame_state_to_string (state),
04635 meta_frame_resize_to_string (META_FRAME_RESIZE_NONE),
04636 meta_frame_focus_to_string (i));
04637 return FALSE;
04638 }
04639 }
04640
04641 return TRUE;
04642 }
04643
04644 gboolean
04645 meta_frame_style_set_validate (MetaFrameStyleSet *style_set,
04646 GError **error)
04647 {
04648 int i, j;
04649
04650 g_return_val_if_fail (style_set != NULL, FALSE);
04651
04652 for (i = 0; i < META_FRAME_RESIZE_LAST; i++)
04653 for (j = 0; j < META_FRAME_FOCUS_LAST; j++)
04654 if (get_style (style_set, META_FRAME_STATE_NORMAL, i, j) == NULL)
04655 {
04656 g_set_error (error, META_THEME_ERROR,
04657 META_THEME_ERROR_FAILED,
04658 _("Missing <frame state=\"%s\" resize=\"%s\" focus=\"%s\" style=\"whatever\"/>"),
04659 meta_frame_state_to_string (META_FRAME_STATE_NORMAL),
04660 meta_frame_resize_to_string (i),
04661 meta_frame_focus_to_string (j));
04662 return FALSE;
04663 }
04664
04665 if (!check_state (style_set, META_FRAME_STATE_SHADED, error))
04666 return FALSE;
04667
04668 if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED, error))
04669 return FALSE;
04670
04671 if (!check_state (style_set, META_FRAME_STATE_MAXIMIZED_AND_SHADED, error))
04672 return FALSE;
04673
04674 return TRUE;
04675 }
04676
04677 MetaTheme*
04678 meta_theme_get_current (void)
04679 {
04680 return meta_current_theme;
04681 }
04682
04683 void
04684 meta_theme_set_current (const char *name,
04685 gboolean force_reload)
04686 {
04687 MetaTheme *new_theme;
04688 GError *err;
04689
04690 meta_topic (META_DEBUG_THEMES, "Setting current theme to \"%s\"\n", name);
04691
04692 if (!force_reload &&
04693 meta_current_theme &&
04694 strcmp (name, meta_current_theme->name) == 0)
04695 return;
04696
04697 err = NULL;
04698 new_theme = meta_theme_load (name, &err);
04699
04700 if (new_theme == NULL)
04701 {
04702 meta_warning (_("Failed to load theme \"%s\": %s\n"),
04703 name, err->message);
04704 g_error_free (err);
04705 }
04706 else
04707 {
04708 if (meta_current_theme)
04709 meta_theme_free (meta_current_theme);
04710
04711 meta_current_theme = new_theme;
04712
04713 meta_topic (META_DEBUG_THEMES, "New theme is \"%s\"\n", meta_current_theme->name);
04714 }
04715 }
04716
04717 MetaTheme*
04718 meta_theme_new (void)
04719 {
04720 MetaTheme *theme;
04721
04722 theme = g_new0 (MetaTheme, 1);
04723
04724 theme->images_by_filename =
04725 g_hash_table_new_full (g_str_hash,
04726 g_str_equal,
04727 g_free,
04728 (GDestroyNotify) g_object_unref);
04729
04730 theme->layouts_by_name =
04731 g_hash_table_new_full (g_str_hash,
04732 g_str_equal,
04733 g_free,
04734 (GDestroyNotify) meta_frame_layout_unref);
04735
04736 theme->draw_op_lists_by_name =
04737 g_hash_table_new_full (g_str_hash,
04738 g_str_equal,
04739 g_free,
04740 (GDestroyNotify) meta_draw_op_list_unref);
04741
04742 theme->styles_by_name =
04743 g_hash_table_new_full (g_str_hash,
04744 g_str_equal,
04745 g_free,
04746 (GDestroyNotify) meta_frame_style_unref);
04747
04748 theme->style_sets_by_name =
04749 g_hash_table_new_full (g_str_hash,
04750 g_str_equal,
04751 g_free,
04752 (GDestroyNotify) meta_frame_style_set_unref);
04753
04754
04755
04756 theme->quark_width = g_quark_from_static_string ("width");
04757 theme->quark_height = g_quark_from_static_string ("height");
04758 theme->quark_object_width = g_quark_from_static_string ("object_width");
04759 theme->quark_object_height = g_quark_from_static_string ("object_height");
04760 theme->quark_left_width = g_quark_from_static_string ("left_width");
04761 theme->quark_right_width = g_quark_from_static_string ("right_width");
04762 theme->quark_top_height = g_quark_from_static_string ("top_height");
04763 theme->quark_bottom_height = g_quark_from_static_string ("bottom_height");
04764 theme->quark_mini_icon_width = g_quark_from_static_string ("mini_icon_width");
04765 theme->quark_mini_icon_height = g_quark_from_static_string ("mini_icon_height");
04766 theme->quark_icon_width = g_quark_from_static_string ("icon_width");
04767 theme->quark_icon_height = g_quark_from_static_string ("icon_height");
04768 theme->quark_title_width = g_quark_from_static_string ("title_width");
04769 theme->quark_title_height = g_quark_from_static_string ("title_height");
04770 return theme;
04771 }
04772
04773
04774 void
04775 meta_theme_free (MetaTheme *theme)
04776 {
04777 int i;
04778
04779 g_return_if_fail (theme != NULL);
04780
04781 g_free (theme->name);
04782 g_free (theme->dirname);
04783 g_free (theme->filename);
04784 g_free (theme->readable_name);
04785 g_free (theme->date);
04786 g_free (theme->description);
04787 g_free (theme->author);
04788 g_free (theme->copyright);
04789
04790
04791
04792 if (theme->integer_constants)
04793 g_hash_table_destroy (theme->integer_constants);
04794 if (theme->images_by_filename)
04795 g_hash_table_destroy (theme->images_by_filename);
04796 if (theme->layouts_by_name)
04797 g_hash_table_destroy (theme->layouts_by_name);
04798 if (theme->draw_op_lists_by_name)
04799 g_hash_table_destroy (theme->draw_op_lists_by_name);
04800 if (theme->styles_by_name)
04801 g_hash_table_destroy (theme->styles_by_name);
04802 if (theme->style_sets_by_name)
04803 g_hash_table_destroy (theme->style_sets_by_name);
04804
04805 for (i = 0; i < META_FRAME_TYPE_LAST; i++)
04806 if (theme->style_sets_by_type[i])
04807 meta_frame_style_set_unref (theme->style_sets_by_type[i]);
04808
04809 DEBUG_FILL_STRUCT (theme);
04810 g_free (theme);
04811 }
04812
04813 gboolean
04814 meta_theme_validate (MetaTheme *theme,
04815 GError **error)
04816 {
04817 int i;
04818
04819 g_return_val_if_fail (theme != NULL, FALSE);
04820
04821
04822
04823 g_assert (theme->name);
04824
04825 if (theme->readable_name == NULL)
04826 {
04827 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
04828 _("No <%s> set for theme \"%s\""), "name", theme->name);
04829 return FALSE;
04830 }
04831
04832 if (theme->author == NULL)
04833 {
04834 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
04835 _("No <%s> set for theme \"%s\""), "author", theme->name);
04836 return FALSE;
04837 }
04838
04839 if (theme->date == NULL)
04840 {
04841 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
04842 _("No <%s> set for theme \"%s\""), "date", theme->name);
04843 return FALSE;
04844 }
04845
04846 if (theme->description == NULL)
04847 {
04848 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
04849 _("No <%s> set for theme \"%s\""), "description", theme->name);
04850 return FALSE;
04851 }
04852
04853 if (theme->copyright == NULL)
04854 {
04855 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
04856 _("No <%s> set for theme \"%s\""), "copyright", theme->name);
04857 return FALSE;
04858 }
04859
04860 for (i = 0; i < (int)META_FRAME_TYPE_LAST; i++)
04861 if (theme->style_sets_by_type[i] == NULL)
04862 {
04863 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
04864 _("No frame style set for window type \"%s\" in theme \"%s\", add a <window type=\"%s\" style_set=\"whatever\"/> element"),
04865 meta_frame_type_to_string (i),
04866 theme->name,
04867 meta_frame_type_to_string (i));
04868
04869 return FALSE;
04870 }
04871
04872 return TRUE;
04873 }
04874
04875 GdkPixbuf*
04876 meta_theme_load_image (MetaTheme *theme,
04877 const char *filename,
04878 guint size_of_theme_icons,
04879 GError **error)
04880 {
04881 GdkPixbuf *pixbuf;
04882
04883 pixbuf = g_hash_table_lookup (theme->images_by_filename,
04884 filename);
04885
04886 if (pixbuf == NULL)
04887 {
04888
04889 if (g_str_has_prefix (filename, "theme:") &&
04890 META_THEME_ALLOWS (theme, META_THEME_IMAGES_FROM_ICON_THEMES))
04891 {
04892 pixbuf = gtk_icon_theme_load_icon (
04893 gtk_icon_theme_get_default (),
04894 filename+6,
04895 size_of_theme_icons,
04896 0,
04897 error);
04898 if (pixbuf == NULL) return NULL;
04899 }
04900 else
04901 {
04902 char *full_path;
04903 full_path = g_build_filename (theme->dirname, filename, NULL);
04904
04905 pixbuf = gdk_pixbuf_new_from_file (full_path, error);
04906 if (pixbuf == NULL)
04907 {
04908 g_free (full_path);
04909 return NULL;
04910 }
04911
04912 g_free (full_path);
04913 }
04914 g_hash_table_replace (theme->images_by_filename,
04915 g_strdup (filename),
04916 pixbuf);
04917 }
04918
04919 g_assert (pixbuf);
04920
04921 g_object_ref (G_OBJECT (pixbuf));
04922
04923 return pixbuf;
04924 }
04925
04926 static MetaFrameStyle*
04927 theme_get_style (MetaTheme *theme,
04928 MetaFrameType type,
04929 MetaFrameFlags flags)
04930 {
04931 MetaFrameState state;
04932 MetaFrameResize resize;
04933 MetaFrameFocus focus;
04934 MetaFrameStyle *style;
04935 MetaFrameStyleSet *style_set;
04936
04937 style_set = theme->style_sets_by_type[type];
04938
04939
04940
04941
04942 if (style_set == NULL)
04943 style_set = theme->style_sets_by_type[META_FRAME_TYPE_NORMAL];
04944 if (style_set == NULL)
04945 return NULL;
04946
04947 switch (flags & (META_FRAME_MAXIMIZED | META_FRAME_SHADED))
04948 {
04949 case 0:
04950 state = META_FRAME_STATE_NORMAL;
04951 break;
04952 case META_FRAME_MAXIMIZED:
04953 state = META_FRAME_STATE_MAXIMIZED;
04954 break;
04955 case META_FRAME_SHADED:
04956 state = META_FRAME_STATE_SHADED;
04957 break;
04958 case (META_FRAME_MAXIMIZED | META_FRAME_SHADED):
04959 state = META_FRAME_STATE_MAXIMIZED_AND_SHADED;
04960 break;
04961 default:
04962 g_assert_not_reached ();
04963 state = META_FRAME_STATE_LAST;
04964 break;
04965 }
04966
04967 switch (flags & (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE))
04968 {
04969 case 0:
04970 resize = META_FRAME_RESIZE_NONE;
04971 break;
04972 case META_FRAME_ALLOWS_VERTICAL_RESIZE:
04973 resize = META_FRAME_RESIZE_VERTICAL;
04974 break;
04975 case META_FRAME_ALLOWS_HORIZONTAL_RESIZE:
04976 resize = META_FRAME_RESIZE_HORIZONTAL;
04977 break;
04978 case (META_FRAME_ALLOWS_VERTICAL_RESIZE | META_FRAME_ALLOWS_HORIZONTAL_RESIZE):
04979 resize = META_FRAME_RESIZE_BOTH;
04980 break;
04981 default:
04982 g_assert_not_reached ();
04983 resize = META_FRAME_RESIZE_LAST;
04984 break;
04985 }
04986
04987
04988 if (((flags & META_FRAME_HAS_FOCUS) && !(flags & META_FRAME_IS_FLASHING))
04989 || (!(flags & META_FRAME_HAS_FOCUS) && (flags & META_FRAME_IS_FLASHING)))
04990 focus = META_FRAME_FOCUS_YES;
04991 else
04992 focus = META_FRAME_FOCUS_NO;
04993
04994 style = get_style (style_set, state, resize, focus);
04995
04996 return style;
04997 }
04998
04999 MetaFrameStyle*
05000 meta_theme_get_frame_style (MetaTheme *theme,
05001 MetaFrameType type,
05002 MetaFrameFlags flags)
05003 {
05004 MetaFrameStyle *style;
05005
05006 g_return_val_if_fail (type < META_FRAME_TYPE_LAST, NULL);
05007
05008 style = theme_get_style (theme, type, flags);
05009
05010 return style;
05011 }
05012
05013 double
05014 meta_theme_get_title_scale (MetaTheme *theme,
05015 MetaFrameType type,
05016 MetaFrameFlags flags)
05017 {
05018 MetaFrameStyle *style;
05019
05020 g_return_val_if_fail (type < META_FRAME_TYPE_LAST, 1.0);
05021
05022 style = theme_get_style (theme, type, flags);
05023
05024
05025 if (style == NULL)
05026 return 1.0;
05027
05028 return style->layout->title_scale;
05029 }
05030
05031 void
05032 meta_theme_draw_frame (MetaTheme *theme,
05033 GtkWidget *widget,
05034 GdkDrawable *drawable,
05035 const GdkRectangle *clip,
05036 int x_offset,
05037 int y_offset,
05038 MetaFrameType type,
05039 MetaFrameFlags flags,
05040 int client_width,
05041 int client_height,
05042 PangoLayout *title_layout,
05043 int text_height,
05044 const MetaButtonLayout *button_layout,
05045 MetaButtonState button_states[META_BUTTON_TYPE_LAST],
05046 GdkPixbuf *mini_icon,
05047 GdkPixbuf *icon)
05048 {
05049 MetaFrameGeometry fgeom;
05050 MetaFrameStyle *style;
05051
05052 g_return_if_fail (type < META_FRAME_TYPE_LAST);
05053
05054 style = theme_get_style (theme, type, flags);
05055
05056
05057 if (style == NULL)
05058 return;
05059
05060 meta_frame_layout_calc_geometry (style->layout,
05061 text_height,
05062 flags,
05063 client_width, client_height,
05064 button_layout,
05065 &fgeom,
05066 theme);
05067
05068 meta_frame_style_draw (style,
05069 widget,
05070 drawable,
05071 x_offset, y_offset,
05072 clip,
05073 &fgeom,
05074 client_width, client_height,
05075 title_layout,
05076 text_height,
05077 button_states,
05078 mini_icon, icon);
05079 }
05080
05081 void
05082 meta_theme_get_frame_borders (MetaTheme *theme,
05083 MetaFrameType type,
05084 int text_height,
05085 MetaFrameFlags flags,
05086 int *top_height,
05087 int *bottom_height,
05088 int *left_width,
05089 int *right_width)
05090 {
05091 MetaFrameStyle *style;
05092
05093 g_return_if_fail (type < META_FRAME_TYPE_LAST);
05094
05095 if (top_height)
05096 *top_height = 0;
05097 if (bottom_height)
05098 *bottom_height = 0;
05099 if (left_width)
05100 *left_width = 0;
05101 if (right_width)
05102 *right_width = 0;
05103
05104 style = theme_get_style (theme, type, flags);
05105
05106
05107 if (style == NULL)
05108 return;
05109
05110 meta_frame_layout_get_borders (style->layout,
05111 text_height,
05112 flags,
05113 top_height, bottom_height,
05114 left_width, right_width);
05115 }
05116
05117 void
05118 meta_theme_calc_geometry (MetaTheme *theme,
05119 MetaFrameType type,
05120 int text_height,
05121 MetaFrameFlags flags,
05122 int client_width,
05123 int client_height,
05124 const MetaButtonLayout *button_layout,
05125 MetaFrameGeometry *fgeom)
05126 {
05127 MetaFrameStyle *style;
05128
05129 g_return_if_fail (type < META_FRAME_TYPE_LAST);
05130
05131 style = theme_get_style (theme, type, flags);
05132
05133
05134 if (style == NULL)
05135 return;
05136
05137 meta_frame_layout_calc_geometry (style->layout,
05138 text_height,
05139 flags,
05140 client_width, client_height,
05141 button_layout,
05142 fgeom,
05143 theme);
05144 }
05145
05146 MetaFrameLayout*
05147 meta_theme_lookup_layout (MetaTheme *theme,
05148 const char *name)
05149 {
05150 return g_hash_table_lookup (theme->layouts_by_name, name);
05151 }
05152
05153 void
05154 meta_theme_insert_layout (MetaTheme *theme,
05155 const char *name,
05156 MetaFrameLayout *layout)
05157 {
05158 meta_frame_layout_ref (layout);
05159 g_hash_table_replace (theme->layouts_by_name, g_strdup (name), layout);
05160 }
05161
05162 MetaDrawOpList*
05163 meta_theme_lookup_draw_op_list (MetaTheme *theme,
05164 const char *name)
05165 {
05166 return g_hash_table_lookup (theme->draw_op_lists_by_name, name);
05167 }
05168
05169 void
05170 meta_theme_insert_draw_op_list (MetaTheme *theme,
05171 const char *name,
05172 MetaDrawOpList *op_list)
05173 {
05174 meta_draw_op_list_ref (op_list);
05175 g_hash_table_replace (theme->draw_op_lists_by_name, g_strdup (name), op_list);
05176 }
05177
05178 MetaFrameStyle*
05179 meta_theme_lookup_style (MetaTheme *theme,
05180 const char *name)
05181 {
05182 return g_hash_table_lookup (theme->styles_by_name, name);
05183 }
05184
05185 void
05186 meta_theme_insert_style (MetaTheme *theme,
05187 const char *name,
05188 MetaFrameStyle *style)
05189 {
05190 meta_frame_style_ref (style);
05191 g_hash_table_replace (theme->styles_by_name, g_strdup (name), style);
05192 }
05193
05194 MetaFrameStyleSet*
05195 meta_theme_lookup_style_set (MetaTheme *theme,
05196 const char *name)
05197 {
05198 return g_hash_table_lookup (theme->style_sets_by_name, name);
05199 }
05200
05201 void
05202 meta_theme_insert_style_set (MetaTheme *theme,
05203 const char *name,
05204 MetaFrameStyleSet *style_set)
05205 {
05206 meta_frame_style_set_ref (style_set);
05207 g_hash_table_replace (theme->style_sets_by_name, g_strdup (name), style_set);
05208 }
05209
05210 static gboolean
05211 first_uppercase (const char *str)
05212 {
05213 return g_ascii_isupper (*str);
05214 }
05215
05216 gboolean
05217 meta_theme_define_int_constant (MetaTheme *theme,
05218 const char *name,
05219 int value,
05220 GError **error)
05221 {
05222 if (theme->integer_constants == NULL)
05223 theme->integer_constants = g_hash_table_new_full (g_str_hash,
05224 g_str_equal,
05225 g_free,
05226 NULL);
05227
05228 if (!first_uppercase (name))
05229 {
05230 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
05231 _("User-defined constants must begin with a capital letter; \"%s\" does not"),
05232 name);
05233 return FALSE;
05234 }
05235
05236 if (g_hash_table_lookup_extended (theme->integer_constants, name, NULL, NULL))
05237 {
05238 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
05239 _("Constant \"%s\" has already been defined"),
05240 name);
05241
05242 return FALSE;
05243 }
05244
05245 g_hash_table_insert (theme->integer_constants,
05246 g_strdup (name),
05247 GINT_TO_POINTER (value));
05248
05249 return TRUE;
05250 }
05251
05252 gboolean
05253 meta_theme_lookup_int_constant (MetaTheme *theme,
05254 const char *name,
05255 int *value)
05256 {
05257 gpointer old_value;
05258
05259 *value = 0;
05260
05261 if (theme->integer_constants == NULL)
05262 return FALSE;
05263
05264 if (g_hash_table_lookup_extended (theme->integer_constants,
05265 name, NULL, &old_value))
05266 {
05267 *value = GPOINTER_TO_INT (old_value);
05268 return TRUE;
05269 }
05270 else
05271 {
05272 return FALSE;
05273 }
05274 }
05275
05276 gboolean
05277 meta_theme_define_float_constant (MetaTheme *theme,
05278 const char *name,
05279 double value,
05280 GError **error)
05281 {
05282 double *d;
05283
05284 if (theme->float_constants == NULL)
05285 theme->float_constants = g_hash_table_new_full (g_str_hash,
05286 g_str_equal,
05287 g_free,
05288 g_free);
05289
05290 if (!first_uppercase (name))
05291 {
05292 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
05293 _("User-defined constants must begin with a capital letter; \"%s\" does not"),
05294 name);
05295 return FALSE;
05296 }
05297
05298 if (g_hash_table_lookup_extended (theme->float_constants, name, NULL, NULL))
05299 {
05300 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
05301 _("Constant \"%s\" has already been defined"),
05302 name);
05303
05304 return FALSE;
05305 }
05306
05307 d = g_new (double, 1);
05308 *d = value;
05309
05310 g_hash_table_insert (theme->float_constants,
05311 g_strdup (name), d);
05312
05313 return TRUE;
05314 }
05315
05316 gboolean
05317 meta_theme_lookup_float_constant (MetaTheme *theme,
05318 const char *name,
05319 double *value)
05320 {
05321 double *d;
05322
05323 *value = 0.0;
05324
05325 if (theme->float_constants == NULL)
05326 return FALSE;
05327
05328 d = g_hash_table_lookup (theme->float_constants, name);
05329
05330 if (d)
05331 {
05332 *value = *d;
05333 return TRUE;
05334 }
05335 else
05336 {
05337 return FALSE;
05338 }
05339 }
05340
05341 gboolean
05342 meta_theme_define_color_constant (MetaTheme *theme,
05343 const char *name,
05344 const char *value,
05345 GError **error)
05346 {
05347 if (theme->color_constants == NULL)
05348 theme->color_constants = g_hash_table_new_full (g_str_hash,
05349 g_str_equal,
05350 g_free,
05351 NULL);
05352
05353 if (!first_uppercase (name))
05354 {
05355 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
05356 _("User-defined constants must begin with a capital letter; \"%s\" does not"),
05357 name);
05358 return FALSE;
05359 }
05360
05361 if (g_hash_table_lookup_extended (theme->color_constants, name, NULL, NULL))
05362 {
05363 g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
05364 _("Constant \"%s\" has already been defined"),
05365 name);
05366
05367 return FALSE;
05368 }
05369
05370 g_hash_table_insert (theme->color_constants,
05371 g_strdup (name),
05372 g_strdup (value));
05373
05374 return TRUE;
05375 }
05376
05377 gboolean
05378 meta_theme_lookup_color_constant (MetaTheme *theme,
05379 const char *name,
05380 char **value)
05381 {
05382 char *result;
05383
05384 *value = NULL;
05385
05386 if (theme->color_constants == NULL)
05387 return FALSE;
05388
05389 result = g_hash_table_lookup (theme->color_constants, name);
05390
05391 if (result)
05392 {
05393 *value = result;
05394 return TRUE;
05395 }
05396 else
05397 {
05398 return FALSE;
05399 }
05400 }
05401
05402
05403 PangoFontDescription*
05404 meta_gtk_widget_get_font_desc (GtkWidget *widget,
05405 double scale,
05406 const PangoFontDescription *override)
05407 {
05408 PangoFontDescription *font_desc;
05409
05410 g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
05411
05412 font_desc = pango_font_description_copy (widget->style->font_desc);
05413
05414 if (override)
05415 pango_font_description_merge (font_desc, override, TRUE);
05416
05417 pango_font_description_set_size (font_desc,
05418 MAX (pango_font_description_get_size (font_desc) * scale, 1));
05419
05420 return font_desc;
05421 }
05422
05423 int
05424 meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc,
05425 PangoContext *context)
05426 {
05427 PangoFontMetrics *metrics;
05428 PangoLanguage *lang;
05429 int retval;
05430
05431 lang = pango_context_get_language (context);
05432 metrics = pango_context_get_metrics (context, font_desc, lang);
05433
05434 retval = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
05435 pango_font_metrics_get_descent (metrics));
05436
05437 pango_font_metrics_unref (metrics);
05438
05439 return retval;
05440 }
05441
05442 MetaGtkColorComponent
05443 meta_color_component_from_string (const char *str)
05444 {
05445 if (strcmp ("fg", str) == 0)
05446 return META_GTK_COLOR_FG;
05447 else if (strcmp ("bg", str) == 0)
05448 return META_GTK_COLOR_BG;
05449 else if (strcmp ("light", str) == 0)
05450 return META_GTK_COLOR_LIGHT;
05451 else if (strcmp ("dark", str) == 0)
05452 return META_GTK_COLOR_DARK;
05453 else if (strcmp ("mid", str) == 0)
05454 return META_GTK_COLOR_MID;
05455 else if (strcmp ("text", str) == 0)
05456 return META_GTK_COLOR_TEXT;
05457 else if (strcmp ("base", str) == 0)
05458 return META_GTK_COLOR_BASE;
05459 else if (strcmp ("text_aa", str) == 0)
05460 return META_GTK_COLOR_TEXT_AA;
05461 else
05462 return META_GTK_COLOR_LAST;
05463 }
05464
05465 const char*
05466 meta_color_component_to_string (MetaGtkColorComponent component)
05467 {
05468 switch (component)
05469 {
05470 case META_GTK_COLOR_FG:
05471 return "fg";
05472 case META_GTK_COLOR_BG:
05473 return "bg";
05474 case META_GTK_COLOR_LIGHT:
05475 return "light";
05476 case META_GTK_COLOR_DARK:
05477 return "dark";
05478 case META_GTK_COLOR_MID:
05479 return "mid";
05480 case META_GTK_COLOR_TEXT:
05481 return "text";
05482 case META_GTK_COLOR_BASE:
05483 return "base";
05484 case META_GTK_COLOR_TEXT_AA:
05485 return "text_aa";
05486 case META_GTK_COLOR_LAST:
05487 break;
05488 }
05489
05490 return "<unknown>";
05491 }
05492
05493 MetaButtonState
05494 meta_button_state_from_string (const char *str)
05495 {
05496 if (strcmp ("normal", str) == 0)
05497 return META_BUTTON_STATE_NORMAL;
05498 else if (strcmp ("pressed", str) == 0)
05499 return META_BUTTON_STATE_PRESSED;
05500 else if (strcmp ("prelight", str) == 0)
05501 return META_BUTTON_STATE_PRELIGHT;
05502 else
05503 return META_BUTTON_STATE_LAST;
05504 }
05505
05506 const char*
05507 meta_button_state_to_string (MetaButtonState state)
05508 {
05509 switch (state)
05510 {
05511 case META_BUTTON_STATE_NORMAL:
05512 return "normal";
05513 case META_BUTTON_STATE_PRESSED:
05514 return "pressed";
05515 case META_BUTTON_STATE_PRELIGHT:
05516 return "prelight";
05517 case META_BUTTON_STATE_LAST:
05518 break;
05519 }
05520
05521 return "<unknown>";
05522 }
05523
05524 MetaButtonType
05525 meta_button_type_from_string (const char *str, MetaTheme *theme)
05526 {
05527 if (META_THEME_ALLOWS(theme, META_THEME_SHADE_STICK_ABOVE_BUTTONS))
05528 {
05529 if (strcmp ("shade", str) == 0)
05530 return META_BUTTON_TYPE_SHADE;
05531 else if (strcmp ("above", str) == 0)
05532 return META_BUTTON_TYPE_ABOVE;
05533 else if (strcmp ("stick", str) == 0)
05534 return META_BUTTON_TYPE_STICK;
05535 else if (strcmp ("unshade", str) == 0)
05536 return META_BUTTON_TYPE_UNSHADE;
05537 else if (strcmp ("unabove", str) == 0)
05538 return META_BUTTON_TYPE_UNABOVE;
05539 else if (strcmp ("unstick", str) == 0)
05540 return META_BUTTON_TYPE_UNSTICK;
05541 }
05542
05543 if (strcmp ("close", str) == 0)
05544 return META_BUTTON_TYPE_CLOSE;
05545 else if (strcmp ("maximize", str) == 0)
05546 return META_BUTTON_TYPE_MAXIMIZE;
05547 else if (strcmp ("minimize", str) == 0)
05548 return META_BUTTON_TYPE_MINIMIZE;
05549 else if (strcmp ("menu", str) == 0)
05550 return META_BUTTON_TYPE_MENU;
05551 else if (strcmp ("left_left_background", str) == 0)
05552 return META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND;
05553 else if (strcmp ("left_middle_background", str) == 0)
05554 return META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND;
05555 else if (strcmp ("left_right_background", str) == 0)
05556 return META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND;
05557 else if (strcmp ("right_left_background", str) == 0)
05558 return META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND;
05559 else if (strcmp ("right_middle_background", str) == 0)
05560 return META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND;
05561 else if (strcmp ("right_right_background", str) == 0)
05562 return META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND;
05563 else
05564 return META_BUTTON_TYPE_LAST;
05565 }
05566
05567 const char*
05568 meta_button_type_to_string (MetaButtonType type)
05569 {
05570 switch (type)
05571 {
05572 case META_BUTTON_TYPE_CLOSE:
05573 return "close";
05574 case META_BUTTON_TYPE_MAXIMIZE:
05575 return "maximize";
05576 case META_BUTTON_TYPE_MINIMIZE:
05577 return "minimize";
05578 case META_BUTTON_TYPE_SHADE:
05579 return "shade";
05580 case META_BUTTON_TYPE_ABOVE:
05581 return "above";
05582 case META_BUTTON_TYPE_STICK:
05583 return "stick";
05584 case META_BUTTON_TYPE_UNSHADE:
05585 return "unshade";
05586 case META_BUTTON_TYPE_UNABOVE:
05587 return "unabove";
05588 case META_BUTTON_TYPE_UNSTICK:
05589 return "unstick";
05590 case META_BUTTON_TYPE_MENU:
05591 return "menu";
05592 case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
05593 return "left_left_background";
05594 case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
05595 return "left_middle_background";
05596 case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
05597 return "left_right_background";
05598 case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
05599 return "right_left_background";
05600 case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
05601 return "right_middle_background";
05602 case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
05603 return "right_right_background";
05604 case META_BUTTON_TYPE_LAST:
05605 break;
05606 }
05607
05608 return "<unknown>";
05609 }
05610
05611 MetaFramePiece
05612 meta_frame_piece_from_string (const char *str)
05613 {
05614 if (strcmp ("entire_background", str) == 0)
05615 return META_FRAME_PIECE_ENTIRE_BACKGROUND;
05616 else if (strcmp ("titlebar", str) == 0)
05617 return META_FRAME_PIECE_TITLEBAR;
05618 else if (strcmp ("titlebar_middle", str) == 0)
05619 return META_FRAME_PIECE_TITLEBAR_MIDDLE;
05620 else if (strcmp ("left_titlebar_edge", str) == 0)
05621 return META_FRAME_PIECE_LEFT_TITLEBAR_EDGE;
05622 else if (strcmp ("right_titlebar_edge", str) == 0)
05623 return META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE;
05624 else if (strcmp ("top_titlebar_edge", str) == 0)
05625 return META_FRAME_PIECE_TOP_TITLEBAR_EDGE;
05626 else if (strcmp ("bottom_titlebar_edge", str) == 0)
05627 return META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE;
05628 else if (strcmp ("title", str) == 0)
05629 return META_FRAME_PIECE_TITLE;
05630 else if (strcmp ("left_edge", str) == 0)
05631 return META_FRAME_PIECE_LEFT_EDGE;
05632 else if (strcmp ("right_edge", str) == 0)
05633 return META_FRAME_PIECE_RIGHT_EDGE;
05634 else if (strcmp ("bottom_edge", str) == 0)
05635 return META_FRAME_PIECE_BOTTOM_EDGE;
05636 else if (strcmp ("overlay", str) == 0)
05637 return META_FRAME_PIECE_OVERLAY;
05638 else
05639 return META_FRAME_PIECE_LAST;
05640 }
05641
05642 const char*
05643 meta_frame_piece_to_string (MetaFramePiece piece)
05644 {
05645 switch (piece)
05646 {
05647 case META_FRAME_PIECE_ENTIRE_BACKGROUND:
05648 return "entire_background";
05649 case META_FRAME_PIECE_TITLEBAR:
05650 return "titlebar";
05651 case META_FRAME_PIECE_TITLEBAR_MIDDLE:
05652 return "titlebar_middle";
05653 case META_FRAME_PIECE_LEFT_TITLEBAR_EDGE:
05654 return "left_titlebar_edge";
05655 case META_FRAME_PIECE_RIGHT_TITLEBAR_EDGE:
05656 return "right_titlebar_edge";
05657 case META_FRAME_PIECE_TOP_TITLEBAR_EDGE:
05658 return "top_titlebar_edge";
05659 case META_FRAME_PIECE_BOTTOM_TITLEBAR_EDGE:
05660 return "bottom_titlebar_edge";
05661 case META_FRAME_PIECE_TITLE:
05662 return "title";
05663 case META_FRAME_PIECE_LEFT_EDGE:
05664 return "left_edge";
05665 case META_FRAME_PIECE_RIGHT_EDGE:
05666 return "right_edge";
05667 case META_FRAME_PIECE_BOTTOM_EDGE:
05668 return "bottom_edge";
05669 case META_FRAME_PIECE_OVERLAY:
05670 return "overlay";
05671 case META_FRAME_PIECE_LAST:
05672 break;
05673 }
05674
05675 return "<unknown>";
05676 }
05677
05678 MetaFrameState
05679 meta_frame_state_from_string (const char *str)
05680 {
05681 if (strcmp ("normal", str) == 0)
05682 return META_FRAME_STATE_NORMAL;
05683 else if (strcmp ("maximized", str) == 0)
05684 return META_FRAME_STATE_MAXIMIZED;
05685 else if (strcmp ("shaded", str) == 0)
05686 return META_FRAME_STATE_SHADED;
05687 else if (strcmp ("maximized_and_shaded", str) == 0)
05688 return META_FRAME_STATE_MAXIMIZED_AND_SHADED;
05689 else
05690 return META_FRAME_STATE_LAST;
05691 }
05692
05693 const char*
05694 meta_frame_state_to_string (MetaFrameState state)
05695 {
05696 switch (state)
05697 {
05698 case META_FRAME_STATE_NORMAL:
05699 return "normal";
05700 case META_FRAME_STATE_MAXIMIZED:
05701 return "maximized";
05702 case META_FRAME_STATE_SHADED:
05703 return "shaded";
05704 case META_FRAME_STATE_MAXIMIZED_AND_SHADED:
05705 return "maximized_and_shaded";
05706 case META_FRAME_STATE_LAST:
05707 break;
05708 }
05709
05710 return "<unknown>";
05711 }
05712
05713 MetaFrameResize
05714 meta_frame_resize_from_string (const char *str)
05715 {
05716 if (strcmp ("none", str) == 0)
05717 return META_FRAME_RESIZE_NONE;
05718 else if (strcmp ("vertical", str) == 0)
05719 return META_FRAME_RESIZE_VERTICAL;
05720 else if (strcmp ("horizontal", str) == 0)
05721 return META_FRAME_RESIZE_HORIZONTAL;
05722 else if (strcmp ("both", str) == 0)
05723 return META_FRAME_RESIZE_BOTH;
05724 else
05725 return META_FRAME_RESIZE_LAST;
05726 }
05727
05728 const char*
05729 meta_frame_resize_to_string (MetaFrameResize resize)
05730 {
05731 switch (resize)
05732 {
05733 case META_FRAME_RESIZE_NONE:
05734 return "none";
05735 case META_FRAME_RESIZE_VERTICAL:
05736 return "vertical";
05737 case META_FRAME_RESIZE_HORIZONTAL:
05738 return "horizontal";
05739 case META_FRAME_RESIZE_BOTH:
05740 return "both";
05741 case META_FRAME_RESIZE_LAST:
05742 break;
05743 }
05744
05745 return "<unknown>";
05746 }
05747
05748 MetaFrameFocus
05749 meta_frame_focus_from_string (const char *str)
05750 {
05751 if (strcmp ("no", str) == 0)
05752 return META_FRAME_FOCUS_NO;
05753 else if (strcmp ("yes", str) == 0)
05754 return META_FRAME_FOCUS_YES;
05755 else
05756 return META_FRAME_FOCUS_LAST;
05757 }
05758
05759 const char*
05760 meta_frame_focus_to_string (MetaFrameFocus focus)
05761 {
05762 switch (focus)
05763 {
05764 case META_FRAME_FOCUS_NO:
05765 return "no";
05766 case META_FRAME_FOCUS_YES:
05767 return "yes";
05768 case META_FRAME_FOCUS_LAST:
05769 break;
05770 }
05771
05772 return "<unknown>";
05773 }
05774
05775 MetaFrameType
05776 meta_frame_type_from_string (const char *str)
05777 {
05778 if (strcmp ("normal", str) == 0)
05779 return META_FRAME_TYPE_NORMAL;
05780 else if (strcmp ("dialog", str) == 0)
05781 return META_FRAME_TYPE_DIALOG;
05782 else if (strcmp ("modal_dialog", str) == 0)
05783 return META_FRAME_TYPE_MODAL_DIALOG;
05784 else if (strcmp ("utility", str) == 0)
05785 return META_FRAME_TYPE_UTILITY;
05786 else if (strcmp ("menu", str) == 0)
05787 return META_FRAME_TYPE_MENU;
05788 else if (strcmp ("border", str) == 0)
05789 return META_FRAME_TYPE_BORDER;
05790 #if 0
05791 else if (strcmp ("toolbar", str) == 0)
05792 return META_FRAME_TYPE_TOOLBAR;
05793 #endif
05794 else
05795 return META_FRAME_TYPE_LAST;
05796 }
05797
05798 const char*
05799 meta_frame_type_to_string (MetaFrameType type)
05800 {
05801 switch (type)
05802 {
05803 case META_FRAME_TYPE_NORMAL:
05804 return "normal";
05805 case META_FRAME_TYPE_DIALOG:
05806 return "dialog";
05807 case META_FRAME_TYPE_MODAL_DIALOG:
05808 return "modal_dialog";
05809 case META_FRAME_TYPE_UTILITY:
05810 return "utility";
05811 case META_FRAME_TYPE_MENU:
05812 return "menu";
05813 case META_FRAME_TYPE_BORDER:
05814 return "border";
05815 #if 0
05816 case META_FRAME_TYPE_TOOLBAR:
05817 return "toolbar";
05818 #endif
05819 case META_FRAME_TYPE_LAST:
05820 break;
05821 }
05822
05823 return "<unknown>";
05824 }
05825
05826 MetaGradientType
05827 meta_gradient_type_from_string (const char *str)
05828 {
05829 if (strcmp ("vertical", str) == 0)
05830 return META_GRADIENT_VERTICAL;
05831 else if (strcmp ("horizontal", str) == 0)
05832 return META_GRADIENT_HORIZONTAL;
05833 else if (strcmp ("diagonal", str) == 0)
05834 return META_GRADIENT_DIAGONAL;
05835 else
05836 return META_GRADIENT_LAST;
05837 }
05838
05839 const char*
05840 meta_gradient_type_to_string (MetaGradientType type)
05841 {
05842 switch (type)
05843 {
05844 case META_GRADIENT_VERTICAL:
05845 return "vertical";
05846 case META_GRADIENT_HORIZONTAL:
05847 return "horizontal";
05848 case META_GRADIENT_DIAGONAL:
05849 return "diagonal";
05850 case META_GRADIENT_LAST:
05851 break;
05852 }
05853
05854 return "<unknown>";
05855 }
05856
05857 GtkStateType
05858 meta_gtk_state_from_string (const char *str)
05859 {
05860 if (strcmp ("normal", str) == 0 || strcmp ("NORMAL", str) == 0)
05861 return GTK_STATE_NORMAL;
05862 else if (strcmp ("prelight", str) == 0 || strcmp ("PRELIGHT", str) == 0)
05863 return GTK_STATE_PRELIGHT;
05864 else if (strcmp ("active", str) == 0 || strcmp ("ACTIVE", str) == 0)
05865 return GTK_STATE_ACTIVE;
05866 else if (strcmp ("selected", str) == 0 || strcmp ("SELECTED", str) == 0)
05867 return GTK_STATE_SELECTED;
05868 else if (strcmp ("insensitive", str) == 0 || strcmp ("INSENSITIVE", str) == 0)
05869 return GTK_STATE_INSENSITIVE;
05870 else
05871 return -1;
05872 }
05873
05874 const char*
05875 meta_gtk_state_to_string (GtkStateType state)
05876 {
05877 switch (state)
05878 {
05879 case GTK_STATE_NORMAL:
05880 return "NORMAL";
05881 case GTK_STATE_PRELIGHT:
05882 return "PRELIGHT";
05883 case GTK_STATE_ACTIVE:
05884 return "ACTIVE";
05885 case GTK_STATE_SELECTED:
05886 return "SELECTED";
05887 case GTK_STATE_INSENSITIVE:
05888 return "INSENSITIVE";
05889 }
05890
05891 return "<unknown>";
05892 }
05893
05894 GtkShadowType
05895 meta_gtk_shadow_from_string (const char *str)
05896 {
05897 if (strcmp ("none", str) == 0)
05898 return GTK_SHADOW_NONE;
05899 else if (strcmp ("in", str) == 0)
05900 return GTK_SHADOW_IN;
05901 else if (strcmp ("out", str) == 0)
05902 return GTK_SHADOW_OUT;
05903 else if (strcmp ("etched_in", str) == 0)
05904 return GTK_SHADOW_ETCHED_IN;
05905 else if (strcmp ("etched_out", str) == 0)
05906 return GTK_SHADOW_ETCHED_OUT;
05907 else
05908 return -1;
05909 }
05910
05911 const char*
05912 meta_gtk_shadow_to_string (GtkShadowType shadow)
05913 {
05914 switch (shadow)
05915 {
05916 case GTK_SHADOW_NONE:
05917 return "none";
05918 case GTK_SHADOW_IN:
05919 return "in";
05920 case GTK_SHADOW_OUT:
05921 return "out";
05922 case GTK_SHADOW_ETCHED_IN:
05923 return "etched_in";
05924 case GTK_SHADOW_ETCHED_OUT:
05925 return "etched_out";
05926 }
05927
05928 return "<unknown>";
05929 }
05930
05931 GtkArrowType
05932 meta_gtk_arrow_from_string (const char *str)
05933 {
05934 if (strcmp ("up", str) == 0)
05935 return GTK_ARROW_UP;
05936 else if (strcmp ("down", str) == 0)
05937 return GTK_ARROW_DOWN;
05938 else if (strcmp ("left", str) == 0)
05939 return GTK_ARROW_LEFT;
05940 else if (strcmp ("right", str) == 0)
05941 return GTK_ARROW_RIGHT;
05942 else if (strcmp ("none", str) == 0)
05943 return GTK_ARROW_NONE;
05944 else
05945 return -1;
05946 }
05947
05948 const char*
05949 meta_gtk_arrow_to_string (GtkArrowType arrow)
05950 {
05951 switch (arrow)
05952 {
05953 case GTK_ARROW_UP:
05954 return "up";
05955 case GTK_ARROW_DOWN:
05956 return "down";
05957 case GTK_ARROW_LEFT:
05958 return "left";
05959 case GTK_ARROW_RIGHT:
05960 return "right";
05961 case GTK_ARROW_NONE:
05962 return "none";
05963 }
05964
05965 return "<unknown>";
05966 }
05967
05968 MetaImageFillType
05969 meta_image_fill_type_from_string (const char *str)
05970 {
05971 if (strcmp ("tile", str) == 0)
05972 return META_IMAGE_FILL_TILE;
05973 else if (strcmp ("scale", str) == 0)
05974 return META_IMAGE_FILL_SCALE;
05975 else
05976 return -1;
05977 }
05978
05979 const char*
05980 meta_image_fill_type_to_string (MetaImageFillType fill_type)
05981 {
05982 switch (fill_type)
05983 {
05984 case META_IMAGE_FILL_TILE:
05985 return "tile";
05986 case META_IMAGE_FILL_SCALE:
05987 return "scale";
05988 }
05989
05990 return "<unknown>";
05991 }
05992
05993
05994 static void
05995 gtk_style_shade (GdkColor *a,
05996 GdkColor *b,
05997 gdouble k)
05998 {
05999 gdouble red;
06000 gdouble green;
06001 gdouble blue;
06002
06003 red = (gdouble) a->red / 65535.0;
06004 green = (gdouble) a->green / 65535.0;
06005 blue = (gdouble) a->blue / 65535.0;
06006
06007 rgb_to_hls (&red, &green, &blue);
06008
06009 green *= k;
06010 if (green > 1.0)
06011 green = 1.0;
06012 else if (green < 0.0)
06013 green = 0.0;
06014
06015 blue *= k;
06016 if (blue > 1.0)
06017 blue = 1.0;
06018 else if (blue < 0.0)
06019 blue = 0.0;
06020
06021 hls_to_rgb (&red, &green, &blue);
06022
06023 b->red = red * 65535.0;
06024 b->green = green * 65535.0;
06025 b->blue = blue * 65535.0;
06026 }
06027
06028 static void
06029 rgb_to_hls (gdouble *r,
06030 gdouble *g,
06031 gdouble *b)
06032 {
06033 gdouble min;
06034 gdouble max;
06035 gdouble red;
06036 gdouble green;
06037 gdouble blue;
06038 gdouble h, l, s;
06039 gdouble delta;
06040
06041 red = *r;
06042 green = *g;
06043 blue = *b;
06044
06045 if (red > green)
06046 {
06047 if (red > blue)
06048 max = red;
06049 else
06050 max = blue;
06051
06052 if (green < blue)
06053 min = green;
06054 else
06055 min = blue;
06056 }
06057 else
06058 {
06059 if (green > blue)
06060 max = green;
06061 else
06062 max = blue;
06063
06064 if (red < blue)
06065 min = red;
06066 else
06067 min = blue;
06068 }
06069
06070 l = (max + min) / 2;
06071 s = 0;
06072 h = 0;
06073
06074 if (max != min)
06075 {
06076 if (l <= 0.5)
06077 s = (max - min) / (max + min);
06078 else
06079 s = (max - min) / (2 - max - min);
06080
06081 delta = max -min;
06082 if (red == max)
06083 h = (green - blue) / delta;
06084 else if (green == max)
06085 h = 2 + (blue - red) / delta;
06086 else if (blue == max)
06087 h = 4 + (red - green) / delta;
06088
06089 h *= 60;
06090 if (h < 0.0)
06091 h += 360;
06092 }
06093
06094 *r = h;
06095 *g = l;
06096 *b = s;
06097 }
06098
06099 static void
06100 hls_to_rgb (gdouble *h,
06101 gdouble *l,
06102 gdouble *s)
06103 {
06104 gdouble hue;
06105 gdouble lightness;
06106 gdouble saturation;
06107 gdouble m1, m2;
06108 gdouble r, g, b;
06109
06110 lightness = *l;
06111 saturation = *s;
06112
06113 if (lightness <= 0.5)
06114 m2 = lightness * (1 + saturation);
06115 else
06116 m2 = lightness + saturation - lightness * saturation;
06117 m1 = 2 * lightness - m2;
06118
06119 if (saturation == 0)
06120 {
06121 *h = lightness;
06122 *l = lightness;
06123 *s = lightness;
06124 }
06125 else
06126 {
06127 hue = *h + 120;
06128 while (hue > 360)
06129 hue -= 360;
06130 while (hue < 0)
06131 hue += 360;
06132
06133 if (hue < 60)
06134 r = m1 + (m2 - m1) * hue / 60;
06135 else if (hue < 180)
06136 r = m2;
06137 else if (hue < 240)
06138 r = m1 + (m2 - m1) * (240 - hue) / 60;
06139 else
06140 r = m1;
06141
06142 hue = *h;
06143 while (hue > 360)
06144 hue -= 360;
06145 while (hue < 0)
06146 hue += 360;
06147
06148 if (hue < 60)
06149 g = m1 + (m2 - m1) * hue / 60;
06150 else if (hue < 180)
06151 g = m2;
06152 else if (hue < 240)
06153 g = m1 + (m2 - m1) * (240 - hue) / 60;
06154 else
06155 g = m1;
06156
06157 hue = *h - 120;
06158 while (hue > 360)
06159 hue -= 360;
06160 while (hue < 0)
06161 hue += 360;
06162
06163 if (hue < 60)
06164 b = m1 + (m2 - m1) * hue / 60;
06165 else if (hue < 180)
06166 b = m2;
06167 else if (hue < 240)
06168 b = m1 + (m2 - m1) * (240 - hue) / 60;
06169 else
06170 b = m1;
06171
06172 *h = r;
06173 *l = g;
06174 *s = b;
06175 }
06176 }
06177
06178 #if 0
06179
06180
06181
06182
06183 static void
06184 draw_bg_solid_composite (const MetaTextureSpec *bg,
06185 const MetaTextureSpec *fg,
06186 double alpha,
06187 GtkWidget *widget,
06188 GdkDrawable *drawable,
06189 const GdkRectangle *clip,
06190 MetaTextureDrawMode mode,
06191 double xalign,
06192 double yalign,
06193 int x,
06194 int y,
06195 int width,
06196 int height)
06197 {
06198 GdkColor bg_color;
06199
06200 g_assert (bg->type == META_TEXTURE_SOLID);
06201 g_assert (fg->type != META_TEXTURE_COMPOSITE);
06202 g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
06203
06204 meta_color_spec_render (bg->data.solid.color_spec,
06205 widget,
06206 &bg_color);
06207
06208 switch (fg->type)
06209 {
06210 case META_TEXTURE_SOLID:
06211 {
06212 GdkColor fg_color;
06213
06214 meta_color_spec_render (fg->data.solid.color_spec,
06215 widget,
06216 &fg_color);
06217
06218 color_composite (&bg_color, &fg_color,
06219 alpha, &fg_color);
06220
06221 draw_color_rectangle (widget, drawable, &fg_color, clip,
06222 x, y, width, height);
06223 }
06224 break;
06225
06226 case META_TEXTURE_GRADIENT:
06227
06228
06229
06230
06231 case META_TEXTURE_IMAGE:
06232 {
06233 GdkPixbuf *pixbuf;
06234 GdkPixbuf *composited;
06235
06236 pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
06237 width, height);
06238
06239 if (pixbuf == NULL)
06240 return;
06241
06242 composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
06243 gdk_pixbuf_get_has_alpha (pixbuf), 8,
06244 gdk_pixbuf_get_width (pixbuf),
06245 gdk_pixbuf_get_height (pixbuf));
06246
06247 if (composited == NULL)
06248 {
06249 g_object_unref (G_OBJECT (pixbuf));
06250 return;
06251 }
06252
06253 gdk_pixbuf_composite_color (pixbuf,
06254 composited,
06255 0, 0,
06256 gdk_pixbuf_get_width (pixbuf),
06257 gdk_pixbuf_get_height (pixbuf),
06258 0.0, 0.0,
06259 1.0, 1.0,
06260 GDK_INTERP_BILINEAR,
06261 255 * alpha,
06262 0, 0,
06263 0,
06264 GDK_COLOR_RGB (bg_color),
06265 GDK_COLOR_RGB (bg_color));
06266
06267
06268
06269
06270 draw_color_rectangle (widget, drawable, &bg_color, clip,
06271 x, y, width, height);
06272
06273 render_pixbuf_aligned (drawable, clip, composited,
06274 xalign, yalign,
06275 x, y, width, height);
06276
06277 g_object_unref (G_OBJECT (pixbuf));
06278 g_object_unref (G_OBJECT (composited));
06279 }
06280 break;
06281
06282 case META_TEXTURE_BLANK:
06283 case META_TEXTURE_COMPOSITE:
06284 case META_TEXTURE_SHAPE_LIST:
06285 g_assert_not_reached ();
06286 break;
06287 }
06288 }
06289
06290 static void
06291 draw_bg_gradient_composite (const MetaTextureSpec *bg,
06292 const MetaTextureSpec *fg,
06293 double alpha,
06294 GtkWidget *widget,
06295 GdkDrawable *drawable,
06296 const GdkRectangle *clip,
06297 MetaTextureDrawMode mode,
06298 double xalign,
06299 double yalign,
06300 int x,
06301 int y,
06302 int width,
06303 int height)
06304 {
06305 g_assert (bg->type == META_TEXTURE_GRADIENT);
06306 g_assert (fg->type != META_TEXTURE_COMPOSITE);
06307 g_assert (fg->type != META_TEXTURE_SHAPE_LIST);
06308
06309 switch (fg->type)
06310 {
06311 case META_TEXTURE_SOLID:
06312 case META_TEXTURE_GRADIENT:
06313 case META_TEXTURE_IMAGE:
06314 {
06315 GdkPixbuf *bg_pixbuf;
06316 GdkPixbuf *fg_pixbuf;
06317 GdkPixbuf *composited;
06318 int fg_width, fg_height;
06319
06320 bg_pixbuf = meta_texture_spec_render (bg, widget, mode, 255,
06321 width, height);
06322
06323 if (bg_pixbuf == NULL)
06324 return;
06325
06326 fg_pixbuf = meta_texture_spec_render (fg, widget, mode, 255,
06327 width, height);
06328
06329 if (fg_pixbuf == NULL)
06330 {
06331 g_object_unref (G_OBJECT (bg_pixbuf));
06332 return;
06333 }
06334
06335
06336 g_assert (gdk_pixbuf_get_width (bg_pixbuf) == width);
06337 g_assert (gdk_pixbuf_get_height (bg_pixbuf) == height);
06338
06339 composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
06340 gdk_pixbuf_get_has_alpha (bg_pixbuf), 8,
06341 gdk_pixbuf_get_width (bg_pixbuf),
06342 gdk_pixbuf_get_height (bg_pixbuf));
06343
06344 if (composited == NULL)
06345 {
06346 g_object_unref (G_OBJECT (bg_pixbuf));
06347 g_object_unref (G_OBJECT (fg_pixbuf));
06348 return;
06349 }
06350
06351 fg_width = gdk_pixbuf_get_width (fg_pixbuf);
06352 fg_height = gdk_pixbuf_get_height (fg_pixbuf);
06353
06354
06355
06356
06357
06358
06359 gdk_pixbuf_composite (fg_pixbuf,
06360 composited,
06361 x + (width - fg_width) * xalign,
06362 y + (height - fg_height) * yalign,
06363 gdk_pixbuf_get_width (fg_pixbuf),
06364 gdk_pixbuf_get_height (fg_pixbuf),
06365 0.0, 0.0,
06366 1.0, 1.0,
06367 GDK_INTERP_BILINEAR,
06368 255 * alpha);
06369
06370 render_pixbuf (drawable, clip, composited, x, y);
06371
06372 g_object_unref (G_OBJECT (bg_pixbuf));
06373 g_object_unref (G_OBJECT (fg_pixbuf));
06374 g_object_unref (G_OBJECT (composited));
06375 }
06376 break;
06377
06378 case META_TEXTURE_BLANK:
06379 case META_TEXTURE_SHAPE_LIST:
06380 case META_TEXTURE_COMPOSITE:
06381 g_assert_not_reached ();
06382 break;
06383 }
06384 }
06385 #endif
06386
06387 guint
06388 meta_theme_earliest_version_with_button (MetaButtonType type)
06389 {
06390 switch (type)
06391 {
06392 case META_BUTTON_TYPE_CLOSE:
06393 case META_BUTTON_TYPE_MAXIMIZE:
06394 case META_BUTTON_TYPE_MINIMIZE:
06395 case META_BUTTON_TYPE_MENU:
06396 case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND:
06397 case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND:
06398 case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND:
06399 case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND:
06400 case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND:
06401 case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND:
06402 return 1;
06403
06404 case META_BUTTON_TYPE_SHADE:
06405 case META_BUTTON_TYPE_ABOVE:
06406 case META_BUTTON_TYPE_STICK:
06407 case META_BUTTON_TYPE_UNSHADE:
06408 case META_BUTTON_TYPE_UNABOVE:
06409 case META_BUTTON_TYPE_UNSTICK:
06410 return 2;
06411
06412 default:
06413 meta_warning("Unknown button %d\n", type);
06414 return 1;
06415 }
06416 }