gtkcssimagebuiltin.c 26.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright © 2012 Red Hat Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: Benjamin Otte <otte@gnome.org>
 */

#include "config.h"

#include "gtkcssimagebuiltinprivate.h"

24 25 26 27
#include "gtkcssenumvalueprivate.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkcssrgbavalueprivate.h"
#include "gtkcssstyleprivate.h"
28 29 30 31 32 33 34
#include "gtkhslaprivate.h"
#include "gtkrenderprivate.h"

#include <math.h>

#include "fallback-c89.c"

35 36 37 38 39
G_DEFINE_TYPE (GtkCssImageBuiltin, gtk_css_image_builtin, GTK_TYPE_CSS_IMAGE)

static GtkCssImage *the_one_true_image;

static void
40 41 42 43 44 45
gtk_css_image_builtin_draw_check (GtkCssImage *image,
                                  cairo_t     *cr,
                                  double       width,
                                  double       height,
                                  gboolean     checked,
                                  gboolean     inconsistent)
46
{
47
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
48
  gint x, y, exterior_size, interior_size, pad;
49 50 51 52 53 54

  exterior_size = MIN (width, height);

  if (exterior_size % 2 == 0) /* Ensure odd */
    exterior_size -= 1;

55
  pad = 1 + MAX (1, (exterior_size - 2) / 9);
56 57 58
  interior_size = MAX (1, exterior_size - 2 * pad);

  if (interior_size < 7)
59
    pad = MAX (0, (exterior_size - interior_size) / 2);
60 61 62 63

  x = - (1 + exterior_size - (gint) width) / 2;
  y = - (1 + exterior_size - (gint) height) / 2;

64
  gdk_cairo_set_source_rgba (cr, &builtin->fg_color);
65

66
  if (inconsistent)
67 68 69 70 71 72 73 74 75 76 77 78
    {
      int line_thickness = MAX (1, (3 + interior_size * 2) / 7);

      cairo_rectangle (cr,
		       x + pad,
		       y + pad + (1 + interior_size - line_thickness) / 2,
		       interior_size,
		       line_thickness);
      cairo_fill (cr);
    }
  else
    {
79
      if (checked)
80
        {
81
          cairo_save (cr);
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
          cairo_translate (cr,
                           x + pad, y + pad);

          cairo_scale (cr, interior_size / 7., interior_size / 7.);

          cairo_rectangle (cr, 0, 0, 7, 7);
          cairo_clip (cr);

          cairo_move_to  (cr, 7.0, 0.0);
          cairo_line_to  (cr, 7.5, 1.0);
          cairo_curve_to (cr, 5.3, 2.0,
                          4.3, 4.0,
                          3.5, 7.0);
          cairo_curve_to (cr, 3.0, 5.7,
                          1.3, 4.7,
                          0.0, 4.7);
          cairo_line_to  (cr, 0.2, 3.5);
          cairo_curve_to (cr, 1.1, 3.5,
                          2.3, 4.3,
                          3.0, 5.0);
          cairo_curve_to (cr, 1.0, 3.9,
                          2.4, 4.1,
                          3.2, 4.9);
          cairo_curve_to (cr, 3.5, 3.1,
                          5.2, 2.0,
                          7.0, 0.0);

          cairo_fill (cr);
110
          cairo_restore (cr);
111 112 113 114 115
        }
    }
}

static void
116 117 118 119 120 121
gtk_css_image_builtin_draw_option (GtkCssImage *image,
                                   cairo_t     *cr,
                                   double       width,
                                   double       height,
                                   gboolean     checked,
                                   gboolean     inconsistent)
