select-commands.c 20.2 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2 3
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7 8 9 10 11 12 13 14
 * (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
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 17 18 19
 */

#include "config.h"

20
#include <gegl.h>
21 22 23 24 25
#include <gtk/gtk.h>

#include "libgimpmath/gimpmath.h"
#include "libgimpwidgets/gimpwidgets.h"

Michael Natterer's avatar
Michael Natterer committed
26
#include "actions-types.h"
27

28 29
#include "config/gimpdialogconfig.h"

30
#include "core/gimp.h"
31
#include "core/gimpchannel.h"
32
#include "core/gimpimage.h"
33
#include "core/gimpselection.h"
34

35
#include "widgets/gimphelp-ids.h"
36
#include "widgets/gimpdialogfactory.h"
37
#include "widgets/gimpwidgets-utils.h"
38
#include "widgets/gimpwindowstrategy.h"
39

40
#include "display/gimpdisplay.h"
41
#include "display/gimpdisplayshell.h"
42

43
#include "dialogs/dialogs.h"
Michael Natterer's avatar
Michael Natterer committed
44

45
#include "actions.h"
46
#include "items-commands.h"
47 48
#include "select-commands.h"

49
#include "gimp-intl.h"
50 51


52
/*  local function prototypes  */
53

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
static void   select_feather_callback (GtkWidget *widget,
                                       gdouble    size,
                                       GimpUnit   unit,
                                       gpointer   data);
static void   select_border_callback  (GtkWidget *widget,
                                       gdouble    size,
                                       GimpUnit   unit,
                                       gpointer   data);
static void   select_grow_callback    (GtkWidget *widget,
                                       gdouble    size,
                                       GimpUnit   unit,
                                       gpointer   data);
static void   select_shrink_callback  (GtkWidget *widget,
                                       gdouble    size,
                                       GimpUnit   unit,
                                       gpointer   data);
70 71


72 73
/*  public functions  */

74
void
75 76
select_all_cmd_callback (GtkAction *action,
                         gpointer   data)
77
{
78 79
  GimpImage *image;
  return_if_no_image (image, data);
80

81
  gimp_channel_all (gimp_image_get_mask (image), TRUE);
82
  gimp_image_flush (image);
83 84 85
}

void
86 87
select_none_cmd_callback (GtkAction *action,
                          gpointer   data)
88
{
89 90
  GimpImage *image;
  return_if_no_image (image, data);
91

92
  gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE);
93
  gimp_image_flush (image);
94 95 96
}

void
97 98
select_invert_cmd_callback (GtkAction *action,
                            gpointer   data)
99
{
100 101
  GimpImage *image;
  return_if_no_image (image, data);
102

103
  gimp_channel_invert (gimp_image_get_mask (image), TRUE);
104
  gimp_image_flush (image);
105 106 107
}

void
108
select_float_cmd_callback (GtkAction *action,
109
                           gpointer   data)
110
{
111
  GimpImage *image;
112
  GtkWidget *widget;
113
  GError    *error = NULL;
114
  return_if_no_image (image, data);
115 116
  return_if_no_widget (widget, data);

117
  if (gimp_selection_float (GIMP_SELECTION (gimp_image_get_mask (image)),
118 119
                            gimp_image_get_active_drawable (image),
                            action_data_get_context (data),
120
                            TRUE, 0, 0, &error))
121 122 123 124 125
    {
      gimp_image_flush (image);
    }
  else
    {
126
      gimp_message_literal (image->gimp,
127 128
                            G_OBJECT (widget), GIMP_MESSAGE_WARNING,
                            error->message);
129
      g_clear_error (&error);
130
    }
131 132 133
}

void
134
select_feather_cmd_callback (GtkAction *action,
135
                             gpointer   data)
