desktop-events.cpp 32.2 KB
Newer Older
1 2 3
/**
 * @file
 * Event handlers for SPDesktop.
4 5
 */
/* Author:
MenTaLguY's avatar
MenTaLguY committed
6
 *   Lauris Kaplinski <lauris@kaplinski.com>
7
 *   Abhishek Sharma
MenTaLguY's avatar
MenTaLguY committed
8 9
 *
 * Copyright (C) 1999-2002 Lauris Kaplinski
10
 * Copyright (C) 1999-2010 Others
MenTaLguY's avatar
MenTaLguY committed
11 12 13 14
 *
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

Peter Moulder's avatar
Peter Moulder committed
15 16 17
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
18

19 20
#include <map>
#include <string>
21 22 23

#include "desktop-events.h"

24
#include <gdkmm/display.h>
25
#include <gtk/gtk.h>
26 27 28 29 30
#if GTK_CHECK_VERSION(3, 20, 0)
# include <gdkmm/seat.h>
#else
# include <gdkmm/devicemanager.h>
#endif
31

32 33
#include <glibmm/i18n.h>

34
#include <2geom/line.h>
35
#include <2geom/angle.h>
36 37

#include "desktop.h"
38 39 40 41 42 43 44
#include "document-undo.h"
#include "document.h"
#include "message-context.h"
#include "preferences.h"
#include "snap.h"
#include "sp-cursor.h"
#include "verbs.h"
45

46 47
#include "display/canvas-axonomgrid.h"
#include "display/canvas-grid.h"
48
#include "display/guideline.h"
49
#include "display/snap-indicator.h"
50
#include "display/sp-canvas.h"
51 52 53 54 55 56 57 58 59 60

#include "helper/action.h"

#include "pixmaps/cursor-select.xpm"

#include "object/sp-guide.h"
#include "object/sp-namedview.h"
#include "object/sp-root.h"

#include "ui/dialog-events.h"
liamwhite's avatar
liamwhite committed
61
#include "ui/tools-switch.h"
62 63 64
#include "ui/dialog/guides.h"
#include "ui/tools/tool-base.h"

65
#include "widgets/desktop-widget.h"
66

Peter Moulder's avatar
Peter Moulder committed
67
#include "xml/repr.h"
MenTaLguY's avatar
MenTaLguY committed
68

69 70
using Inkscape::DocumentUndo;

71 72
static void snoop_extended(GdkEvent* event, SPDesktop *desktop);
static void init_extended();
73
void sp_dt_ruler_snap_new_guide(SPDesktop *desktop, SPCanvasItem *guide, Geom::Point &event_dt, Geom::Point &normal);
74

MenTaLguY's avatar
MenTaLguY committed
75 76
/* Root item handler */

Jon A. Cruz's avatar
Jon A. Cruz committed
77
int sp_desktop_root_handler(SPCanvasItem */*item*/, GdkEvent *event, SPDesktop *desktop)
MenTaLguY's avatar
MenTaLguY committed
78
{
79 80
    static bool watch = false;
    static bool first = true;
81

82
    if ( first ) {
Ted Gould's avatar
Ted Gould committed
83 84 85
        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
        if ( prefs->getBool("/options/useextinput/value", true)
            && prefs->getBool("/options/switchonextinput/value") ) {
86 87 88 89 90 91 92 93 94
            watch = true;
            init_extended();
        }
        first = false;
    }
    if ( watch ) {
        snoop_extended(event, desktop);
    }

95
    return sp_event_context_root_handler(desktop->event_context, event);
MenTaLguY's avatar
MenTaLguY committed
96 97
}

98
static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw, bool horiz)
MenTaLguY's avatar
MenTaLguY committed
99
{
100 101
    static bool clicked = false;
    static bool dragged = false;
102
    static SPCanvasItem *guide = NULL;
103
    static Geom::Point normal;
104
    int wx, wy;
105
    static gint xp = 0, yp = 0; // where drag started
MenTaLguY's avatar
MenTaLguY committed
106

107
    SPDesktop *desktop = dtw->desktop;
108
    GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(dtw->canvas));
MenTaLguY's avatar
MenTaLguY committed
109

110
    gint width, height;
111

112
    auto device = gdk_event_get_device(event);
113
    gdk_window_get_device_position(window, device, &wx, &wy, NULL);