122
{
123
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
124
  gint x, y, exterior_size, interior_size, pad;
125 126 127 128 129 130 131 132 133

  exterior_size = MIN (width, height);

  if (exterior_size % 2 == 0) /* Ensure odd */
    exterior_size -= 1;

  x = - (1 + exterior_size - width) / 2;
  y = - (1 + exterior_size - height) / 2;

134
  gdk_cairo_set_source_rgba (cr, &builtin->fg_color);
135

136 137 138 139 140
  pad = 1 + MAX (1, 2 * (exterior_size - 2) / 9);
  interior_size = MAX (1, exterior_size - 2 * pad);

  if (interior_size < 7)
    pad = MAX (0, (exterior_size - interior_size) / 2);
141

142
  if (inconsistent)
143 144 145 146 147 148 149 150 151 152 153 154
    {
      gint line_thickness;

      line_thickness = MAX (1, (3 + interior_size * 2) / 7);

      cairo_rectangle (cr,
                       x + pad,
                       y + pad + (interior_size - line_thickness) / 2.,
                       interior_size,
                       line_thickness);
      cairo_fill (cr);
    }
155
  else if (checked)
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
    {
      cairo_new_sub_path (cr);
      cairo_arc (cr,
                 x + pad + interior_size / 2.,
                 y + pad + interior_size / 2.,
                 interior_size / 2.,
                 0, 2 * G_PI);
      cairo_fill (cr);
    }
}

static void
gtk_css_image_builtin_draw_arrow (GtkCssImage            *image,
                                  cairo_t                *cr,
                                  double                  width,
                                  double                  height,
172
                                  GtkCssImageBuiltinType  image_type)
173
{
174
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
175 176 177 178 179
  double line_width;
  double size;

  size = MIN (width, height);

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
  cairo_translate (cr, width / 2.0, height / 2.0);
  switch (image_type)
  {
    case GTK_CSS_IMAGE_BUILTIN_ARROW_UP:
      break;
    case GTK_CSS_IMAGE_BUILTIN_ARROW_DOWN:
      cairo_rotate (cr, G_PI);
      break;
    case GTK_CSS_IMAGE_BUILTIN_ARROW_LEFT:
      cairo_rotate (cr, 3 * G_PI / 2);
      break;
    case GTK_CSS_IMAGE_BUILTIN_ARROW_RIGHT:
      cairo_rotate (cr, G_PI / 2);
      break;
    default:
      g_assert_not_reached ();
      break;
  }
198 199 200 201 202 203 204 205 206 207

  line_width = size / 3.0 / sqrt (2);
  cairo_set_line_width (cr, line_width);
  cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
  cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);

  cairo_scale (cr,
               (size / (size + line_width)),
               (size / (size + line_width)));

208 209
  cairo_move_to (cr, -size / 2.0, size / 4.0);
  cairo_rel_line_to (cr, size / 2.0, -size / 2.0);
210 211
  cairo_rel_line_to (cr, size / 2.0, size / 2.0);

212
  gdk_cairo_set_source_rgba (cr, &builtin->fg_color);
213 214 215 216
  cairo_stroke (cr);
}

static void
217 218 219 220 221 222 223
gtk_css_image_builtin_draw_expander (GtkCssImage *image,
                                     cairo_t     *cr,
                                     double       width,
                                     double       height,
                                     gboolean     horizontal,
                                     gboolean     is_rtl,
                                     gboolean     expanded)
