gimpdisplayshell-callbacks.c 57.3 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
Elliot Lee's avatar
Elliot Lee committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * 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 2 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, write to the Free Software
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
17
 */
18

19
#include "config.h"
20

Elliot Lee's avatar
Elliot Lee committed
21
#include <stdlib.h>
22

23
#include <gtk/gtk.h>
24 25
#include <gdk/gdkkeysyms.h>

26
#include "libgimpmath/gimpmath.h"
Michael Natterer's avatar
Michael Natterer committed
27 28 29
#include "libgimpcolor/gimpcolor.h"
#include "libgimpwidgets/gimpwidgets.h"

30
#include "display-types.h"
31
#include "tools/tools-types.h"
32

33 34
#include "config/gimpdisplayconfig.h"

Michael Natterer's avatar
Michael Natterer committed
35
#include "core/gimp.h"
36 37
#include "core/gimpcontext.h"
#include "core/gimpimage.h"
38
#include "core/gimpimage-guides.h"
39
#include "core/gimpimage-sample-points.h"
40
#include "core/gimpimage-quick-mask.h"
41
#include "core/gimplayer.h"
Michael Natterer's avatar
Michael Natterer committed
42
#include "core/gimptoolinfo.h"
43

44
#include "tools/gimpimagemaptool.h"
45
#include "tools/gimpmovetool.h"
46
#include "tools/gimppainttool.h"
47
#include "tools/gimptoolcontrol.h"
48 49
#include "tools/tool_manager.h"

50
#include "widgets/gimpactiongroup.h"
51
#include "widgets/gimpcontrollers.h"
52
#include "widgets/gimpcontrollerkeyboard.h"
53
#include "widgets/gimpcontrollerwheel.h"
54
#include "widgets/gimpcursor.h"
55
#include "widgets/gimpdevices.h"
56 57
#include "widgets/gimpdialogfactory.h"
#include "widgets/gimpuimanager.h"
58
#include "widgets/gimpwidgets-utils.h"
Michael Natterer's avatar
Michael Natterer committed
59

60
#include "gimpcanvas.h"
61
#include "gimpdisplay.h"
62
#include "gimpdisplayoptions.h"
Michael Natterer's avatar
Michael Natterer committed
63
#include "gimpdisplayshell.h"
64
#include "gimpdisplayshell-appearance.h"
65
#include "gimpdisplayshell-autoscroll.h"
66
#include "gimpdisplayshell-callbacks.h"
67
#include "gimpdisplayshell-coords.h"
68
#include "gimpdisplayshell-cursor.h"
69
#include "gimpdisplayshell-draw.h"
70
#include "gimpdisplayshell-layer-select.h"
71
#include "gimpdisplayshell-preview.h"
72 73
#include "gimpdisplayshell-scale.h"
#include "gimpdisplayshell-scroll.h"
74
#include "gimpdisplayshell-selection.h"
75 76
#include "gimpdisplayshell-title.h"
#include "gimpdisplayshell-transform.h"
77
#include "gimpnavigationeditor.h"
78

79
#include "gimp-log.h"
80
#include "gimp-intl.h"
81

82

Michael Natterer's avatar
Michael Natterer committed
83 84
/*  local function prototypes  */

85 86 87 88
static void       gimp_display_shell_vscrollbar_update (GtkAdjustment    *adjustment,
                                                        GimpDisplayShell *shell);
static void       gimp_display_shell_hscrollbar_update (GtkAdjustment    *adjustment,
                                                        GimpDisplayShell *shell);
Michael Natterer's avatar
Michael Natterer committed
89

90
static GdkModifierType
91
                  gimp_display_shell_key_to_state      (gint              key);
92

93
static GdkEvent * gimp_display_shell_compress_motion   (GimpDisplayShell *shell);
94 95


Michael Natterer's avatar
Michael Natterer committed
96
/*  public functions  */
Michael Natterer's avatar
Michael Natterer committed
97