Alex Valavanis's avatar
Alex Valavanis committed
114
    gdk_window_get_geometry(window, NULL /*x*/, NULL /*y*/, &width, &height);
115 116
    
    Geom::Point const event_win(wx, wy);
117

118
    switch (event->type) {
119
    case GDK_BUTTON_PRESS:
120
            if (event->button.button == 1) {
121 122 123 124 125
                clicked = true;
                dragged = false;
                // save click origin
                xp = (gint) event->button.x;
                yp = (gint) event->button.y;
Diederik van Lierop's avatar
Diederik van Lierop committed
126

127 128
                Geom::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win));
                Geom::Point const event_dt(desktop->w2d(event_w));
129

130 131 132 133 134 135
                // calculate the normal of the guidelines when dragged from the edges of rulers.
                Geom::Point normal_bl_to_tr(-1.,1.); //bottomleft to topright
                Geom::Point normal_tr_to_bl(1.,1.); //topright to bottomleft
                normal_bl_to_tr.normalize();
                normal_tr_to_bl.normalize();
                Inkscape::CanvasGrid * grid = sp_namedview_get_first_enabled_grid(desktop->namedview);
136 137 138 139 140 141 142 143 144 145 146
                if (grid){
                    if (grid->getGridType() == Inkscape::GRID_AXONOMETRIC ) {
                        Inkscape::CanvasAxonomGrid *axonomgrid = dynamic_cast<Inkscape::CanvasAxonomGrid *>(grid);
                        if (event->button.state & GDK_CONTROL_MASK) {
                            // guidelines normal to gridlines
                            normal_bl_to_tr = Geom::Point::polar(-axonomgrid->angle_rad[0], 1.0);
                            normal_tr_to_bl = Geom::Point::polar(axonomgrid->angle_rad[2], 1.0);
                        } else {
                            normal_bl_to_tr = rot90(Geom::Point::polar(axonomgrid->angle_rad[2], 1.0));
                            normal_tr_to_bl = rot90(Geom::Point::polar(-axonomgrid->angle_rad[0], 1.0));
                        }
147 148
                    }
                }
149 150
                if (horiz) {
                    if (wx < 50) {
151
                        normal = normal_bl_to_tr;
152
                    } else if (wx > width - 50) {
153
                        normal = normal_tr_to_bl;
154 155 156 157 158
                    } else {
                        normal = Geom::Point(0.,1.);
                    }
                } else {
                    if (wy < 50) {
159
                        normal = normal_bl_to_tr;
160
                    } else if (wy > height - 50) {
161
                        normal = normal_tr_to_bl;
162 163 164 165 166
                    } else {
                        normal = Geom::Point(1.,0.);
                    }
                }

167
                guide = sp_guideline_new(desktop->guides, NULL, event_dt, normal);
168
                sp_guideline_set_color(SP_GUIDELINE(guide), desktop->namedview->guidehicolor);
169

Alex Valavanis's avatar
Alex Valavanis committed
170 171 172 173 174 175 176 177 178 179 180 181 182
                auto window = gtk_widget_get_window(widget);

#if GTK_CHECK_VERSION(3,20,0)
                auto seat = gdk_device_get_seat(device);
                gdk_seat_grab(seat,
                              window,
                              GDK_SEAT_CAPABILITY_ALL_POINTING,
                              FALSE,
                              NULL,
                              event,
                              NULL,
                              NULL);
#else
183
                gdk_device_grab(device,
Alex Valavanis's avatar
Alex Valavanis committed
184
                                window,
185 186 187 188 189
                                GDK_OWNERSHIP_NONE,
                                FALSE,
                                (GdkEventMask)(GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK ),
                                NULL,
                                event->button.time);
Alex Valavanis's avatar
Alex Valavanis committed
190
#endif
191 192
            }
            break;
193
    case GDK_MOTION_NOTIFY:
194
            if (clicked) {
195
                Geom::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win));
196
                Geom::Point event_dt(desktop->w2d(event_w));
197

198 199 200 201 202 203 204 205 206 207 208 209 210 211
                Inkscape::Preferences *prefs = Inkscape::Preferences::get();
                gint tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
                if ( ( abs( (gint) event->motion.x - xp ) < tolerance )
                        && ( abs( (gint) event->motion.y - yp ) < tolerance ) ) {
                    break;
                }

                dragged = true;

                // explicitly show guidelines; if I draw a guide, I want them on
                if ((horiz ? wy : wx) >= 0) {
                    desktop->namedview->setGuides(true);
                }