224
{
225
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
226 227 228 229 230 231 232 233 234 235 236 237
  double vertical_overshoot;
  int diameter;
  double radius;
  double interp;		/* interpolation factor for center position */
  double x_double_horz, y_double_horz;
  double x_double_vert, y_double_vert;
  double x_double, y_double;
  gdouble angle;
  gint line_width;
  gdouble progress;

  line_width = 1;
238
  progress = expanded ? 1 : 0;
239

240
  if (!horizontal)
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
    {
      if (is_rtl)
        angle = (G_PI) - ((G_PI / 2) * progress);
      else
        angle = (G_PI / 2) * progress;
    }
  else
    {
      if (is_rtl)
        angle = (G_PI / 2) + ((G_PI / 2) * progress);
      else
        angle = (G_PI / 2) - ((G_PI / 2) * progress);
    }

  interp = progress;

  /* Compute distance that the stroke extends beyonds the end
   * of the triangle we draw.
   */
  vertical_overshoot = line_width / 2.0 * (1. / tan (G_PI / 8));

  /* For odd line widths, we end the vertical line of the triangle
   * at a half pixel, so we round differently.
   */
  if (line_width % 2 == 1)
    vertical_overshoot = ceil (0.5 + vertical_overshoot) - 0.5;
  else
    vertical_overshoot = ceil (vertical_overshoot);

  /* Adjust the size of the triangle we draw so that the entire stroke fits
   */
  diameter = (gint) MAX (3, width - 2 * vertical_overshoot);

  /* If the line width is odd, we want the diameter to be even,
   * and vice versa, so force the sum to be odd. This relationship
   * makes the point of the triangle look right.
   */
  diameter -= (1 - (diameter + line_width) % 2);

  radius = diameter / 2.;

  /* Adjust the center so that the stroke is properly aligned with
   * the pixel grid. The center adjustment is different for the
   * horizontal and vertical orientations. For intermediate positions
   * we interpolate between the two.
   */
  x_double_vert = floor ((width / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;
  y_double_vert = (height / 2) - 0.5;

  x_double_horz = (width / 2) - 0.5;
  y_double_horz = floor ((height / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;

  x_double = x_double_vert * (1 - interp) + x_double_horz * interp;
  y_double = y_double_vert * (1 - interp) + y_double_horz * interp;

  cairo_translate (cr, x_double, y_double);
  cairo_rotate (cr, angle);

  cairo_move_to (cr, - radius / 2., - radius);
  cairo_line_to (cr,   radius / 2.,   0);
  cairo_line_to (cr, - radius / 2.,   radius);
  cairo_close_path (cr);

  cairo_set_line_width (cr, line_width);

306
  gdk_cairo_set_source_rgba (cr, &builtin->fg_color);
307

308
  cairo_fill (cr);
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
}

static void
color_shade (const GdkRGBA *color,
             gdouble        factor,
             GdkRGBA       *color_return)
{
  GtkHSLA hsla;

  _gtk_hsla_init_from_rgba (&hsla, color);
  _gtk_hsla_shade (&hsla, &hsla, factor);
  _gdk_rgba_init_from_hsla (color_return, &hsla);
}

static void
render_dot (cairo_t       *cr,
            const GdkRGBA *lighter,
            const GdkRGBA *darker,
            gdouble        x,
            gdouble        y,
            gdouble        size)
{
  size = CLAMP ((gint) size, 2, 3);

  if (size == 2)
    {
      gdk_cairo_set_source_rgba (cr, lighter);
      cairo_rectangle (cr, x, y, 1, 1);
      cairo_rectangle (cr, x + 1, y + 1, 1, 1);
      cairo_fill (cr);
    }
  else if (size == 3)
    {
      gdk_cairo_set_source_rgba (cr, lighter);
      cairo_rectangle (cr, x, y, 2, 1);
      cairo_rectangle (cr, x, y, 1, 2);
      cairo_fill (cr);

      gdk_cairo_set_source_rgba (cr, darker);
      cairo_rectangle (cr, x + 1, y + 1, 2, 1);
      cairo_rectangle (cr, x + 2, y, 1, 2);
      cairo_fill (cr);
    }
}

static void
add_path_line (cairo_t        *cr,
               gdouble         x1,
               gdouble         y1,
               gdouble         x2,
               gdouble         y2)
{
  /* Adjust endpoints */
  if (y1 == y2)
    {
      y1 += 0.5;
      y2 += 0.5;
      x2 += 1;
    }
  else if (x1 == x2)
    {
      x1 += 0.5;
      x2 += 0.5;
      y2 += 1;
    }

  cairo_move_to (cr, x1, y1);
  cairo_line_to (cr, x2, y2);
}

void
380 381 382 383
gtk_css_image_builtin_draw_grip (GtkCssImage            *image,
                                 cairo_t                *cr,
                                 double                  width,
                                 double                  height,
384
                                 GtkCssImageBuiltinType  image_type)
385
{
386
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
387 388 389 390
  GdkRGBA lighter, darker;

  cairo_set_line_width (cr, 1.0);

391 392
  color_shade (&builtin->bg_color, 0.7, &darker);
  color_shade (&builtin->bg_color, 1.3, &lighter);
393 394

  /* align drawing area to the connected side */
395
  if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_LEFT)
396 397 398 399
    {
      if (height < width)
        width = height;
    }
400
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOPLEFT)
401 402 403 404 405 406
    {
      if (width < height)
        height = width;
      else if (height < width)
        width = height;
    }
407
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMLEFT)
408 409 410 411 412 413 414 415 416 417
    {
      /* make it square, aligning to bottom left */
      if (width < height)
        {
          cairo_translate (cr, 0, height - width);
          height = width;
        }
      else if (height < width)
        width = height;
    }
418
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_RIGHT)
419 420 421 422 423 424 425 426
    {
      /* aligning to right */
      if (height < width)
        {
          cairo_translate (cr, width - height, 0);
          width = height;
        }
    }
427
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOPRIGHT)
428 429 430 431 432 433 434 435 436
    {
      if (width < height)
        height = width;
      else if (height < width)
        {
          cairo_translate (cr, width - height, 0);
          width = height;
        }
    }
