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
04