212
                if (!(event->motion.state & GDK_SHIFT_MASK)) {
213
                    sp_dt_ruler_snap_new_guide(desktop, guide, event_dt, normal);
214
                }
215
                sp_guideline_set_normal(SP_GUIDELINE(guide), normal);
216
                sp_guideline_set_position(SP_GUIDELINE(guide), event_dt);
217

218
                desktop->set_coordinate_status(event_dt);
219 220
            }
            break;
221
    case GDK_BUTTON_RELEASE:
222
            if (clicked && event->button.button == 1) {
223 224
                sp_event_context_discard_delayed_snap_event(desktop->event_context);

Alex Valavanis's avatar
Alex Valavanis committed
225 226 227 228
#if GTK_CHECK_VERSION(3,20,0)
                auto seat = gdk_device_get_seat(device);
                gdk_seat_ungrab(seat);
#else
229
                gdk_device_ungrab(device, event->button.time);
Alex Valavanis's avatar
Alex Valavanis committed
230
#endif
231

232
                Geom::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win));
233
                Geom::Point event_dt(desktop->w2d(event_w));
234

235
                if (!(event->button.state & GDK_SHIFT_MASK)) {
236
                    sp_dt_ruler_snap_new_guide(desktop, guide, event_dt, normal);
237
                }
238

239
                sp_canvas_item_destroy(guide);
240
                guide = NULL;
241
                if ((horiz ? wy : wx) >= 0) {
242
                    Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
243
                    Inkscape::XML::Node *repr = xml_doc->createElement("sodipodi:guide");
244 245 246 247 248 249 250 251 252 253 254

                    // If root viewBox set, interpret guides in terms of viewBox (90/96)
                    double newx = event_dt.x();
                    double newy = event_dt.y();

                    SPRoot *root = desktop->doc()->getRoot();
                    if( root->viewBox_set ) {
                        newx = newx * root->viewBox.width()  / root->width.computed;
                        newy = newy * root->viewBox.height() / root->height.computed;
                    }
                    sp_repr_set_point(repr, "position", Geom::Point( newx, newy ));
255
                    sp_repr_set_point(repr, "orientation", normal);
256
                    desktop->namedview->appendChild(repr);
257
                    Inkscape::GC::release(repr);
258
                    DocumentUndo::done(desktop->getDocument(), SP_VERB_NONE,
Dmitry Kirsanov's avatar
Dmitry Kirsanov committed
259
                                     _("Create guide"));
260
                }
261
                desktop->set_coordinate_status(event_dt);
262 263 264 265

                if (!dragged) {
                    // Ruler click (without drag) toggle the guide visibility on and off
                    Inkscape::XML::Node *repr = desktop->namedview->getRepr();
266
                    sp_namedview_toggle_guides(desktop->getDocument(), repr);
267
                    
268 269 270 271
                }

                clicked = false;
                dragged = false;
272
            }
273
    default:
274 275
            break;
    }
276

277
    return FALSE;
MenTaLguY's avatar
MenTaLguY committed
278 279
}

280
int sp_dt_hruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw)
MenTaLguY's avatar
MenTaLguY committed
281
{
282
    if (event->type == GDK_MOTION_NOTIFY) {
283
        sp_event_context_snap_delay_handler(dtw->desktop->event_context, (gpointer) widget, (gpointer) dtw, (GdkEventMotion *)event, Inkscape::UI::Tools::DelayedSnapEvent::GUIDE_HRULER);
284
    }
285
    return sp_dt_ruler_event(widget, event, dtw, true);
MenTaLguY's avatar
MenTaLguY committed
286 287
}

288
int sp_dt_vruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw)
MenTaLguY's avatar
MenTaLguY committed
289
{
290
    if (event->type == GDK_MOTION_NOTIFY) {
291
        sp_event_context_snap_delay_handler(dtw->desktop->event_context, (gpointer) widget, (gpointer) dtw, (GdkEventMotion *)event, Inkscape::UI::Tools::DelayedSnapEvent::GUIDE_VRULER);
292
    }
293
    return sp_dt_ruler_event(widget, event, dtw, false);
MenTaLguY's avatar
MenTaLguY committed
294 295
}