98
gboolean
99 100 101
gimp_display_shell_events (GtkWidget        *widget,
                           GdkEvent         *event,
                           GimpDisplayShell *shell)
102
{
103 104
  Gimp     *gimp;
  gboolean  set_display = FALSE;
105 106

  /*  are we in destruction?  */
107
  if (! shell->display || ! shell->display->shell)
108 109
    return TRUE;

110
  gimp = shell->display->image->gimp;
111

112 113 114
  switch (event->type)
    {
    case GDK_KEY_PRESS:
115
    case GDK_KEY_RELEASE:
116 117
      {
        GdkEventKey *kevent = (GdkEventKey *) event;
118

119 120
        if (gimp->busy)
          return TRUE;
121

122 123 124
        /*  do not process any key events while BUTTON1 is down. We do this
         *  so tools keep the modifier state they were in when BUTTON1 was
         *  pressed and to prevent accelerators from being invoked.
125 126 127
         */
        if (kevent->state & GDK_BUTTON1_MASK)
          {
128 129 130 131 132 133 134 135 136 137
            if (kevent->keyval == GDK_Shift_L   ||
                kevent->keyval == GDK_Shift_R   ||
                kevent->keyval == GDK_Control_L ||
                kevent->keyval == GDK_Control_R ||
                kevent->keyval == GDK_Alt_L     ||
                kevent->keyval == GDK_Alt_R)
              {
                break;
              }

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
            if (event->type == GDK_KEY_PRESS)
              {
                if (kevent->keyval == GDK_space && shell->space_release_pending)
                  {
                    shell->space_pressed         = TRUE;
                    shell->space_release_pending = FALSE;
                  }
              }
            else
              {
                if (kevent->keyval == GDK_space && shell->space_pressed)
                  {
                    shell->space_pressed         = FALSE;
                    shell->space_release_pending = TRUE;
                  }
              }
154

155 156
            return TRUE;
          }
157

158 159 160 161 162 163 164 165 166 167
        switch (kevent->keyval)
          {
          case GDK_Left:      case GDK_Right:
          case GDK_Up:        case GDK_Down:
          case GDK_space:
          case GDK_Tab:
          case GDK_ISO_Left_Tab:
          case GDK_Alt_L:     case GDK_Alt_R:
          case GDK_Shift_L:   case GDK_Shift_R:
          case GDK_Control_L: case GDK_Control_R:
168 169
          case GDK_Return:    case GDK_KP_Enter:
          case GDK_BackSpace: case GDK_Delete:
170
            break;
171

172
          case GDK_Escape:
173 174
            if (event->type == GDK_KEY_PRESS)
              gimp_display_shell_set_fullscreen (shell, FALSE);
175
            break;
176

177 178 179 180 181
          default:
            if (shell->space_pressed)
              return TRUE;
            break;
          }
182

183 184 185
        set_display = TRUE;
        break;
      }
186

Michael Natterer's avatar
Michael Natterer committed
187
    case GDK_BUTTON_PRESS:
188
    case GDK_SCROLL:
189
      set_display = TRUE;
190
      break;
191

192 193 194 195 196 197 198 199 200
    case GDK_FOCUS_CHANGE:
      {
        GdkEventFocus *fevent = (GdkEventFocus *) event;

        if (fevent->in && GIMP_DISPLAY_CONFIG (gimp->config)->activate_on_focus)
          set_display = TRUE;
      }
      break;

201 202
    case GDK_WINDOW_STATE:
      {
203
        GdkEventWindowState *sevent = (GdkEventWindowState *) event;
204 205
        GimpDisplayOptions  *options;
        gboolean             fullscreen;
206
        GimpActionGroup     *group;
207

208
        shell->window_state = sevent->new_window_state;
209

210 211
        if (! (sevent->changed_mask & GDK_WINDOW_STATE_FULLSCREEN))
          break;
212

213 214
        fullscreen = gimp_display_shell_get_fullscreen (shell);

215 216 217
        gtk_widget_set_name (GTK_WIDGET (shell->menubar),
                             fullscreen ? "gimp-menubar-fullscreen" : NULL);

218 219
        options = fullscreen ? shell->fullscreen_options : shell->options;

220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
        gimp_display_shell_set_show_menubar       (shell,
                                                   options->show_menubar);
        gimp_display_shell_set_show_rulers        (shell,
                                                   options->show_rulers);
        gimp_display_shell_set_show_scrollbars    (shell,
                                                   options->show_scrollbars);
        gimp_display_shell_set_show_statusbar     (shell,
                                                   options->show_statusbar);
        gimp_display_shell_set_show_selection     (shell,
                                                   options->show_selection);
        gimp_display_shell_set_show_layer         (shell,
                                                   options->show_layer_boundary);
        gimp_display_shell_set_show_guides        (shell,
                                                   options->show_guides);
        gimp_display_shell_set_show_grid          (shell,
                                                   options->show_grid);
236 237
        gimp_display_shell_set_show_sample_points (shell,
                                                   options->show_sample_points);
238 239 240
        gimp_display_shell_set_padding            (shell,
                                                   options->padding_mode,
                                                   &options->padding_color);
241

242 243 244 245
        group = gimp_ui_manager_get_action_group (shell->menubar_manager,
                                                  "view");
        gimp_action_group_set_action_active (group, "view-fullscreen",
                                             fullscreen);
246

247
        if (shell->display ==
248
            gimp_context_get_display (gimp_get_user_context (gimp)))
249 250 251 252 253 254
          {
            group = gimp_ui_manager_get_action_group (shell->popup_manager,
                                                      "view");
            gimp_action_group_set_action_active (group, "view-fullscreen",
                                                 fullscreen);
          }
255 256 257
      }
      break;

258 259
    default:
      break;
260 261
    }

262 263
  if (set_display)
    {
264
      Gimp *gimp = shell->display->image->gimp;
Sven Neumann's avatar
Sven Neumann committed
265

266
      /*  Setting the context's display automatically sets the image, too  */
267
      gimp_context_set_display (gimp_get_user_context (gimp), shell->display);
268 269
    }

270 271 272
  return FALSE;
}