437
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMRIGHT)
438 439 440 441 442 443 444 445 446 447 448 449 450
    {
      /* make it square, aligning to bottom right */
      if (width < height)
        {
          cairo_translate (cr, 0, height - width);
          height = width;
        }
      else if (height < width)
        {
          cairo_translate (cr, width - height, 0);
          width = height;
        }
    }
451
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOP)
452 453 454 455
    {
      if (width < height)
        height = width;
    }
456
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOM)
457 458 459 460 461 462 463 464 465 466 467
    {
      /* align to bottom */
      if (width < height)
        {
          cairo_translate (cr, 0, height - width);
          height = width;
        }
    }
  else
    g_assert_not_reached ();

468 469
  if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_LEFT ||
      image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_RIGHT)
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
    {
      gint xi;

      xi = 0;

      while (xi < width)
        {
          gdk_cairo_set_source_rgba (cr, &lighter);
          add_path_line (cr, 0, 0, 0, height);
          cairo_stroke (cr);
          xi++;

          gdk_cairo_set_source_rgba (cr, &darker);
          add_path_line (cr, xi, 0, xi, height);
          cairo_stroke (cr);
          xi += 2;
        }
    }
488 489
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOP ||
           image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOM)
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
    {
      gint yi;

      yi = 0;

      while (yi < height)
        {
          gdk_cairo_set_source_rgba (cr, &lighter);
          add_path_line (cr, 0, yi, width, yi);
          cairo_stroke (cr);
          yi++;

          gdk_cairo_set_source_rgba (cr, &darker);
          add_path_line (cr, 0, yi, width, yi);
          cairo_stroke (cr);
          yi += 2;
        }
    }
508
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOPLEFT)
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
    {
      gint xi, yi;

      xi = width;
      yi = height;

      while (xi > 3)
        {
          gdk_cairo_set_source_rgba (cr, &darker);
          add_path_line (cr, xi, 0, 0, yi);
          cairo_stroke (cr);

          --xi;
          --yi;

          add_path_line (cr, xi, 0, 0, yi);
          cairo_stroke (cr);

          --xi;
          --yi;

          gdk_cairo_set_source_rgba (cr, &lighter);
          add_path_line (cr, xi, 0, 0, yi);
          cairo_stroke (cr);

          xi -= 3;
          yi -= 3;
        }
    }
538
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOPRIGHT)
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
    {
      gint xi, yi;

      xi = 0;
      yi = height;

      while (xi < (width - 3))
        {
          gdk_cairo_set_source_rgba (cr, &lighter);
          add_path_line (cr, xi, 0, width, yi);
          cairo_stroke (cr);

          ++xi;
          --yi;

          gdk_cairo_set_source_rgba (cr, &darker);
          add_path_line (cr, xi, 0, width, yi);
          cairo_stroke (cr);

          ++xi;
          --yi;

          add_path_line (cr, xi, 0, width, yi);
          cairo_stroke (cr);

          xi += 3;
          yi -= 3;
        }
    }