296
static Geom::Point drag_origin;
297
static SPGuideDragType drag_type = SP_DRAG_NONE;
298 299
//static bool reset_drag_origin = false; // when Ctrl is pressed while dragging, this is used to trigger resetting of the
//                                       // drag origin to that location so that constrained movement is more intuitive
300

301
// Min distance from anchor to initiate rotation, measured in screenpixels
302
#define tol 40.0
303

304
gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data)
MenTaLguY's avatar
MenTaLguY committed
305
{
306
    static bool moved = false;
307
    gint ret = FALSE;
308

309
    SPGuide *guide = SP_GUIDE(data);
310
    SPDesktop *desktop = static_cast<SPDesktop*>(g_object_get_data(G_OBJECT(item->canvas), "SPDesktop"));
MenTaLguY's avatar
MenTaLguY committed
311

312
    switch (event->type) {
313
    case GDK_2BUTTON_PRESS:
314
            if (event->button.button == 1) {
315
                drag_type = SP_DRAG_NONE;
316
                sp_event_context_discard_delayed_snap_event(desktop->event_context);
317
                sp_canvas_item_ungrab(item, event->button.time);
318
                Inkscape::UI::Dialogs::GuidelinePropertiesDialog::showDialog(guide, desktop);
319 320 321
                ret = TRUE;
            }
            break;
322
    case GDK_BUTTON_PRESS:
323
            if (event->button.button == 1) {
324 325
                Geom::Point const event_w(event->button.x, event->button.y);
                Geom::Point const event_dt(desktop->w2d(event_w));
326 327 328 329 330 331

                // Due to the tolerance allowed when grabbing a guide, event_dt will generally
                // be close to the guide but not just exactly on it. The drag origin calculated
                // here must be exactly on the guide line though, otherwise
                // small errors will occur once we snap, see
                // https://bugs.launchpad.net/inkscape/+bug/333762
332
                drag_origin = Geom::projection(event_dt, Geom::Line(guide->getPoint(), guide->angle()));
333

334 335 336
                if (event->button.state & GDK_SHIFT_MASK) {
                    // with shift we rotate the guide
                    drag_type = SP_DRAG_ROTATE;
337
                } else {
338 339 340 341 342 343 344 345
                    if (event->button.state & GDK_CONTROL_MASK) {
                        drag_type = SP_DRAG_MOVE_ORIGIN;
                    } else {
                        drag_type = SP_DRAG_TRANSLATE;
                    }
                }

                if (drag_type == SP_DRAG_ROTATE || drag_type == SP_DRAG_TRANSLATE) {
346 347 348 349 350 351 352
                    sp_canvas_item_grab(item,
                                        ( GDK_BUTTON_RELEASE_MASK  |
                                          GDK_BUTTON_PRESS_MASK    |
                                          GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK ),
                                        NULL,
                                        event->button.time);
                }
353 354 355
                ret = TRUE;
            }
            break;
356 357
        case GDK_MOTION_NOTIFY:
            if (drag_type != SP_DRAG_NONE) {
358
                Geom::Point const motion_w(event->motion.x,
359 360
                                           event->motion.y);
                Geom::Point motion_dt(desktop->w2d(motion_w));
361

362
                sp_event_context_snap_delay_handler(desktop->event_context, (gpointer) item, data, (GdkEventMotion *)event, Inkscape::UI::Tools::DelayedSnapEvent::GUIDE_HANDLER);
363

364
                // This is for snapping while dragging existing guidelines. New guidelines,
365
                // which are dragged off the ruler, are being snapped in sp_dt_ruler_event
366
                SnapManager &m = desktop->namedview->snap_manager;
367
                m.setup(desktop, true, NULL, NULL, guide);
368
                if (drag_type == SP_DRAG_MOVE_ORIGIN) {
369 370 371 372
                    // If we snap in guideConstrainedSnap() below, then motion_dt will
                    // be forced to be on the guide. If we don't snap however, then
                    // the origin should still be constrained to the guide. So let's do
                    // that explicitly first:
373
                    Geom::Line line(guide->getPoint(), guide->angle());
374
                    Geom::Coord t = line.nearestTime(motion_dt);
375
                    motion_dt = line.pointAt(t);
376 377 378
                    if (!(event->motion.state & GDK_SHIFT_MASK)) {
                        m.guideConstrainedSnap(motion_dt, *guide);
                    }
379
                } else if (!((drag_type == SP_DRAG_ROTATE) && (event->motion.state & GDK_CONTROL_MASK))) {
380
                    // cannot use shift here to disable snapping, because we already use it for rotating the guide
381
                    Geom::Point temp;
382
                    if (drag_type == SP_DRAG_ROTATE) {
383 384 385
                        temp = guide->getPoint();
                        m.guideFreeSnap(motion_dt, temp, true, false);
                        guide->moveto(temp, false);
386
                    } else {
387 388 389
                        temp = guide->getNormal();
                        m.guideFreeSnap(motion_dt, temp, false, true);
                        guide->set_normal(temp, false);
390
                    }
391
                }
392
                m.unSetup();
393

394 395 396
                switch (drag_type) {
                    case SP_DRAG_TRANSLATE:
                    {
397
                        guide->moveto(motion_dt, false);
398 399 400 401
                        break;
                    }
                    case SP_DRAG_ROTATE:
                    {
402
                        Geom::Point pt = motion_dt - guide->getPoint();
403
                        Geom::Angle angle(pt);
404
                        if (event->motion.state & GDK_CONTROL_MASK) {
405 406
                            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
                            unsigned const snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12));
407
                            bool const relative_snaps = prefs->getBool("/options/relativeguiderotationsnap/value", false);
408
                            if (snaps) {
409
                                if (relative_snaps) {
410
                                    Geom::Angle orig_angle(guide->getNormal());
411 412 413 414 415 416 417
                                    Geom::Angle snap_angle = angle - orig_angle;
                                    double sections = floor(snap_angle.radians0() * snaps / M_PI + .5);
                                    angle = (M_PI / snaps) * sections + orig_angle.radians0();
                                } else {
                                    double sections = floor(angle.radians0() * snaps / M_PI + .5);
                                    angle = (M_PI / snaps) * sections;
                                }
418 419
                            }
                        }
420
                        guide->set_normal(Geom::Point::polar(angle).cw(), false);
421 422 423 424
                        break;
                    }
                    case SP_DRAG_MOVE_ORIGIN:
                    {
425
                        guide->moveto(motion_dt, false);
426
                        break;
427 428
                    }
                    case SP_DRAG_NONE:
429
                        assert(false);
430 431
                        break;
                }
432
                moved = true;
433
                desktop->set_coordinate_status(motion_dt);
434

435 436 437
                ret = TRUE;
            }
            break;