273 274 275 276
void
gimp_display_shell_canvas_realize (GtkWidget        *canvas,
                                   GimpDisplayShell *shell)
{
277
  GimpDisplayConfig     *config;
278
  GimpDisplay           *display;
279 280
  GimpCanvasPaddingMode  padding_mode;
  GimpRGB                padding_color;
281

282 283
  display = shell->display;
  config  = GIMP_DISPLAY_CONFIG (display->image->gimp->config);
284

285 286
  gtk_widget_grab_focus (shell->canvas);

287 288
  gimp_display_shell_get_padding (shell, &padding_mode, &padding_color);
  gimp_display_shell_set_padding (shell, padding_mode, &padding_color);
Michael Natterer's avatar
Michael Natterer committed
289

290
  gimp_display_shell_title_update (shell);
291

292 293 294 295
  shell->disp_width  = canvas->allocation.width;
  shell->disp_height = canvas->allocation.height;

  /*  set up the scrollbar observers  */
296
  g_signal_connect (shell->hsbdata, "value-changed",
297 298
                    G_CALLBACK (gimp_display_shell_hscrollbar_update),
                    shell);
299
  g_signal_connect (shell->vsbdata, "value-changed",
300 301 302
                    G_CALLBACK (gimp_display_shell_vscrollbar_update),
                    shell);

303
  /*  set the initial cursor  */
304 305 306 307
  gimp_display_shell_set_cursor (shell,
                                 GDK_TOP_LEFT_ARROW,
                                 GIMP_TOOL_CURSOR_NONE,
                                 GIMP_CURSOR_MODIFIER_NONE);
308 309 310

  /*  allow shrinking  */
  gtk_widget_set_size_request (GTK_WIDGET (shell), 0, 0);
311 312

  gimp_display_shell_draw_vectors (shell);
313 314
}