136
{
137 138 139
  GimpDisplay *display;
  GimpImage   *image;
  GtkWidget   *dialog;
140
  return_if_no_display (display, data);
141

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
  image = gimp_display_get_image (display);

#define FEATHER_DIALOG_KEY "gimp-selection-feather-dialog"

  dialog = dialogs_get_dialog (G_OBJECT (image), FEATHER_DIALOG_KEY);

  if (! dialog)
    {
      GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
      gdouble           xres;
      gdouble           yres;

      gimp_image_get_resolution (image, &xres, &yres);

      dialog = gimp_query_size_box (_("Feather Selection"),
                                    GTK_WIDGET (gimp_display_get_shell (display)),
                                    gimp_standard_help_func,
                                    GIMP_HELP_SELECTION_FEATHER,
                                    _("Feather selection by"),
                                    config->selection_feather_radius, 0, 32767, 3,
                                    gimp_display_get_shell (display)->unit,
                                    MIN (xres, yres),
                                    FALSE,
                                    G_OBJECT (image), "disconnect",
                                    select_feather_callback, image);

      dialogs_attach_dialog (G_OBJECT (image), FEATHER_DIALOG_KEY, dialog);
    }

  gtk_window_present (GTK_WINDOW (dialog));
172 173 174
}

void
175
select_sharpen_cmd_callback (GtkAction *action,
176
                             gpointer   data)
177
{
178 179
  GimpImage *image;
  return_if_no_image (image, data);
180

181 182
  gimp_channel_sharpen (gimp_image_get_mask (image), TRUE);
  gimp_image_flush (image);
183 184 185
}

void
186
select_shrink_cmd_callback (GtkAction *action,
187
                            gpointer   data)
188
{
189 190 191
  GimpDisplay *display;
  GimpImage   *image;
  GtkWidget   *dialog;
192
  return_if_no_display (display, data);
193

194 195 196 197 198 199 200 201 202 203
  image = gimp_display_get_image (display);

#define SHRINK_DIALOG_KEY "gimp-selection-shrink-dialog"

  dialog = dialogs_get_dialog (G_OBJECT (image), SHRINK_DIALOG_KEY);

  if (! dialog)
    {
      GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
      GtkWidget        *button;
204 205 206
      gint              width;
      gint              height;
      gint              max_value;
207 208 209
      gdouble           xres;
      gdouble           yres;

210 211 212 213
      gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
                        NULL, NULL, &width, &height);
      max_value = MIN (width, height) / 2;

214 215 216 217 218 219 220
      gimp_image_get_resolution (image, &xres, &yres);

      dialog = gimp_query_size_box (_("Shrink Selection"),
                                    GTK_WIDGET (gimp_display_get_shell (display)),
                                    gimp_standard_help_func,
                                    GIMP_HELP_SELECTION_SHRINK,
                                    _("Shrink selection by"),
221 222
                                    config->selection_shrink_radius,
                                    1, max_value, 0,
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
                                    gimp_display_get_shell (display)->unit,
                                    MIN (xres, yres),
                                    FALSE,
                                    G_OBJECT (image), "disconnect",
                                    select_shrink_callback, image);

      /* Edge lock button */
      button = gtk_check_button_new_with_mnemonic (_("_Selected areas continue outside the image"));
      g_object_set_data (G_OBJECT (dialog), "edge-lock-toggle", button);
      gimp_help_set_help_data (button,
                               _("When shrinking, act as if selected areas "
                                 "continued outside the image."),
                               NULL);
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
                                    config->selection_shrink_edge_lock);
      gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), button,
                          FALSE, FALSE, 0);
      gtk_widget_show (button);

      dialogs_attach_dialog (G_OBJECT (image), SHRINK_DIALOG_KEY, dialog);
    }

  gtk_window_present (GTK_WINDOW (dialog));
246 247 248
}

void
249
select_grow_cmd_callback (GtkAction *action,
250
                          gpointer   data)