438
    case GDK_BUTTON_RELEASE:
439
            if (drag_type != SP_DRAG_NONE && event->button.button == 1) {
440 441
                sp_event_context_discard_delayed_snap_event(desktop->event_context);

442
                if (moved) {
443 444
                    Geom::Point const event_w(event->button.x,
                                              event->button.y);
445
                    Geom::Point event_dt(desktop->w2d(event_w));
446

447
                    SnapManager &m = desktop->namedview->snap_manager;
448
                    m.setup(desktop, true, NULL, NULL, guide);
449
                    if (drag_type == SP_DRAG_MOVE_ORIGIN) {
450
                        // If we snap in guideConstrainedSnap() below, then motion_dt will
451 452 453
                        // be forced to be on the guide. If we don't snap however, then
                        // the origin should still be constrained to the guide. So let's
                        // do that explicitly first:
454
                        Geom::Line line(guide->getPoint(), guide->angle());
455
                        Geom::Coord t = line.nearestTime(event_dt);
456
                        event_dt = line.pointAt(t);
457 458 459
                        if (!(event->button.state & GDK_SHIFT_MASK)) {
                            m.guideConstrainedSnap(event_dt, *guide);
                        }
460
                    } else if (!((drag_type == SP_DRAG_ROTATE) && (event->motion.state & GDK_CONTROL_MASK))) {
461
                        // cannot use shift here to disable snapping, because we already use it for rotating the guide
462
                        Geom::Point temp;
463
                        if (drag_type == SP_DRAG_ROTATE) {
464 465 466
                            temp = guide->getPoint();
                            m.guideFreeSnap(event_dt, temp, true, false);
                            guide->moveto(temp, false);
467
                        } else {
468 469 470
                            temp = guide->getNormal();
                            m.guideFreeSnap(event_dt, temp, false, true);
                            guide->set_normal(temp, false);
471
                        }
472
                    }
473
                    m.unSetup();
474

475
                    if (sp_canvas_world_pt_inside_window(item->canvas, event_w)) {
476 477 478
                        switch (drag_type) {
                            case SP_DRAG_TRANSLATE:
                            {
479
                                guide->moveto(event_dt, true);
480 481 482 483
                                break;
                            }
                            case SP_DRAG_ROTATE:
                            {
484
                                Geom::Point pt = event_dt - guide->getPoint();
485 486
                                Geom::Angle angle(pt);
                                if (event->motion.state & GDK_CONTROL_MASK) {
487 488
                                    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
                                    unsigned const snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12));
489
                                    bool const relative_snaps = prefs->getBool("/options/relativeguiderotationsnap/value", false);
490
                                    if (snaps) {
491
                                        if (relative_snaps) {
492
                                            Geom::Angle orig_angle(guide->getNormal());
493 494 495 496 497 498 499
                                            Geom::Angle snap_angle = angle - orig_angle;
                                            double sections = floor(snap_angle.radians0() * snaps / M_PI + .5);
                                            angle = (M_PI / snaps) * sections + orig_angle.radians0();
                                        } else {
                                            double sections = floor(angle.radians0() * snaps / M_PI + .5);
                                            angle = (M_PI / snaps) * sections;
                                        }
500 501
                                    }
                                }
502
                                guide->set_normal(Geom::Point::polar(angle).cw(), true);
503 504 505 506
                                break;
                            }
                            case SP_DRAG_MOVE_ORIGIN:
                            {
507
                                guide->moveto(event_dt, true);
508
                                break;
509 510
                            }
                            case SP_DRAG_NONE:
511
                                assert(false);
512 513
                                break;
                        }
514
                        DocumentUndo::done(desktop->getDocument(), SP_VERB_NONE,
515
                                         _("Move guide"));
516 517
                    } else {
                        /* Undo movement of any attached shapes. */
518 519
                        guide->moveto(guide->getPoint(), false);
                        guide->set_normal(guide->getNormal(), false);
520
                        sp_guide_remove(guide);
521
                        DocumentUndo::done(desktop->getDocument(), SP_VERB_NONE,
Dmitry Kirsanov's avatar
Dmitry Kirsanov committed
522
                                     _("Delete guide"));
523
                    }
