gimpcanvashandle.c 20 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 24 25 26 27 28 29 30
/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * gimpcanvashandle.c
 * Copyright (C) 2010 Michael Natterer <mitch@gimp.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <gegl.h>
#include <gtk/gtk.h>

#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"

#include "display-types.h"

31
#include "core/gimp-cairo.h"
32

33
#include "gimpcanvashandle.h"
34
#include "gimpcanvasitem-utils.h"
35 36 37 38 39 40 41 42 43 44 45
#include "gimpdisplayshell.h"


enum
{
  PROP_0,
  PROP_TYPE,
  PROP_ANCHOR,
  PROP_X,
  PROP_Y,
  PROP_WIDTH,
46 47 48
  PROP_HEIGHT,
  PROP_START_ANGLE,
  PROP_SLICE_ANGLE
49 50 51 52 53 54 55
};


typedef struct _GimpCanvasHandlePrivate GimpCanvasHandlePrivate;

struct _GimpCanvasHandlePrivate
{
56 57 58 59 60 61 62
  GimpHandleType   type;
  GimpHandleAnchor anchor;
  gdouble          x;
  gdouble          y;
  gint             width;
  gint             height;
  gdouble          start_angle;
Mikael Magnusson's avatar
Mikael Magnusson committed
63
  gdouble          slice_angle;
64 65 66 67 68 69 70 71 72 73
};

#define GET_PRIVATE(handle) \
        G_TYPE_INSTANCE_GET_PRIVATE (handle, \
                                     GIMP_TYPE_CANVAS_HANDLE, \
                                     GimpCanvasHandlePrivate)


/*  local function prototypes  */

74 75 76 77 78 79 80 81 82 83 84 85 86 87
static void             gimp_canvas_handle_set_property (GObject        *object,
                                                         guint           property_id,
                                                         const GValue   *value,
                                                         GParamSpec     *pspec);
static void             gimp_canvas_handle_get_property (GObject        *object,
                                                         guint           property_id,
                                                         GValue         *value,
                                                         GParamSpec     *pspec);
static void             gimp_canvas_handle_draw         (GimpCanvasItem *item,
                                                         cairo_t        *cr);
static cairo_region_t * gimp_canvas_handle_get_extents  (GimpCanvasItem *item);
static gboolean         gimp_canvas_handle_hit          (GimpCanvasItem *item,
                                                         gdouble         x,
                                                         gdouble         y);
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106


G_DEFINE_TYPE (GimpCanvasHandle, gimp_canvas_handle,
               GIMP_TYPE_CANVAS_ITEM)

#define parent_class gimp_canvas_handle_parent_class