251
{
252 253 254
  GimpDisplay *display;
  GimpImage   *image;
  GtkWidget   *dialog;
255
  return_if_no_display (display, data);
256

257 258 259 260 261 262 263 264 265
  image = gimp_display_get_image (display);

#define GROW_DIALOG_KEY "gimp-selection-grow-dialog"

  dialog = dialogs_get_dialog (G_OBJECT (image), GROW_DIALOG_KEY);

  if (! dialog)
    {
      GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
266 267 268
      gint              width;
      gint              height;
      gint              max_value;
269 270 271
      gdouble           xres;
      gdouble           yres;

272 273 274 275
      width  = gimp_image_get_width  (image);
      height = gimp_image_get_height (image);
      max_value = MAX (width, height);

276 277 278 279 280 281 282
      gimp_image_get_resolution (image, &xres, &yres);

      dialog = gimp_query_size_box (_("Grow Selection"),
                                    GTK_WIDGET (gimp_display_get_shell (display)),
                                    gimp_standard_help_func,
                                    GIMP_HELP_SELECTION_GROW,
                                    _("Grow selection by"),
283 284
                                    config->selection_grow_radius,
                                    1, max_value, 0,
285 286 287 288 289 290 291 292 293 294
                                    gimp_display_get_shell (display)->unit,
                                    MIN (xres, yres),
                                    FALSE,
                                    G_OBJECT (image), "disconnect",
                                    select_grow_callback, image);

      dialogs_attach_dialog (G_OBJECT (image), GROW_DIALOG_KEY, dialog);
    }

  gtk_window_present (GTK_WINDOW (dialog));
295 296 297
}

void
298
select_border_cmd_callback (GtkAction *action,
299
                            gpointer   data)
300
{
301 302 303
  GimpDisplay *display;
  GimpImage   *image;
  GtkWidget   *dialog;
304
  return_if_no_display (display, data);
305

306 307 308 309 310 311 312 313 314 315 316
  image = gimp_display_get_image (display);

#define BORDER_DIALOG_KEY "gimp-selection-border-dialog"

  dialog = dialogs_get_dialog (G_OBJECT (image), BORDER_DIALOG_KEY);

  if (! dialog)
    {
      GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
      GtkWidget        *combo;
      GtkWidget        *button;
317 318 319
      gint              width;
      gint              height;
      gint              max_value;
320 321 322
      gdouble           xres;
      gdouble           yres;

323 324 325 326
      gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
                        NULL, NULL, &width, &height);
      max_value = MIN (width, height) / 2;

327 328 329 330 331 332 333
      gimp_image_get_resolution (image, &xres, &yres);

      dialog = gimp_query_size_box (_("Border Selection"),
                                    GTK_WIDGET (gimp_display_get_shell (display)),
                                    gimp_standard_help_func,
                                    GIMP_HELP_SELECTION_BORDER,
                                    _("Border selection by"),
334 335
                                    config->selection_border_radius,
                                    1, max_value, 0,
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
                                    gimp_display_get_shell (display)->unit,
                                    MIN (xres, yres),
                                    FALSE,
                                    G_OBJECT (image), "disconnect",
                                    select_border_callback, image);

      /* Border style combo */
      combo = gimp_enum_combo_box_new (GIMP_TYPE_CHANNEL_BORDER_STYLE);
      gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (combo),
                                    _("Border style"));

      gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), combo,
                          FALSE, FALSE, 0);

      g_object_set_data (G_OBJECT (dialog), "border-style-combo", combo);
      gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
                                     config->selection_border_style);
      gtk_widget_show (combo);

      /* Edge lock button */
      button = gtk_check_button_new_with_mnemonic (_("_Selected areas continue outside the image"));
      g_object_set_data (G_OBJECT (dialog), "edge-lock-toggle", button);
      gimp_help_set_help_data (button,
                               _("When bordering, act as if selected areas "
                                 "continued outside the image."),
                               NULL);
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
                                    config->selection_border_edge_lock);
      gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), button,
                          FALSE, FALSE, 0);
      gtk_widget_show (button);

      dialogs_attach_dialog (G_OBJECT (image), BORDER_DIALOG_KEY, dialog);
    }

  gtk_window_present (GTK_WINDOW (dialog));
372 373
}

Ell's avatar
Ell committed
374 375 376 377 378 379 380 381 382 383 384
void
select_flood_cmd_callback (GtkAction *action,
                           gpointer   data)
{
  GimpImage *image;
  return_if_no_image (image, data);

  gimp_channel_flood (gimp_image_get_mask (image), TRUE);
  gimp_image_flush (image);
}

385
void
386
select_save_cmd_callback (GtkAction *action,
387
                          gpointer   data)
