gtkcssimagebuiltin.c 29.2 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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
  gint x, y, exterior_size, interior_size, thickness, pad;

  exterior_size = MIN (width, height);

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

  /* FIXME: thickness */
  thickness = 1;
  pad = thickness + MAX (1, (exterior_size - 2 * thickness) / 9);
  interior_size = MAX (1, exterior_size - 2 * pad);

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

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

69
  if (builtin->border_width > 0)
70
    {
71
      cairo_set_line_width (cr, builtin->border_width);
72 73

      cairo_rectangle (cr, x + 0.5, y + 0.5, exterior_size - 1, exterior_size - 1);
74
      gdk_cairo_set_source_rgba (cr, &builtin->bg_color);
75 76
      cairo_fill_preserve (cr);

77
      gdk_cairo_set_source_rgba (cr, &builtin->border_color);
78 79 80
      cairo_stroke (cr);
    }

81
  gdk_cairo_set_source_rgba (cr, &builtin->fg_color);
82

83
  if (inconsistent)
84 85 86 87 88 89 90 91 92 93 94 95
    {
      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
    {
96
      if (checked)
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
        {
          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);
        }
    }
}

static void
131 132 133 134 135 136
gtk_css_image_builtin_draw_option (GtkCssImage *image,
                                   cairo_t     *cr,
                                   double       width,
                                   double       height,
                                   gboolean     checked,
                                   gboolean     inconsistent)
137
{
138
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
139 140 141 142 143 144 145 146 147 148
  gint x, y, exterior_size, interior_size, thickness, pad;

  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;

149
  if (builtin->border_width > 0)
150
    {
151
      cairo_set_line_width (cr, builtin->border_width);
152 153 154 155 156 157 158 159

      cairo_new_sub_path (cr);
      cairo_arc (cr,
                 x + exterior_size / 2.,
                 y + exterior_size / 2.,
                 (exterior_size - 1) / 2.,
                 0, 2 * G_PI);

160
      gdk_cairo_set_source_rgba (cr, &builtin->bg_color);
161 162
      cairo_fill_preserve (cr);

163
      gdk_cairo_set_source_rgba (cr, &builtin->border_color);
164 165 166
      cairo_stroke (cr);
    }

167
  gdk_cairo_set_source_rgba (cr, &builtin->fg_color);
168 169 170 171

  /* FIXME: thickness */
  thickness = 1;

172
  if (inconsistent)
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
    {
      gint line_thickness;

      pad = thickness + MAX (1, (exterior_size - 2 * thickness) / 9);
      interior_size = MAX (1, exterior_size - 2 * pad);

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

      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);
    }
194
  if (checked)
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    {
      pad = thickness + MAX (1, 2 * (exterior_size - 2 * thickness) / 9);
      interior_size = MAX (1, exterior_size - 2 * pad);

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

      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,
220
                                  GtkCssImageBuiltinType  image_type)
221
{
222
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
223 224 225 226 227
  double line_width;
  double size;

  size = MIN (width, height);

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
  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;
  }
246 247 248 249 250 251 252 253 254 255

  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)));

256 257
  cairo_move_to (cr, -size / 2.0, size / 4.0);
  cairo_rel_line_to (cr, size / 2.0, -size / 2.0);
258 259
  cairo_rel_line_to (cr, size / 2.0, size / 2.0);

260
  gdk_cairo_set_source_rgba (cr, &builtin->fg_color);
261 262 263 264
  cairo_stroke (cr);
}

static void
265 266 267 268 269 270 271
gtk_css_image_builtin_draw_expander (GtkCssImage *image,
                                     cairo_t     *cr,
                                     double       width,
                                     double       height,
                                     gboolean     horizontal,
                                     gboolean     is_rtl,
                                     gboolean     expanded)
272
{
273
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
274 275 276 277 278 279 280 281 282 283 284 285
  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;
286
  progress = expanded ? 1 : 0;
287

288
  if (!horizontal)
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 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
    {
      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);

354
  gdk_cairo_set_source_rgba (cr, &builtin->fg_color);
355 356 357

  cairo_fill_preserve (cr);

358
  gdk_cairo_set_source_rgba (cr, &builtin->border_color);
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
  cairo_stroke (cr);
}

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
431 432 433 434
gtk_css_image_builtin_draw_grip (GtkCssImage            *image,
                                 cairo_t                *cr,
                                 double                  width,
                                 double                  height,
435
                                 GtkCssImageBuiltinType  image_type)
436
{
437
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
438 439 440 441
  GdkRGBA lighter, darker;

  cairo_set_line_width (cr, 1.0);

442 443
  color_shade (&builtin->bg_color, 0.7, &darker);
  color_shade (&builtin->bg_color, 1.3, &lighter);
444 445

  /* align drawing area to the connected side */
446
  if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_LEFT)