static void
gimp_canvas_handle_class_init (GimpCanvasHandleClass *klass)
{
  GObjectClass        *object_class = G_OBJECT_CLASS (klass);
  GimpCanvasItemClass *item_class   = GIMP_CANVAS_ITEM_CLASS (klass);

  object_class->set_property = gimp_canvas_handle_set_property;
  object_class->get_property = gimp_canvas_handle_get_property;

  item_class->draw           = gimp_canvas_handle_draw;
  item_class->get_extents    = gimp_canvas_handle_get_extents;
107
  item_class->hit            = gimp_canvas_handle_hit;
108 109 110 111 112 113 114 115 116

  g_object_class_install_property (object_class, PROP_TYPE,
                                   g_param_spec_enum ("type", NULL, NULL,
                                                      GIMP_TYPE_HANDLE_TYPE,
                                                      GIMP_HANDLE_CROSS,
                                                      GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_ANCHOR,
                                   g_param_spec_enum ("anchor", NULL, NULL,
117 118
                                                      GIMP_TYPE_HANDLE_ANCHOR,
                                                      GIMP_HANDLE_ANCHOR_CENTER,
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
                                                      GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_X,
                                   g_param_spec_double ("x", NULL, NULL,
                                                        -GIMP_MAX_IMAGE_SIZE,
                                                        GIMP_MAX_IMAGE_SIZE, 0,
                                                        GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_Y,
                                   g_param_spec_double ("y", NULL, NULL,
                                                        -GIMP_MAX_IMAGE_SIZE,
                                                        GIMP_MAX_IMAGE_SIZE, 0,
                                                        GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_WIDTH,
                                   g_param_spec_int ("width", NULL, NULL,
                                                     3, 1001, 7,
                                                     GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_HEIGHT,
                                   g_param_spec_int ("height", NULL, NULL,
                                                     3, 1001, 7,
                                                     GIMP_PARAM_READWRITE));

143 144 145 146 147 148 149 150 151 152
  g_object_class_install_property (object_class, PROP_START_ANGLE,
                                   g_param_spec_double ("start-angle", NULL, NULL,
                                                        -1000, 1000, 0,
                                                        GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_SLICE_ANGLE,
                                   g_param_spec_double ("slice-angle", NULL, NULL,
                                                        -1000, 1000, 2 * G_PI,
                                                        GIMP_PARAM_READWRITE));

153 154 155 156 157 158
  g_type_class_add_private (klass, sizeof (GimpCanvasHandlePrivate));
}

static void
gimp_canvas_handle_init (GimpCanvasHandle *handle)
{
159 160
  GimpCanvasHandlePrivate *private = GET_PRIVATE (handle);

161 162 163
  gimp_canvas_item_set_line_cap (GIMP_CANVAS_ITEM (handle),
                                 CAIRO_LINE_CAP_SQUARE);

164 165
  private->start_angle = 0.0;
  private->slice_angle = 2.0 * G_PI;
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
}

static void
gimp_canvas_handle_set_property (GObject      *object,
                                 guint         property_id,
                                 const GValue *value,
                                 GParamSpec   *pspec)
{
  GimpCanvasHandlePrivate *private = GET_PRIVATE (object);

  switch (property_id)
    {
    case PROP_TYPE:
      private->type = g_value_get_enum (value);
      break;
    case PROP_ANCHOR:
      private->anchor = g_value_get_enum (value);
      break;
    case PROP_X:
      private->x = g_value_get_double (value);
      break;
    case PROP_Y:
      private->y = g_value_get_double (value);
      break;
    case PROP_WIDTH:
      private->width = g_value_get_int (value);
      break;
    case PROP_HEIGHT:
      private->height = g_value_get_int (value);
      break;
196 197 198 199 200 201
    case PROP_START_ANGLE:
      private->start_angle = g_value_get_double (value);
      break;
    case PROP_SLICE_ANGLE:
      private->slice_angle = g_value_get_double (value);
      break;
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_canvas_handle_get_property (GObject    *object,
                                 guint       property_id,
                                 GValue     *value,
                                 GParamSpec *pspec)
{
  GimpCanvasHandlePrivate *private = GET_PRIVATE (object);

  switch (property_id)
    {
    case PROP_TYPE:
      g_value_set_enum (value, private->type);
      break;
    case PROP_ANCHOR:
      g_value_set_enum (value, private->anchor);
      break;
    case PROP_X:
      g_value_set_double (value, private->x);
      break;
    case PROP_Y:
      g_value_set_double (value, private->y);
      break;
    case PROP_WIDTH:
      g_value_set_int (value, private->width);
      break;
    case PROP_HEIGHT:
      g_value_set_int (value, private->height);
      break;
237 238 239 240 241 242
    case PROP_START_ANGLE:
      g_value_set_double (value, private->start_angle);
      break;
    case PROP_SLICE_ANGLE:
      g_value_set_double (value, private->slice_angle);
      break;
243 244 245 246 247 248 249 250

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
251 252 253
gimp_canvas_handle_transform (GimpCanvasItem *item,
                              gdouble        *x,
                              gdouble        *y)
254 255 256
{
  GimpCanvasHandlePrivate *private = GET_PRIVATE (item);

257 258 259
  gimp_canvas_item_transform_xy_f (item,
                                   private->x, private->y,
                                   x, y);
260 261 262 263 264

  switch (private->type)
    {
    case GIMP_HANDLE_SQUARE:
    case GIMP_HANDLE_FILLED_SQUARE:
265 266 267 268 269
      gimp_canvas_item_shift_to_north_west (private->anchor,
                                            *x, *y,
                                            private->width,
                                            private->height,
                                            x, y);
270 271 272 273 274
      break;

    case GIMP_HANDLE_CIRCLE:
    case GIMP_HANDLE_FILLED_CIRCLE:
    case GIMP_HANDLE_CROSS:
275
    case GIMP_HANDLE_CROSSHAIR:
276 277
    case GIMP_HANDLE_DIAMOND:
    case GIMP_HANDLE_FILLED_DIAMOND:
278 279 280 281 282
      gimp_canvas_item_shift_to_center (private->anchor,
                                        *x, *y,
                                        private->width,
                                        private->height,
                                        x, y);
283 284 285 286 287 288
      break;

    default:
      break;
    }

289 290
  *x = floor (*x) + 0.5;
  *y = floor (*y) + 0.5;
291 292 293
}

static void
294 295
gimp_canvas_handle_draw (GimpCanvasItem *item,
                         cairo_t        *cr)
296 297
{
  GimpCanvasHandlePrivate *private = GET_PRIVATE (item);
298
  gdouble                  x, y, tx, ty;
299

300 301 302 303 304
  gimp_canvas_handle_transform (item, &x, &y);

  gimp_canvas_item_transform_xy_f (item,
                                   private->x, private->y,
                                   &tx, &ty);
305 306 307 308 309

  switch (private->type)
    {
    case GIMP_HANDLE_SQUARE:
    case GIMP_HANDLE_FILLED_SQUARE:
310 311
    case GIMP_HANDLE_DIAMOND:
    case GIMP_HANDLE_FILLED_DIAMOND:
312
    case GIMP_HANDLE_CROSS:
313 314 315 316 317 318 319 320 321 322 323
      cairo_save (cr);
      cairo_translate (cr, tx, ty);
      cairo_rotate (cr, private->start_angle);
      cairo_translate (cr, -tx, -ty);

      switch (private->type)
        {
        case GIMP_HANDLE_SQUARE:
          cairo_rectangle (cr, x, y, private->width - 1.0, private->height - 1.0);
          _gimp_canvas_item_stroke (item, cr);
          break;
324

325 326 327 328
        case GIMP_HANDLE_FILLED_SQUARE:
          cairo_rectangle (cr, x - 0.5, y - 0.5, private->width, private->height);
          _gimp_canvas_item_fill (item, cr);
          break;
329

330 331 332 333 334 335 336 337 338 339 340 341
        case GIMP_HANDLE_DIAMOND:
        case GIMP_HANDLE_FILLED_DIAMOND:
          cairo_move_to (cr, x, y - (gdouble) private->height / 2.0);
          cairo_line_to (cr, x + (gdouble) private->width / 2.0, y);
          cairo_line_to (cr, x, y + (gdouble) private->height / 2.0);
          cairo_line_to (cr, x - (gdouble) private->width / 2.0, y);
          cairo_line_to (cr, x, y - (gdouble) private->height / 2.0);
          if (private->type == GIMP_HANDLE_DIAMOND)
            _gimp_canvas_item_stroke (item, cr);
          else
            _gimp_canvas_item_fill (item, cr);
          break;
342 343 344 345 346 347 348 349 350

        case GIMP_HANDLE_CROSS:
          cairo_move_to (cr, x - private->width / 2, y);
          cairo_line_to (cr, x + private->width / 2 - 0.5, y);
          cairo_move_to (cr, x, y - private->height / 2);
          cairo_line_to (cr, x, y + private->height / 2 - 0.5);
          _gimp_canvas_item_stroke (item, cr);
          break;

351 352 353
        default:
          g_assert_not_reached ();
        }
354

355
      cairo_restore (cr);
356 357 358
      break;

    case GIMP_HANDLE_CIRCLE:
359 360 361
      gimp_cairo_add_arc (cr, x, y, private->width / 2,
                          private->start_angle,
                          private->slice_angle);
362

363
      _gimp_canvas_item_stroke (item, cr);
364 365 366
      break;

    case GIMP_HANDLE_FILLED_CIRCLE:
367 368
      cairo_move_to (cr, x, y);

369 370 371
      gimp_cairo_add_arc (cr, x, y, (gdouble) private->width / 2.0,
                          private->start_angle,
                          private->slice_angle);
372

373
      _gimp_canvas_item_fill (item, cr);
374 375
      break;

376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
    case GIMP_HANDLE_CROSSHAIR:
      cairo_move_to (cr, x - private->width / 2, y);
      cairo_line_to (cr, x - private->width * 0.4, y);

      cairo_move_to (cr, x + private->width / 2 - 0.5, y);
      cairo_line_to (cr, x + private->width * 0.4, y);

      cairo_move_to (cr, x, y - private->height / 2);
      cairo_line_to (cr, x, y - private->height * 0.4 - 0.5);

      cairo_move_to (cr, x, y + private->height / 2 - 0.5);
      cairo_line_to (cr, x, y + private->height * 0.4 - 0.5);

       _gimp_canvas_item_stroke (item, cr);
      break;

392 393 394 395 396
    default:
      break;
    }
}

397
static cairo_region_t *
398
gimp_canvas_handle_get_extents (GimpCanvasItem *item)
399
{
400
  GimpCanvasHandlePrivate *private = GET_PRIVATE (item);
401
  cairo_rectangle_int_t    rectangle;
402
  gdouble                  x, y;
403
  gdouble                  w, h;
404

405
  gimp_canvas_handle_transform (item, &x, &y);
406 407 408 409 410

  switch (private->type)
    {
    case GIMP_HANDLE_SQUARE:
    case GIMP_HANDLE_FILLED_SQUARE:
411 412 413 414 415 416
      w = private->width * (sqrt(2) - 1) / 2;
      h = private->height * (sqrt(2) - 1) / 2;
      rectangle.x      = x - 1.5 - w;
      rectangle.y      = y - 1.5 - h;
      rectangle.width  = private->width  + 3.0 + w * 2;
      rectangle.height = private->height + 3.0 + h * 2;
417 418 419 420 421
      break;

    case GIMP_HANDLE_CIRCLE:
    case GIMP_HANDLE_FILLED_CIRCLE:
    case GIMP_HANDLE_CROSS:
422
    case GIMP_HANDLE_CROSSHAIR:
423 424 425 426 427 428
    case GIMP_HANDLE_DIAMOND:
    case GIMP_HANDLE_FILLED_DIAMOND:
      rectangle.x      = x - private->width  / 2 - 2.0;
      rectangle.y      = y - private->height / 2 - 2.0;
      rectangle.width  = private->width  + 4.0;
      rectangle.height = private->height + 4.0;
429 430 431 432 433 434
      break;

    default:
      break;
    }

435
  return cairo_region_create_rectangle (&rectangle);
436 437
}

438
static gboolean
439 440 441
gimp_canvas_handle_hit (GimpCanvasItem *item,
                        gdouble         x,
                        gdouble         y)
442 443 444
{
  GimpCanvasHandlePrivate *private = GET_PRIVATE (item);
  gdouble                  handle_tx, handle_ty;
445
  gdouble                  mx, my, tx, ty, mmx, mmy;
446 447
  gdouble                  diamond_offset_x = 0.0;
  gdouble                  diamond_offset_y = 0.0;
448
  gdouble                  angle            = -private->start_angle;
449

450
  gimp_canvas_handle_transform (item, &handle_tx, &handle_ty);
451

452 453 454
  gimp_canvas_item_transform_xy_f (item,
                                   x, y,
                                   &mx, &my);
455 456 457

  switch (private->type)
    {
458 459 460 461 462
    case GIMP_HANDLE_DIAMOND:
    case GIMP_HANDLE_FILLED_DIAMOND:
      angle -= G_PI / 4.0;
      diamond_offset_x = private->width / 2.0;
      diamond_offset_y = private->height / 2.0;
463 464
    case GIMP_HANDLE_SQUARE:
    case GIMP_HANDLE_FILLED_SQUARE:
465 466 467
      gimp_canvas_item_transform_xy_f (item,
                                       private->x, private->y,
                                       &tx, &ty);
468 469 470 471 472
      mmx = mx - tx; mmy = my - ty;
      mx = cos (angle) * mmx - sin (angle) * mmy + tx + diamond_offset_x;
      my = sin (angle) * mmx + cos (angle) * mmy + ty + diamond_offset_y;
      return mx > handle_tx && mx < handle_tx + private->width &&
             my > handle_ty && my < handle_ty + private->height;
473 474 475 476

    case GIMP_HANDLE_CIRCLE:
    case GIMP_HANDLE_FILLED_CIRCLE:
    case GIMP_HANDLE_CROSS:
477
    case GIMP_HANDLE_CROSSHAIR:
478 479 480 481 482 483 484 485
      {
        gint width = private->width;

        if (width != private->height)
          width = (width + private->height) / 2;

        width /= 2;

486
        return ((SQR (handle_tx - mx) + SQR (handle_ty - my)) < SQR (width));
487 488 489 490 491 492 493 494 495
      }

    default:
      break;
    }

  return FALSE;
}

496
GimpCanvasItem *
497 498
gimp_canvas_handle_new (GimpDisplayShell *shell,
                        GimpHandleType    type,
499
                        GimpHandleAnchor  anchor,
500 501 502 503
                        gdouble           x,
                        gdouble           y,
                        gint              width,
                        gint              height)
504
{
505 506
  g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);

507
  return g_object_new (GIMP_TYPE_CANVAS_HANDLE,
508
                       "shell",  shell,
509 510 511 512 513 514 515 516
                       "type",   type,
                       "anchor", anchor,
                       "x",      x,
                       "y",      y,
                       "width",  width,
                       "height", height,
                       NULL);
}
517

518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
void
gimp_canvas_handle_get_position (GimpCanvasItem *handle,
                                 gdouble        *x,
                                 gdouble        *y)
{
  g_return_if_fail (GIMP_IS_CANVAS_HANDLE (handle));
  g_return_if_fail (x != NULL);
  g_return_if_fail (y != NULL);

  g_object_get (handle,
                "x", x,
                "y", y,
                NULL);
}

533
void
534 535 536
gimp_canvas_handle_set_position (GimpCanvasItem *handle,
                                 gdouble         x,
                                 gdouble         y)
537 538 539
{
  g_return_if_fail (GIMP_IS_CANVAS_HANDLE (handle));

540
  gimp_canvas_item_begin_change (handle);
541 542 543 544 545 546

  g_object_set (handle,
                "x", x,
                "y", y,
                NULL);

547
  gimp_canvas_item_end_change (handle);
548 549
}

550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
gint
gimp_canvas_handle_calc_size (GimpCanvasItem *item,
                              gdouble         mouse_x,
                              gdouble         mouse_y,
                              gint            normal_size,
                              gint            hover_size)
{
  gdouble x, y;
  gdouble distance;
  gdouble size;
  gint    full_threshold_sq    = SQR (hover_size / 2) * 9;
  gint    partial_threshold_sq = full_threshold_sq * 5;

  g_return_val_if_fail (GIMP_IS_CANVAS_HANDLE (item), normal_size);

  gimp_canvas_handle_get_position (item, &x, &y);
  distance = gimp_canvas_item_transform_distance_square (item,
                                                         mouse_x,
                                                         mouse_y,
                                                         x, y);

  /*  calculate the handle size based on distance from the cursor  */
  size = (1.0 - (distance - full_threshold_sq) /
          (partial_threshold_sq - full_threshold_sq));

  size = CLAMP (size, 0.0, 1.0);

  return (gint) CLAMP ((size * hover_size),
                       normal_size,
                       hover_size);
}

582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
void
gimp_canvas_handle_get_size (GimpCanvasItem *handle,
                             gint           *width,
                             gint           *height)
{
  g_return_if_fail (GIMP_IS_CANVAS_HANDLE (handle));
  g_return_if_fail (width != NULL);
  g_return_if_fail (height != NULL);

  g_object_get (handle,
                "width",  width,
                "height", height,
                NULL);
}

void
gimp_canvas_handle_set_size (GimpCanvasItem *handle,
                             gint            width,
                             gint            height)
{
  g_return_if_fail (GIMP_IS_CANVAS_HANDLE (handle));

  gimp_canvas_item_begin_change (handle);

  g_object_set (handle,
                "width",  width,
                "height", height,
                NULL);

  gimp_canvas_item_end_change (handle);
}

614
void
615 616 617
gimp_canvas_handle_set_angles (GimpCanvasItem *handle,
                               gdouble         start_angle,
                               gdouble         slice_angle)
618 619 620
{
  g_return_if_fail (GIMP_IS_CANVAS_HANDLE (handle));

621
  gimp_canvas_item_begin_change (handle);
622

623 624 625 626
  g_object_set (handle,
                "start-angle", start_angle,
                "slice-angle", slice_angle,
                NULL);
627

628
  gimp_canvas_item_end_change (handle);
629
}