388
{
389
  GimpImage *image;
390
  GtkWidget *widget;
391
  return_if_no_image (image, data);
392
  return_if_no_widget (widget, data);
393

394
  gimp_selection_save (GIMP_SELECTION (gimp_image_get_mask (image)));
395
  gimp_image_flush (image);
396

397 398 399 400
  gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (image->gimp)),
                                             image->gimp,
                                             gimp_dialog_factory_get_singleton (),
                                             gtk_widget_get_screen (widget),
401
                                             gimp_widget_get_monitor (widget),
402
                                             "gimp-channel-list");
403 404
}

405 406 407 408
void
select_fill_cmd_callback (GtkAction *action,
                          gpointer   data)
{
409
  GimpImage *image;
410 411
  return_if_no_image (image, data);

412 413 414 415 416 417 418
  items_fill_cmd_callback (action,
                           image, GIMP_ITEM (gimp_image_get_mask (image)),
                           "gimp-selection-fill-dialog",
                           _("Fill Selection Outline"),
                           GIMP_STOCK_TOOL_BUCKET_FILL,
                           GIMP_HELP_SELECTION_FILL,
                           data);
419 420 421 422 423 424
}

void
select_fill_last_vals_cmd_callback (GtkAction *action,
                                    gpointer   data)
{
425
  GimpImage *image;
426 427
  return_if_no_image (image, data);

428 429 430 431
  items_fill_last_vals_cmd_callback (action,
                                     image,
                                     GIMP_ITEM (gimp_image_get_mask (image)),
                                     data);
432 433
}

434 435 436 437
void
select_stroke_cmd_callback (GtkAction *action,
                            gpointer   data)
{
438
  GimpImage *image;
439
  return_if_no_image (image, data);
440

441 442 443 444 445 446 447
  items_stroke_cmd_callback (action,
                             image, GIMP_ITEM (gimp_image_get_mask (image)),
                             "gimp-selection-stroke-dialog",
                             _("Stroke Selection"),
                             GIMP_STOCK_SELECTION_STROKE,
                             GIMP_HELP_SELECTION_STROKE,
                             data);
448 449
}

450 451 452 453
void
select_stroke_last_vals_cmd_callback (GtkAction *action,
                                      gpointer   data)
{
454
  GimpImage *image;
455 456
  return_if_no_image (image, data);

457 458 459 460
  items_stroke_last_vals_cmd_callback (action,
                                       image,
                                       GIMP_ITEM (gimp_image_get_mask (image)),
                                       data);
461 462
}

463 464 465 466

/*  private functions  */

static void
467 468 469 470
select_feather_callback (GtkWidget *widget,
                         gdouble    size,
                         GimpUnit   unit,
                         gpointer   data)
471
{
472 473 474 475
  GimpImage        *image  = GIMP_IMAGE (data);
  GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
  gdouble           radius_x;
  gdouble           radius_y;
476

477
  g_object_set (config,
Jehan's avatar
Jehan committed
478 479
                "selection-feather-radius", size,
                NULL);
480 481 482

  radius_x = config->selection_feather_radius;
  radius_y = config->selection_feather_radius;
483 484 485

  if (unit != GIMP_UNIT_PIXEL)
    {
486 487
      gdouble xres;
      gdouble yres;
488 489
      gdouble factor;

490
      gimp_image_get_resolution (image, &xres, &yres);
491

492 493 494 495
      factor = (MAX (xres, yres) /
                MIN (xres, yres));

      if (xres == MIN (xres, yres))
496
        radius_y *= factor;
497
      else
498
        radius_x *= factor;
499 500
    }

501 502
  gimp_channel_feather (gimp_image_get_mask (image), radius_x, radius_y, TRUE);
  gimp_image_flush (image);
503 504 505
}

static void
506 507 508 509
select_border_callback (GtkWidget *widget,
                        gdouble    size,
                        GimpUnit   unit,
                        gpointer   data)