315 316 317 318
void
gimp_display_shell_canvas_size_allocate (GtkWidget        *widget,
                                         GtkAllocation    *allocation,
                                         GimpDisplayShell *shell)
319
{
320
  /*  are we in destruction?  */
321
  if (! shell->display || ! shell->display->shell)
322
    return;
323

324 325
  if ((shell->disp_width  != allocation->width) ||
      (shell->disp_height != allocation->height))
Michael Natterer's avatar
Michael Natterer committed
326
    {
327 328 329 330 331
      if (shell->zoom_on_resize   &&
          shell->disp_width  > 64 &&
          shell->disp_height > 64 &&
          allocation->width  > 64 &&
          allocation->height > 64)
332
        {
333
          gdouble scale = gimp_zoom_model_get_factor (shell->zoom);
334 335 336 337 338 339
          gint    offset_x;
          gint    offset_y;

          /*  multiply the zoom_factor with the ratio of the new and
           *  old canvas diagonals
           */
340 341
          scale *= (sqrt (SQR (allocation->width) +
                          SQR (allocation->height)) /
342 343 344 345 346 347
                    sqrt (SQR (shell->disp_width) +
                          SQR (shell->disp_height)));

          offset_x = UNSCALEX (shell, shell->offset_x);
          offset_y = UNSCALEX (shell, shell->offset_y);

348 349
          gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, scale);

350 351 352 353
          shell->offset_x = SCALEX (shell, offset_x);
          shell->offset_y = SCALEY (shell, offset_y);
        }

354 355
      shell->disp_width  = allocation->width;
      shell->disp_height = allocation->height;
356

Michael Natterer's avatar
Michael Natterer committed
357 358
      gimp_display_shell_scroll_clamp_offsets (shell);
      gimp_display_shell_scale_setup (shell);
359
      gimp_display_shell_scaled (shell);
Michael Natterer's avatar
Michael Natterer committed
360
    }
361 362
}

Michael Natterer's avatar
Michael Natterer committed
363 364 365 366
gboolean
gimp_display_shell_canvas_expose (GtkWidget        *widget,
                                  GdkEventExpose   *eevent,
                                  GimpDisplayShell *shell)
367
{
368
  GdkRegion    *region = NULL;
369 370 371
  GdkRectangle *rects;
  gint          n_rects;
  gint          i;
372

373
  /*  are we in destruction?  */
374
  if (! shell->display || ! shell->display->shell)
375 376
    return TRUE;

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
  /*  If the call to gimp_display_shell_pause() would cause a redraw,
   *  we need to make sure that no XOR drawing happens on areas that
   *  have already been cleared by the windowing system.
   */
  if (shell->paused_count == 0)
    {
      GdkRectangle  area;

      area.x      = 0;
      area.y      = 0;
      area.width  = shell->disp_width;
      area.height = shell->disp_height;

      region = gdk_region_rectangle (&area);

      gdk_region_subtract (region, eevent->region);

      gimp_canvas_set_clip_region (GIMP_CANVAS (shell->canvas),
                                   GIMP_CANVAS_STYLE_XOR, region);
      gimp_canvas_set_clip_region (GIMP_CANVAS (shell->canvas),
                                   GIMP_CANVAS_STYLE_XOR_DASHED, region);
    }

400
  gimp_display_shell_pause (shell);
401

402 403 404 405 406 407 408 409 410
  if (region)
    {
      gimp_canvas_set_clip_region (GIMP_CANVAS (shell->canvas),
                                   GIMP_CANVAS_STYLE_XOR, NULL);
      gimp_canvas_set_clip_region (GIMP_CANVAS (shell->canvas),
                                   GIMP_CANVAS_STYLE_XOR_DASHED, NULL);
      gdk_region_destroy (region);
    }

411
  gdk_region_get_rectangles (eevent->region, &rects, &n_rects);
Michael Natterer's avatar
Michael Natterer committed
412

413 414 415 416 417 418 419 420 421
  for (i = 0; i < n_rects; i++)
    gimp_display_shell_draw_area (shell,
                                  rects[i].x,
                                  rects[i].y,
                                  rects[i].width,
                                  rects[i].height);

  g_free (rects);

422 423
  /* draw the transform tool preview */
  gimp_display_shell_preview_transform (shell);
424

425
  /* draw the grid */
426
  gimp_display_shell_draw_grid (shell, &eevent->area);
427

Sven Neumann's avatar
Sven Neumann committed
428 429 430
  /* draw the guides */
  gimp_display_shell_draw_guides (shell);

431 432 433
  /* draw the sample points */
  gimp_display_shell_draw_sample_points (shell);

434
  /* and the cursor (if we have a software cursor) */
435
  gimp_display_shell_draw_cursor (shell);
436 437

  /* restart (and recalculate) the selection boundaries */
Sven Neumann's avatar
Sven Neumann committed
438
  gimp_display_shell_selection_control (shell, GIMP_SELECTION_ON);
439

440
  gimp_display_shell_resume (shell);
441

Michael Natterer's avatar
Michael Natterer committed
442 443
  return TRUE;
}
444