568
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMLEFT)
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
    {
      gint xi, yi;

      xi = width;
      yi = 0;

      while (xi > 3)
        {
          gdk_cairo_set_source_rgba (cr, &darker);
          add_path_line (cr, 0, yi, xi, height);
          cairo_stroke (cr);

          --xi;
          ++yi;

          add_path_line (cr, 0, yi, xi, height);
          cairo_stroke (cr);

          --xi;
          ++yi;

          gdk_cairo_set_source_rgba (cr, &lighter);
          add_path_line (cr, 0, yi, xi, height);
          cairo_stroke (cr);

          xi -= 3;
          yi += 3;
        }
    }
598
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMRIGHT)
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
    {
      gint xi, yi;

      xi = 0;
      yi = 0;

      while (xi < (width - 3))
        {
          gdk_cairo_set_source_rgba (cr, &lighter);
          add_path_line (cr, xi, height, width, yi);
          cairo_stroke (cr);

          ++xi;
          ++yi;

          gdk_cairo_set_source_rgba (cr, &darker);
          add_path_line (cr, xi, height, width, yi);
          cairo_stroke (cr);

          ++xi;
          ++yi;

          add_path_line (cr, xi, height, width, yi);
          cairo_stroke (cr);

          xi += 3;
          yi += 3;
        }
    }
}

void
631 632 633 634
gtk_css_image_builtin_draw_pane_separator (GtkCssImage *image,
                                           cairo_t     *cr,
                                           double       width,
                                           double       height)
635
{
636
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
637 638 639 640 641
  GdkRGBA lighter, darker;
  gint xx, yy;

  cairo_set_line_width (cr, 1.0);

642 643
  color_shade (&builtin->bg_color, 0.7, &darker);
  color_shade (&builtin->bg_color, 1.3, &lighter);
644 645 646 647 648 649 650 651 652 653

  if (width > height)
    for (xx = width / 2 - 15; xx <= width / 2 + 15; xx += 5)
      render_dot (cr, &lighter, &darker, xx, height / 2 - 1, 3);
  else
    for (yy = height / 2 - 15; yy <= height / 2 + 15; yy += 5)
      render_dot (cr, &lighter, &darker, width / 2 - 1, yy, 3);
}

void
654 655 656 657
gtk_css_image_builtin_draw_handle (GtkCssImage *image,
                                   cairo_t     *cr,
                                   double       width,
                                   double       height)
658
{
659
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
660 661 662 663 664
  GdkRGBA lighter, darker;
  gint xx, yy;

  cairo_set_line_width (cr, 1.0);

665 666
  color_shade (&builtin->bg_color, 0.7, &darker);
  color_shade (&builtin->bg_color, 1.3, &lighter);
667 668 669 670 671 672 673 674 675 676

  for (yy = 0; yy < height; yy += 3)
    for (xx = 0; xx < width; xx += 6)
      {
        render_dot (cr, &lighter, &darker, xx, yy, 2);
        render_dot (cr, &lighter, &darker, xx + 3, yy + 1, 2);
      }
}

static void
677 678 679 680
gtk_css_image_builtin_draw_spinner (GtkCssImage *image,
                                    cairo_t     *cr,
                                    double       width,
                                    double       height)
681
{
682
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
683
  guint num_steps;
684
  gdouble radius;
685 686
  gdouble half;
  gint i;
687 688 689 690 691

  radius = MIN (width / 2, height / 2);

  cairo_translate (cr, width / 2, height / 2);

692 693 694
  num_steps = 12;

  cairo_set_line_width (cr, 2.0);
695

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
  half = num_steps / 2;

  for (i = 0; i < num_steps; i++)
    {
      gint inset = 0.7 * radius;
      /* transparency is a function of time and intial value */
      gdouble t = 1.0 - (gdouble) i / num_steps;
      gdouble xscale = - sin (i * G_PI / half);
      gdouble yscale = - cos (i * G_PI / half);

      cairo_move_to (cr,
                     (radius - inset) * xscale,
                     (radius - inset) * yscale);
      cairo_line_to (cr,
                     radius * xscale,
                     radius * yscale);

      cairo_set_source_rgba (cr,
                             builtin->fg_color.red,
                             builtin->fg_color.green,
                             builtin->fg_color.blue,
                             builtin->fg_color.alpha * t);

      cairo_stroke (cr);
    }
721 722 723 724 725 726 727
}