524
                    moved = false;
525
                    desktop->set_coordinate_status(event_dt);
526
                }
527
                drag_type = SP_DRAG_NONE;
528 529 530
                sp_canvas_item_ungrab(item, event->button.time);
                ret=TRUE;
            }
531
            break;
532 533
    case GDK_ENTER_NOTIFY:
    {
534 535 536
            if (!guide->getLocked()) {
                sp_guideline_set_color(SP_GUIDELINE(item), guide->getHiColor());
            }
537

538 539 540
            // set move or rotate cursor
            Geom::Point const event_w(event->crossing.x, event->crossing.y);

541 542 543
            GdkDisplay *display = gdk_display_get_default();
            GdkCursorType cursor_type;

544
            if ((event->crossing.state & GDK_SHIFT_MASK) && (drag_type != SP_DRAG_MOVE_ORIGIN)) {
545
                cursor_type = GDK_EXCHANGE;
546
            } else {
547 548 549 550 551
                cursor_type = GDK_HAND1;
            }

            GdkCursor *guide_cursor = gdk_cursor_new_for_display(display, cursor_type);
            if(guide->getLocked()){
552
                guide_cursor = sp_cursor_from_xpm(cursor_select_xpm);
553 554 555
            }
            gdk_window_set_cursor(gtk_widget_get_window (GTK_WIDGET(desktop->getCanvas())), guide_cursor);
            g_object_unref(guide_cursor);
556

557
            char *guide_description = guide->description();
Dmitry Kirsanov's avatar
Dmitry Kirsanov committed
558
            desktop->guidesMessageContext()->setF(Inkscape::NORMAL_MESSAGE, _("<b>Guideline</b>: %s"), guide_description);
559 560
            g_free(guide_description);
            break;
561 562
    }
    case GDK_LEAVE_NOTIFY:
563
            sp_guideline_set_color(SP_GUIDELINE(item), guide->getColor());
564 565

            // restore event context's cursor
liamwhite's avatar
liamwhite committed
566
            gdk_window_set_cursor(gtk_widget_get_window (GTK_WIDGET(desktop->getCanvas())), desktop->event_context->cursor);
567

568 569
            desktop->guidesMessageContext()->clear();
            break;