Michael Natterer's avatar
Michael Natterer committed
445 446
static void
gimp_display_shell_check_device_cursor (GimpDisplayShell *shell)
447
{
448
  GdkDevice *current_device;
449

450
  current_device = gimp_devices_get_current (shell->display->image->gimp);
451

452
  shell->draw_cursor = ! current_device->has_cursor;
453 454
}

455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
static void
gimp_display_shell_start_scrolling (GimpDisplayShell *shell,
                                    gint              x,
                                    gint              y)
{
  g_return_if_fail (! shell->scrolling);

  shell->scrolling      = TRUE;
  shell->scroll_start_x = x + shell->offset_x;
  shell->scroll_start_y = y + shell->offset_y;

  gimp_display_shell_set_override_cursor (shell, GDK_FLEUR);

  gtk_grab_add (shell->canvas);
}

static void
gimp_display_shell_stop_scrolling (GimpDisplayShell *shell)
{
  g_return_if_fail (shell->scrolling);

  shell->scrolling      = FALSE;
  shell->scroll_start_x = 0;
  shell->scroll_start_y = 0;

  gimp_display_shell_unset_override_cursor (shell);

  gtk_grab_remove (shell->canvas);
}

485 486 487 488 489
static void
gimp_display_shell_space_pressed (GimpDisplayShell *shell,
                                  GdkModifierType   state,
                                  guint32           time)
{
490 491 492 493 494 495
  Gimp *gimp = shell->display->image->gimp;

  if (shell->space_pressed)
    return;

  switch (GIMP_DISPLAY_CONFIG (gimp->config)->space_bar_action)
496
    {
497
    case GIMP_SPACE_BAR_ACTION_NONE:
498
      return;
499

500 501
    case GIMP_SPACE_BAR_ACTION_PAN:
      {
502
        GimpCoords coords;
503

504 505 506
        gimp_display_shell_get_device_coords (shell,
                                              gimp_devices_get_current (gimp),
                                              &coords);
507

508
        gimp_display_shell_start_scrolling (shell, coords.x, coords.y);
509

510 511 512 513
        gdk_pointer_grab (shell->canvas->window, FALSE,
                          GDK_POINTER_MOTION_MASK |
                          GDK_POINTER_MOTION_HINT_MASK,
                          NULL, NULL, time);
514 515
      }
      break;
516

517 518 519
    case GIMP_SPACE_BAR_ACTION_MOVE:
      {
        GimpTool *active_tool = tool_manager_get_active (gimp);
520

521 522
        if (! active_tool || GIMP_IS_MOVE_TOOL (active_tool))
          return;
523

524 525
        shell->space_shaded_tool =
          gimp_object_get_name (GIMP_OBJECT (active_tool->tool_info));
526

527 528
        gimp_context_set_tool (gimp_get_user_context (gimp),
                               gimp_get_tool_info (gimp, "gimp-move-tool"));
529

530 531
        tool_manager_focus_display_active (gimp, shell->display);
        tool_manager_modifier_state_active (gimp, state, shell->display);
532 533
      }
      break;
534
    }
535 536 537 538

  gdk_keyboard_grab (shell->canvas->window, FALSE, time);

  shell->space_pressed = TRUE;
539
}
540 541 542 543 544 545