447 448 449 450
    {
      if (height < width)
        width = height;
    }
451
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOPLEFT)
452 453 454 455 456 457
    {
      if (width < height)
        height = width;
      else if (height < width)
        width = height;
    }
458
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMLEFT)
459 460 461 462 463 464 465 466 467 468
    {
      /* make it square, aligning to bottom left */
      if (width < height)
        {
          cairo_translate (cr, 0, height - width);
          height = width;
        }
      else if (height < width)
        width = height;
    }
469
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_RIGHT)
470 471 472 473 474 475 476 477
    {
      /* aligning to right */
      if (height < width)
        {
          cairo_translate (cr, width - height, 0);
          width = height;
        }
    }
478
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOPRIGHT)
479 480 481 482 483 484 485 486 487
    {
      if (width < height)
        height = width;
      else if (height < width)
        {
          cairo_translate (cr, width - height, 0);
          width = height;
        }
    }
488
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMRIGHT)
489 490 491 492 493 494 495 496 497 498 499 500 501
    {
      /* 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;
        }
    }
502
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOP)
503 504 505 506
    {
      if (width < height)
        height = width;
    }
507
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOM)
508 509 510 511 512 513 514 515 516 517 518
    {
      /* align to bottom */
      if (width < height)
        {
          cairo_translate (cr, 0, height - width);
          height = width;
        }
    }
  else
    g_assert_not_reached ();

519 520
  if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_LEFT ||
      image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_RIGHT)
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
    {
      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;
        }
    }
539 540
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOP ||
           image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOM)
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
    {
      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;
        }
    }
559
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOPLEFT)
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
    {
      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;
        }
    }
589
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_TOPRIGHT)
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
    {
      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;
        }
    }
619
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMLEFT)
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
    {
      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;
        }
    }