570
        case GDK_KEY_PRESS:
571
            switch (Inkscape::UI::Tools::get_latin_keyval (&event->key)) {
572 573 574
                case GDK_KEY_Delete:
                case GDK_KEY_KP_Delete:
                case GDK_KEY_BackSpace:
575
                {
576
                    SPDocument *doc = guide->document;
577
                    sp_guide_remove(guide);
578
                    DocumentUndo::done(doc, SP_VERB_NONE, _("Delete guide"));
579
                    ret = TRUE;
580
                    sp_event_context_discard_delayed_snap_event(desktop->event_context);
581 582
                    break;
                }
583 584
                case GDK_KEY_Shift_L:
                case GDK_KEY_Shift_R:
585
                    if (drag_type != SP_DRAG_MOVE_ORIGIN) {
586 587
                        GdkDisplay *display      = gdk_display_get_default();
                        GdkCursor  *guide_cursor = gdk_cursor_new_for_display(display, GDK_EXCHANGE);
liamwhite's avatar
liamwhite committed
588
                        gdk_window_set_cursor(gtk_widget_get_window (GTK_WIDGET(desktop->getCanvas())), guide_cursor);
589
                        g_object_unref(guide_cursor);
590 591 592 593
                        ret = TRUE;
                        break;
                    }

594 595 596 597
                default:
                    // do nothing;
                    break;
            }
598
            break;
599
        case GDK_KEY_RELEASE:
600
            switch (Inkscape::UI::Tools::get_latin_keyval (&event->key)) {
601 602
                case GDK_KEY_Shift_L:
                case GDK_KEY_Shift_R:
603 604 605
                {
                    GdkDisplay *display      = gdk_display_get_default();
                    GdkCursor  *guide_cursor = gdk_cursor_new_for_display(display, GDK_EXCHANGE);
liamwhite's avatar
liamwhite committed
606
                    gdk_window_set_cursor(gtk_widget_get_window (GTK_WIDGET(desktop->getCanvas())), guide_cursor);
607
                    g_object_unref(guide_cursor);
608
                    break;
609
                }
610 611 612 613
                default:
                    // do nothing;
                    break;
            }
614
            break;
615 616
    default:
        break;
617
    }
618

619
    return ret;
MenTaLguY's avatar
MenTaLguY committed
620 621
}

622 623
//static std::map<GdkInputSource, std::string> switchMap;
static std::map<std::string, int> toolToUse;
624 625 626 627 628
static std::string lastName;
static GdkInputSource lastType = GDK_SOURCE_MOUSE;

static void init_extended()
{
629
    Glib::ustring avoidName("pad");
630
    auto display = Gdk::Display::get_default();
631

632 633 634 635
#if GTK_CHECK_VERSION(3, 20, 0)
    auto seat = display->get_default_seat();
    auto const devices = seat->get_slaves(Gdk::SEAT_CAPABILITY_ALL);
#else
636 637
    auto dm = display->get_device_manager();
    auto const devices = dm->list_devices(Gdk::DEVICE_TYPE_SLAVE);	
638
#endif
639
    
640
    if ( !devices.empty() ) {
641 642 643
        for (auto const dev : devices) {
            auto const devName = dev->get_name();
            auto devSrc = dev->get_source();
Alex Valavanis's avatar
Alex Valavanis committed
644
            
645
            if ( !devName.empty()
646
                 && (avoidName != devName)
647
                 && (devSrc != Gdk::SOURCE_MOUSE) ) {
648
//                 g_message("Adding '%s' as [%d]", devName, devSrc);
649 650

                // Set the initial tool for the device
651
                switch ( devSrc ) {
652
                    case Gdk::SOURCE_PEN:
653
                        toolToUse[devName] = TOOLS_CALLIGRAPHIC;
654
                        break;
655
                    case Gdk::SOURCE_ERASER:
656
                        toolToUse[devName] = TOOLS_ERASER;
657
                        break;
658
                    case Gdk::SOURCE_CURSOR:
659
                        toolToUse[devName] = TOOLS_SELECT;
660
                        break;
661 662
                    default:
                        ; // do not add
663
                }
664 665
//            } else if (devName) {
//                 g_message("Skippn '%s' as [%d]", devName, devSrc);
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
            }
        }
    }
}