static void
gimp_display_shell_space_released (GimpDisplayShell *shell,
                                   GdkModifierType   state,
                                   guint32           time)
{
546 547
  Gimp *gimp = shell->display->image->gimp;

548
  if (! shell->space_pressed && ! shell->space_release_pending)
549 550 551
    return;

  switch (GIMP_DISPLAY_CONFIG (gimp->config)->space_bar_action)
552
    {
553
    case GIMP_SPACE_BAR_ACTION_NONE:
554
      break;
555

556
    case GIMP_SPACE_BAR_ACTION_PAN:
557 558 559
      gimp_display_shell_stop_scrolling (shell);
      gdk_display_pointer_ungrab (gtk_widget_get_display (shell->canvas), time);
      break;
560 561

    case GIMP_SPACE_BAR_ACTION_MOVE:
562 563 564 565 566
      gimp_context_set_tool (gimp_get_user_context (gimp),
                             gimp_get_tool_info (gimp,
                                                 shell->space_shaded_tool));
      shell->space_shaded_tool = NULL;

567 568
      tool_manager_focus_display_active (gimp, shell->display);
      tool_manager_modifier_state_active (gimp, state, shell->display);
569 570
      break;
    }
571

572
  gdk_display_keyboard_ungrab (gtk_widget_get_display (shell->canvas), time);
573

574 575
  shell->space_pressed         = FALSE;
  shell->space_release_pending = FALSE;
576 577
}

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
static void
gimp_display_shell_update_focus (GimpDisplayShell *shell,
                                 GimpCoords       *image_coords,
                                 GdkModifierType   state)
{
  Gimp *gimp = shell->display->image->gimp;

  tool_manager_focus_display_active (gimp, shell->display);
  tool_manager_modifier_state_active (gimp, state, shell->display);

  if (image_coords)
    tool_manager_oper_update_active (gimp,
                                     image_coords, state,
                                     shell->proximity,
                                     shell->display);
}

595
gboolean
Michael Natterer's avatar
Michael Natterer committed
596 597 598
gimp_display_shell_canvas_tool_events (GtkWidget        *canvas,
                                       GdkEvent         *event,
                                       GimpDisplayShell *shell)