510
{
511 512 513 514 515 516 517 518 519 520
  GimpImage        *image  = GIMP_IMAGE (data);
  GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
  GtkWidget        *combo;
  GtkWidget        *button;
  gdouble           radius_x;
  gdouble           radius_y;
  gint              border_style;

  combo  = g_object_get_data (G_OBJECT (widget), "border-style-combo");
  button = g_object_get_data (G_OBJECT (widget), "edge-lock-toggle");
521

522
  gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &border_style);
523

524
  g_object_set (config,
Jehan's avatar
Jehan committed
525 526 527 528 529
                "selection-border-radius", size,
                "selection-border-style",  border_style,
                "selection-border-edge-lock",
                gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)),
                NULL);
530

531 532
  radius_x = ROUND (config->selection_border_radius);
  radius_y = ROUND (config->selection_border_radius);
533

534 535
  if (unit != GIMP_UNIT_PIXEL)
    {
536 537
      gdouble xres;
      gdouble yres;
538 539
      gdouble factor;

540 541 542 543
      gimp_image_get_resolution (image, &xres, &yres);

      factor = (MAX (xres, yres) /
                MIN (xres, yres));
544

545
      if (xres == MIN (xres, yres))
546
        radius_y *= factor;
547
      else
548
        radius_x *= factor;
549 550
    }

551
  gimp_channel_border (gimp_image_get_mask (image), radius_x, radius_y,
552
                       config->selection_border_style,
Jehan's avatar
Jehan committed
553 554
                       config->selection_border_edge_lock,
                       TRUE);
555
  gimp_image_flush (image);
556 557 558
}

static void
559 560 561 562
select_grow_callback (GtkWidget *widget,
                      gdouble    size,
                      GimpUnit   unit,
                      gpointer   data)
563
{
564 565 566 567
  GimpImage        *image  = GIMP_IMAGE (data);
  GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
  gdouble           radius_x;
  gdouble           radius_y;
568

569
  g_object_set (config,
Jehan's avatar
Jehan committed
570 571
                "selection-grow-radius", size,
                NULL);
572 573 574

  radius_x = ROUND (config->selection_grow_radius);
  radius_y = ROUND (config->selection_grow_radius);
575 576 577

  if (unit != GIMP_UNIT_PIXEL)
    {
578 579
      gdouble xres;
      gdouble yres;
580 581
      gdouble factor;

582
      gimp_image_get_resolution (image, &xres, &yres);
583

584 585 586 587
      factor = (MAX (xres, yres) /
                MIN (xres, yres));

      if (xres == MIN (xres, yres))
588
        radius_y *= factor;
589
      else
590
        radius_x *= factor;
591 592
    }

593 594
  gimp_channel_grow (gimp_image_get_mask (image), radius_x, radius_y, TRUE);
  gimp_image_flush (image);
595 596 597
}

static void
598 599 600 601
select_shrink_callback (GtkWidget *widget,
                        gdouble    size,
                        GimpUnit   unit,
                        gpointer   data)
602
{
603 604 605 606 607 608 609
  GimpImage        *image  = GIMP_IMAGE (data);
  GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
  GtkWidget        *button;
  gint              radius_x;
  gint              radius_y;

  button = g_object_get_data (G_OBJECT (widget), "edge-lock-toggle");
610

611
  g_object_set (config,
Jehan's avatar
Jehan committed
612 613 614 615
                "selection-shrink-radius",  size,
                "selection-shrink-edge-lock",
                gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)),
                NULL);
616

617 618
  radius_x = ROUND (config->selection_shrink_radius);
  radius_y = ROUND (config->selection_shrink_radius);
619 620 621

  if (unit != GIMP_UNIT_PIXEL)
    {
622 623
      gdouble xres;
      gdouble yres;
624 625
      gdouble factor;

626 627 628 629
      gimp_image_get_resolution (image, &xres, &yres);

      factor = (MAX (xres, yres) /
                MIN (xres, yres));
630

631
      if (xres == MIN (xres, yres))
632
        radius_y *= factor;
633
      else
634
        radius_x *= factor;
635 636
    }

637
  gimp_channel_shrink (gimp_image_get_mask (image), radius_x, radius_y,
638
                       config->selection_shrink_edge_lock,
Jehan's avatar
Jehan committed
639
                       TRUE);
640
  gimp_image_flush (image);
641
}