void snoop_extended(GdkEvent* event, SPDesktop *desktop)
{
    GdkInputSource source = GDK_SOURCE_MOUSE;
    std::string name;

    switch ( event->type ) {
        case GDK_MOTION_NOTIFY:
        {
            GdkEventMotion* event2 = reinterpret_cast<GdkEventMotion*>(event);
            if ( event2->device ) {
682 683
                source = gdk_device_get_source(event2->device);
                name = gdk_device_get_name(event2->device);
684 685 686 687 688 689 690 691 692 693 694
            }
        }
        break;

        case GDK_BUTTON_PRESS:
        case GDK_2BUTTON_PRESS:
        case GDK_3BUTTON_PRESS:
        case GDK_BUTTON_RELEASE:
        {
            GdkEventButton* event2 = reinterpret_cast<GdkEventButton*>(event);
            if ( event2->device ) {
695 696
                source = gdk_device_get_source(event2->device);
                name = gdk_device_get_name(event2->device);
697 698 699 700 701 702 703 704
            }
        }
        break;

        case GDK_SCROLL:
        {
            GdkEventScroll* event2 = reinterpret_cast<GdkEventScroll*>(event);
            if ( event2->device ) {
705 706
                source = gdk_device_get_source(event2->device);
                name = gdk_device_get_name(event2->device);
707 708 709 710 711 712 713 714 715
            }
        }
        break;

        case GDK_PROXIMITY_IN:
        case GDK_PROXIMITY_OUT:
        {
            GdkEventProximity* event2 = reinterpret_cast<GdkEventProximity*>(event);
            if ( event2->device ) {
716 717
                source = gdk_device_get_source(event2->device);
                name = gdk_device_get_name(event2->device);
718 719 720 721 722 723 724 725 726
            }
        }
        break;

        default:
            ;
    }

    if (!name.empty()) {
727
        if ( lastType != source || lastName != name ) {
728
            // The device switched. See if it is one we 'count'
729 730 731 732 733 734
            //g_message("Changed device %s -> %s", lastName.c_str(), name.c_str());
            std::map<std::string, int>::iterator it = toolToUse.find(lastName);
            if (it != toolToUse.end()) {
                // Save the tool currently selected for next time the input
                // device shows up.
                it->second = tools_active(desktop);
735
            }
736

737 738 739 740 741 742 743
            it = toolToUse.find(name);
            if (it != toolToUse.end() ) {
                tools_switch(desktop, it->second);
            }

            lastName = name;
            lastType = source;
744 745 746 747 748
        }
    }
}


Jon A. Cruz's avatar
Jon A. Cruz committed
749
void sp_dt_ruler_snap_new_guide(SPDesktop *desktop, SPCanvasItem * /*guide*/, Geom::Point &event_dt, Geom::Point &normal)
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
{
    SnapManager &m = desktop->namedview->snap_manager;
    m.setup(desktop);
    // We're dragging a brand new guide, just pulled of the rulers seconds ago. When snapping to a
    // path this guide will change it slope to become either tangential or perpendicular to that path. It's
    // therefore not useful to try tangential or perpendicular snapping, so this will be disabled temporarily
    bool pref_perp = m.snapprefs.getSnapPerp();
    bool pref_tang = m.snapprefs.getSnapTang();
    m.snapprefs.setSnapPerp(false);
    m.snapprefs.setSnapTang(false);
    // We only have a temporary guide which is not stored in our document yet.
    // Because the guide snapper only looks in the document for guides to snap to,
    // we don't have to worry about a guide snapping to itself here
    Geom::Point normal_orig = normal;
    m.guideFreeSnap(event_dt, normal, false, false);
    // After snapping, both event_dt and normal have been modified accordingly; we'll take the normal (of the
    // curve we snapped to) to set the normal the guide. And rotate it by 90 deg. if needed
    if (pref_perp) { // Perpendicular snapping to paths is requested by the user, so let's do that
        if (normal != normal_orig) {
            normal = Geom::rot90(normal);
        }
    }
    if (!(pref_tang || pref_perp)) { // if we don't want to snap either perpendicularly or tangentially, then
        normal = normal_orig; // we must restore the normal to it's original state
    }
    // Restore the preferences
    m.snapprefs.setSnapPerp(pref_perp);
    m.snapprefs.setSnapTang(pref_tang);
    m.unSetup();
}


MenTaLguY's avatar
MenTaLguY committed
782

783 784 785 786 787 788 789 790 791
/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
792
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
793