649
  else if (image_type == GTK_CSS_IMAGE_BUILTIN_GRIP_BOTTOMRIGHT)
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
    {
      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
682 683 684 685
gtk_css_image_builtin_draw_pane_separator (GtkCssImage *image,
                                           cairo_t     *cr,
                                           double       width,
                                           double       height)
686
{
687
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
688 689 690 691 692
  GdkRGBA lighter, darker;
  gint xx, yy;

  cairo_set_line_width (cr, 1.0);

693 694
  color_shade (&builtin->bg_color, 0.7, &darker);
  color_shade (&builtin->bg_color, 1.3, &lighter);
695 696 697 698 699 700 701 702 703 704

  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
705 706 707 708
gtk_css_image_builtin_draw_handle (GtkCssImage *image,
                                   cairo_t     *cr,
                                   double       width,
                                   double       height)
709
{
710
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
711 712 713 714 715
  GdkRGBA lighter, darker;
  gint xx, yy;

  cairo_set_line_width (cr, 1.0);

716 717
  color_shade (&builtin->bg_color, 0.7, &darker);
  color_shade (&builtin->bg_color, 1.3, &lighter);
718 719 720 721 722 723 724 725 726 727

  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
728 729 730 731
gtk_css_image_builtin_draw_spinner (GtkCssImage *image,
                                    cairo_t     *cr,
                                    double       width,
                                    double       height)
732
{
733
  GtkCssImageBuiltin *builtin = GTK_CSS_IMAGE_BUILTIN (image);
734
  guint num_steps;
735
  gdouble radius;
736 737
  gdouble half;
  gint i;
738 739 740 741 742

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

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

743 744 745
  num_steps = 12;

  cairo_set_line_width (cr, 2.0);
746

747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
  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);
    }
772 773 774 775 776 777 778
}

static void
gtk_css_image_builtin_real_draw (GtkCssImage        *image,
                                 cairo_t            *cr,
                                 double              width,
                                 double              height)
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
{
  /* 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,
808
                               GtkCssStyle             *style,
809
                               GtkCssStyle             *parent_style)
810
{
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
  GtkCssImageBuiltin *result;
  GtkBorderStyle border_style;

  result = g_object_new (GTK_TYPE_CSS_IMAGE_BUILTIN, NULL);

  border_style = _gtk_css_border_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
  if (border_style == GTK_BORDER_STYLE_SOLID)
    {
      GtkBorder border;

      border.top = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100);
      border.right = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100);
      border.bottom = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100);
      border.left = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100);

      result->border_width = MIN (MIN (border.top, border.bottom),
                                  MIN (border.left, border.right));
    }

  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));
  result->border_color = *_gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_COLOR));

  return GTK_CSS_IMAGE (result);
835 836 837 838 839 840
}

static gboolean
gtk_css_image_builtin_equal (GtkCssImage *image1,
                             GtkCssImage *image2)
{
841 842
  GtkCssImageBuiltin *builtin1 = GTK_CSS_IMAGE_BUILTIN (image1);
  GtkCssImageBuiltin *builtin2 = GTK_CSS_IMAGE_BUILTIN (image2);
843

844 845 846 847
  return gdk_rgba_equal (&builtin1->fg_color, &builtin2->fg_color)
      && gdk_rgba_equal (&builtin1->bg_color, &builtin2->bg_color)
      && gdk_rgba_equal (&builtin1->border_color, &builtin2->border_color)
      && builtin1->border_width == builtin2->border_width;
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
}

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);

865
  image_class->draw = gtk_css_image_builtin_real_draw;
866 867 868 869 870 871 872 873 874 875 876
  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)
{
877 878 879 880
  /* 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;
881 882 883 884 885 886 887 888 889 890 891 892 893
}

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;
}

894 895 896 897 898
void
gtk_css_image_builtin_draw (GtkCssImage            *image,
                            cairo_t                *cr,
                            double                  width,
                            double                  height,
899
                            GtkCssImageBuiltinType  image_type)
900
{
901 902 903 904 905 906
  if (!GTK_IS_CSS_IMAGE_BUILTIN (image))
    {
      _gtk_css_image_draw (image, cr, width, height);
      return;
    }

907 908 909 910 911 912 913 914
  switch (image_type)
  {
  default:
    g_assert_not_reached ();
    break;
  case GTK_CSS_IMAGE_BUILTIN_NONE:
    break;
  case GTK_CSS_IMAGE_BUILTIN_CHECK:
915 916
  case GTK_CSS_IMAGE_BUILTIN_CHECK_CHECKED:
  case GTK_CSS_IMAGE_BUILTIN_CHECK_INCONSISTENT:
917 918
    gtk_css_image_builtin_draw_check (image, cr,
                                      width, height,
919
                                      image_type == GTK_CSS_IMAGE_BUILTIN_CHECK_CHECKED,
920
                                      image_type == GTK_CSS_IMAGE_BUILTIN_CHECK_INCONSISTENT);
921 922
    break;
  case GTK_CSS_IMAGE_BUILTIN_OPTION:
923 924
  case GTK_CSS_IMAGE_BUILTIN_OPTION_CHECKED:
  case GTK_CSS_IMAGE_BUILTIN_OPTION_INCONSISTENT:
925 926
    gtk_css_image_builtin_draw_option (image, cr,
                                       width, height,
927
                                       image_type == GTK_CSS_IMAGE_BUILTIN_OPTION_CHECKED,
928
                                       image_type == GTK_CSS_IMAGE_BUILTIN_OPTION_INCONSISTENT);
929
    break;
930 931 932 933
  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:
934 935
    gtk_css_image_builtin_draw_arrow (image, cr,
                                      width, height,
936
                                      image_type);
937
    break;
938
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_LEFT:
939 940
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
941
                                         TRUE, FALSE, FALSE);
942 943 944 945
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_LEFT:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
946
                                         FALSE, FALSE, FALSE);
947 948 949 950
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_RIGHT:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
951
                                         TRUE, TRUE, FALSE);
952 953 954 955
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_RIGHT:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
956
                                         FALSE, TRUE, FALSE);
957 958 959 960
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_LEFT_EXPANDED:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
961
                                         TRUE, FALSE, TRUE);
962 963 964 965
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_LEFT_EXPANDED:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
966
                                         FALSE, FALSE, TRUE);
967 968 969 970
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_HORIZONTAL_RIGHT_EXPANDED:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
971
                                         TRUE, TRUE, TRUE);
972 973 974 975
    break;
  case GTK_CSS_IMAGE_BUILTIN_EXPANDER_VERTICAL_RIGHT_EXPANDED:
    gtk_css_image_builtin_draw_expander (image, cr,
                                         width, height,
976
                                         FALSE, TRUE, TRUE);
977
    break;
978 979 980 981 982 983 984 985
  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:
986 987
    gtk_css_image_builtin_draw_grip (image, cr,
                                     width, height,
988
                                     image_type);
989 990 991
    break;
  case GTK_CSS_IMAGE_BUILTIN_PANE_SEPARATOR:
    gtk_css_image_builtin_draw_pane_separator (image, cr,
992
                                               width, height);
993 994 995
    break;
  case GTK_CSS_IMAGE_BUILTIN_HANDLE:
    gtk_css_image_builtin_draw_handle (image, cr,
996
                                       width, height);
997 998 999
    break;
  case GTK_CSS_IMAGE_BUILTIN_SPINNER:
    gtk_css_image_builtin_draw_spinner (image, cr,
1000
                                        width, height);
1001 1002 1003 1004
    break;
  }
}