Elliot Lee's avatar
Elliot Lee committed
599
{
600
  GimpDisplay         *display;
601
  GimpImage           *image;
602 603 604 605 606 607 608
  Gimp                *gimp;
  GdkDisplay          *gdk_display;
  GimpTool            *active_tool;
  GimpCoords           display_coords;
  GimpCoords           image_coords;
  GdkModifierType      state;
  guint32              time;
609 610
  gboolean             return_val       = FALSE;
  gboolean             update_sw_cursor = FALSE;
611

612
  g_return_val_if_fail (GTK_WIDGET_REALIZED (canvas), FALSE);
scott's avatar
scott committed
613

614
  /*  are we in destruction?  */
615
  if (! shell->display || ! shell->display->shell)
616 617
    return TRUE;

618 619 620 621
  /*  set the active display before doing any other canvas event processing  */
  if (gimp_display_shell_events (canvas, event, shell))
    return TRUE;

622 623 624
  display = shell->display;
  image   = display->image;
  gimp    = image->gimp;
625

626 627
  gdk_display = gtk_widget_get_display (canvas);

Michael Natterer's avatar
Michael Natterer committed
628
  /*  Find out what device the event occurred upon  */
629
  if (! gimp->busy && gimp_devices_check_change (gimp, event))
630 631 632
    {
      gimp_display_shell_check_device_cursor (shell);
    }
Michael Natterer's avatar
Michael Natterer committed
633

634 635 636 637 638 639
  gimp_display_shell_get_event_coords (shell, event,
                                       gimp_devices_get_current (gimp),
                                       &display_coords);
  gimp_display_shell_get_event_state (shell, event,
                                      gimp_devices_get_current (gimp),
                                      &state);
640 641 642
  time = gdk_event_get_time (event);

  /*  GimpCoords passed to tools are ALWAYS in image coordinates  */
643 644 645
  gimp_display_shell_untransform_coordinate (shell,
                                             &display_coords,
                                             &image_coords);
646

647 648
  active_tool = tool_manager_get_active (gimp);

649
  if (active_tool && gimp_tool_control_get_snap_to (active_tool->control))
650 651 652
    {
      gint x, y, width, height;

653 654
      gimp_tool_control_get_snap_offsets (active_tool->control,
                                          &x, &y, &width, &height);
655 656 657 658 659 660

      if (gimp_display_shell_snap_coords (shell,
                                          &image_coords,
                                          &image_coords,
                                          x, y, width, height))
        {
Michael Natterer's avatar
Michael Natterer committed
661
          update_sw_cursor = TRUE;
662 663
        }
    }
664

Elliot Lee's avatar
Elliot Lee committed
665 666
  switch (event->type)
    {
667
    case GDK_ENTER_NOTIFY:
668
      {
669
        GdkEventCrossing *cevent = (GdkEventCrossing *) event;
670

671
        GIMP_LOG (TOOLS, "event (display %p): ENTER_NOTIFY", display);
672

673 674
        if (cevent->mode != GDK_CROSSING_NORMAL)
          return TRUE;
675

Michael Natterer's avatar
Michael Natterer committed
676
        update_sw_cursor = TRUE;
677 678 679

        tool_manager_oper_update_active (gimp,
                                         &image_coords, state,
680
                                         shell->proximity,
681
                                         display);
682
      }
683 684
      break;

685
    case GDK_LEAVE_NOTIFY:
686
      {
687
        GdkEventCrossing *cevent = (GdkEventCrossing *) event;
688

689
        GIMP_LOG (TOOLS, "event (display %p): LEAVE_NOTIFY", display);
690

691 692 693 694
        if (cevent->mode != GDK_CROSSING_NORMAL)
          return TRUE;

        shell->proximity = FALSE;
695
        gimp_display_shell_clear_cursor (shell);
696 697 698

        tool_manager_oper_update_active (gimp,
                                         &image_coords, state,
699
                                         shell->proximity,
700
                                         display);
701
      }
702 703 704
      break;

    case GDK_PROXIMITY_IN:
705
      GIMP_LOG (TOOLS, "event (display %p): PROXIMITY_IN", display);
706

707 708
      tool_manager_oper_update_active (gimp,
                                       &image_coords, state,
709
                                       shell->proximity,
710
                                       display);
711 712 713
      break;

    case GDK_PROXIMITY_OUT:
714
      GIMP_LOG (TOOLS, "event (display %p): PROXIMITY_OUT", display);
715

716
      shell->proximity = FALSE;
717
      gimp_display_shell_clear_cursor (shell);
718 719 720

      tool_manager_oper_update_active (gimp,
                                       &image_coords, state,
721
                                       shell->proximity,
722
                                       display);
723 724
      break;

Michael Natterer's avatar
Michael Natterer committed
725 726
    case GDK_FOCUS_CHANGE:
      {
727
        GdkEventFocus *fevent = (GdkEventFocus *) event;
Michael Natterer's avatar
Michael Natterer committed
728 729 730

        if (fevent->in)
          {
731
            GIMP_LOG (TOOLS, "event (display %p): FOCUS_IN", display);
732

Michael Natterer's avatar
Michael Natterer committed
733 734 735 736 737 738 739
            GTK_WIDGET_SET_FLAGS (canvas, GTK_HAS_FOCUS);

            /*  press modifier keys when the canvas gets the focus
             *
             *  in "click to focus" mode, we did this on BUTTON_PRESS, so
             *  do it here only if button_press_before_focus is FALSE
             */
740
            if (! shell->button_press_before_focus)
Michael Natterer's avatar
Michael Natterer committed
741
              {
742
                gimp_display_shell_update_focus (shell, &image_coords, state);
Michael Natterer's avatar
Michael Natterer committed
743 744 745 746
              }
          }
        else
          {
747
            GIMP_LOG (TOOLS, "event (display %p): FOCUS_OUT", display);
748

Michael Natterer's avatar
Michael Natterer committed
749 750 751 752 753
            GTK_WIDGET_UNSET_FLAGS (canvas, GTK_HAS_FOCUS);

            /*  reset it here to be prepared for the next
             *  FOCUS_IN / BUTTON_PRESS confusion
             */
754
            shell->button_press_before_focus = FALSE;
Michael Natterer's avatar
Michael Natterer committed
755 756

            /*  release modifier keys when the canvas loses the focus  */
757
            tool_manager_focus_display_active (gimp, NULL);
Michael Natterer's avatar
Michael Natterer committed
758

759 760
            tool_manager_oper_update_active (gimp,
                                             &image_coords, 0,
761
                                             shell->proximity,
762
                                             display);
Michael Natterer's avatar
Michael Natterer committed
763 764 765 766 767 768 769 770 771
          }

        /*  stop the signal because otherwise gtk+ exposes the whole
         *  canvas to get the non-existant focus indicator drawn
         */
        return_val = TRUE;
      }
      break;

Elliot Lee's avatar
Elliot Lee committed
772
    case GDK_BUTTON_PRESS:
773
      {
774
        GdkEventButton *bevent = (GdkEventButton *) event;
775
        GdkEventMask    event_mask;
776

777
        GIMP_LOG (TOOLS, "event (display %p): BUTTON_PRESS", display);
778

779
        if (! GTK_WIDGET_HAS_FOCUS (canvas))
Michael Natterer's avatar
Michael Natterer committed
780
          {
781 782 783
            /*  in "click to focus" mode, the BUTTON_PRESS arrives before
             *  FOCUS_IN, so we have to update the tool's modifier state here
             */
784
            gimp_display_shell_update_focus (shell, &image_coords, state);
Michael Natterer's avatar
Michael Natterer committed
785

786 787 788 789
            active_tool = tool_manager_get_active (gimp);

            if (active_tool)
              {
790
                if ((! gimp_image_is_empty (image) ||
791
                     gimp_tool_control_get_handle_empty_image (active_tool->control)) &&
792 793 794 795 796 797
                    (bevent->button == 1 ||
                     bevent->button == 2 ||
                     bevent->button == 3))
                  {
                    tool_manager_cursor_update_active (gimp,
                                                       &image_coords, state,
798
                                                       display);
799
                  }
800
                else if (gimp_image_is_empty (image) &&
801
                         ! gimp_tool_control_get_handle_empty_image (active_tool->control))
802 803
                  {
                    gimp_display_shell_set_cursor (shell,
804
                                                   GIMP_CURSOR_MOUSE,
805
                                                   gimp_tool_control_get_tool_cursor (active_tool->control),
806
                                                   GIMP_CURSOR_MODIFIER_BAD);
807 808 809 810 811
                  }
              }
            else
              {
                gimp_display_shell_set_cursor (shell,
812
                                               GIMP_CURSOR_MOUSE,
813
                                               GIMP_TOOL_CURSOR_NONE,
814
                                               GIMP_CURSOR_MODIFIER_BAD);
815
              }
816

817
            shell->button_press_before_focus = TRUE;
818 819 820 821 822

            /*  we expect a FOCUS_IN event to follow, but can't rely
             *  on it, so force one
             */
            gdk_window_focus (canvas->window, time);
Michael Natterer's avatar
Michael Natterer committed
823 824
          }

825
        /*  ignore new mouse events  */
826
        if (gimp->busy || shell->scrolling)
827 828
          return TRUE;

Sven Neumann's avatar