static void
gtk_css_image_builtin_real_draw (GtkCssImage        *image,
                                 cairo_t            *cr,
                                 double              width,
                                 double              height)
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
{
  /* It's a builtin image, other code will draw things */
}


static gboolean
gtk_css_image_builtin_parse (GtkCssImage  *image,
                             GtkCssParser *parser)
{
  if (!_gtk_css_parser_try (parser, "builtin", TRUE))
    {
      _gtk_css_parser_error (parser, "Expected 'builtin'");
      return FALSE;
    }

  return TRUE;
}

static void
gtk_css_image_builtin_print (GtkCssImage *image,
                             GString     *string)
{
  g_string_append (string, "builtin");
}

static GtkCssImage *
gtk_css_image_builtin_compute (GtkCssImage             *image,
                               guint                    property_id,
                               GtkStyleProviderPrivate *provider,
757
                               GtkCssStyle             *style,
758
                               GtkCssStyle             *parent_style)
759
{
760 761 762 763 764 765 766 767
  GtkCssImageBuiltin *result;

  result = g_object_new (GTK_TYPE_CSS_IMAGE_BUILTIN, NULL);

  result->fg_color = *_gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_COLOR));
  result->bg_color = *_gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_COLOR));

  return GTK_CSS_IMAGE (result);
768 769 770 771 772 773
}

static gboolean
gtk_css_image_builtin_equal (GtkCssImage *image1,
                             GtkCssImage *image2)
{
774 775
  GtkCssImageBuiltin *builtin1 = GTK_CSS_IMAGE_BUILTIN (image1);
  GtkCssImageBuiltin *builtin2 = GTK_CSS_IMAGE_BUILTIN (image2);
776

777
  return gdk_rgba_equal (&builtin1->fg_color, &builtin2->fg_color)
778
      && gdk_rgba_equal (&builtin1->bg_color, &builtin2->bg_color);
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
}

static void
gtk_css_image_builtin_dispose (GObject *object)
{
  if (the_one_true_image == GTK_CSS_IMAGE (object))
    the_one_true_image = NULL;

  G_OBJECT_CLASS (gtk_css_image_builtin_parent_class)->dispose (object);
}

static void
gtk_css_image_builtin_class_init (GtkCssImageBuiltinClass *klass)
{
  GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass);
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

796
  image_class->draw = gtk_css_image_builtin_real_draw;
797 798 799 800 801 802 803 804 805 806 807
  image_class->parse = gtk_css_image_builtin_parse;
  image_class->print = gtk_css_image_builtin_print;
  image_class->compute = gtk_css_image_builtin_compute;
  image_class->equal = gtk_css_image_builtin_equal;

  object_class->dispose = gtk_css_image_builtin_dispose;
}

static void
gtk_css_image_builtin_init (GtkCssImageBuiltin *builtin)
{
808 809 810 811
  /* white background */
  builtin->bg_color.red = builtin->bg_color.green = builtin->bg_color.blue = builtin->bg_color.alpha = 1.0;
  /* black foreground */
  builtin->fg_color.alpha = 1.0;
812 813 814 815 816 817 818 819 820 821 822 823 824
}

GtkCssImage *
gtk_css_image_builtin_new (void)
{
  if (the_one_true_image == NULL)
    the_one_true_image = g_object_new (GTK_TYPE_CSS_IMAGE_BUILTIN, NULL);
  else
    g_object_ref (the_one_true_image);

  return the_one_true_image;
}

825 826 827 828 829
void
gtk_css_image_builtin_draw (GtkCssImage            *image,
                            cairo_t                *cr,
                            double                  width,
                            double                  height,
830
                            GtkCssImageBuiltinType  image_type)
831
{
832 833 834 835 836 837
  if (!GTK_IS_CSS_IMAGE_BUILTIN (image))
    {
      _gtk_css_image_draw (image, cr, width, height);
      return;
    }

838 839 840 841 842 843 844 845
  switch (image_type)
  {
  default:
    g_assert_not_reached ();
    break;
  case GTK_CSS_IMAGE_BUILTIN_NONE:
    break;
  case GTK_CSS_IMAGE_BUILTIN_CHECK:
846
  case GTK_CSS_IMAGE_BUILTIN_CHECK_INCONSISTENT:
847 848
    gtk_css_image_builtin_draw_check (image, cr,
                                      width, height,
849
                                      image_type == GTK_CSS_IMAGE_BUILTIN_CHECK,
850
                                      image_type == GTK_CSS_IMAGE_BUILTIN_CHECK_INCONSISTENT);
851 852
    break;
  case GTK_CSS_IMAGE_BUILTIN_OPTION:
853
  case GTK_CSS_IMAGE_BUILTIN_OPTION_INCONSISTENT:
854 855
    gtk_css_image_builtin_draw_option (image, cr,
                                       width, height,
856
                                       image_type == GTK_CSS_IMAGE_BUILTIN_OPTION,
857
                                       image_type == GTK_CSS_IMAGE_BUILTIN_OPTION_INCONSISTENT);
858
    break;
859 860 861 862
  case GTK_CSS_IMAGE_BUILTIN_ARROW_UP:
  case GTK_CSS_IMAGE_BUILTIN_ARROW_DOWN:
  case GTK_CSS_IMAGE_BUILTIN_ARROW_LEFT:
  case GTK_CSS_IMAGE_BUILTIN_ARROW_RIGHT:
863 864
    gtk_css_image_builtin_draw_arrow (image, cr,
                                      width, height,
865
                                      image_type);
866
    break;
867
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_LEFT:
868 869
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
870
                                         TRUE, FALSE, FALSE);
871 872 873 874
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_LEFT:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
875
                                         FALSE, FALSE, FALSE);
876 877 878 879
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_RIGHT:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
880
                                         TRUE, TRUE, FALSE);
881 882 883 884
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_RIGHT:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
885
                                         FALSE, TRUE, FALSE);
886 887 888 889
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_LEFT_EXPANDED:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
890
                                         TRUE, FALSE, TRUE);
891 892 893 894
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_LEFT_EXPANDED:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
895
                                         FALSE, FALSE, TRUE);
896 897 898 899
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_RIGHT_EXPANDED:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
900
                                         TRUE, TRUE, TRUE);
901 902 903 904
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_RIGHT_EXPANDED:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
905
                                         FALSE, TRUE, TRUE);
906
    break;
907 908 909 910 911 912 913 914
  case GTK_CSS_IMAGE_BUILTIN_GRIP_TOPLEFT:
  case GTK_CSS_IMAGE_BUILTIN_GRIP_TOP:
  case GTK_CSS_IMAGE_BUILTIN_GRIP_TOPRIGHT:
  case GTK_CSS_IMAGE_BUILTIN_GRIP_RIGHT:
  case GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMRIGHT:
  case GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOM:
  case GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMLEFT:
  case GTK_CSS_IMAGE_BUILTIN_GRIP_LEFT:
915 916
    gtk_css_image_builtin_draw_grip (image, cr,
                                     width, height,
917
                                     image_type);
918 919 920
    break;
  case GTK_CSS_IMAGE_BUILTIN_PANE_SEPARATOR:
    gtk_css_image_builtin_draw_pane_separator (image, cr,
921
                                               width, height);
922 923 924
    break;
  case GTK_CSS_IMAGE_BUILTIN_HANDLE:
    gtk_css_image_builtin_draw_handle (image, cr,
925
                                       width, height);
926 927 928
    break;
  case GTK_CSS_IMAGE_BUILTIN_SPINNER:
    gtk_css_image_builtin_draw_spinner (image, cr,
929
                                        width, height);
930 931 932 933
    break;
  }
}