~centralelyon2010/inkscape/imagelinks2

« back to all changes in this revision

Viewing changes to src/knot.cpp

  • Committer: mental
  • Date: 2006-01-16 02:36:01 UTC
  • Revision ID: mental@users.sourceforge.net-20060116023601-wkr0h7edl5veyudq
moving trunk for module inkscape

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __SP_KNOT_C__
 
2
 
 
3
/** \file
 
4
 * SPKnot implementation
 
5
 *
 
6
 * Authors:
 
7
 *   Lauris Kaplinski <lauris@kaplinski.com>
 
8
 *   bulia byak <buliabyak@users.sf.net>
 
9
 *
 
10
 * Copyright (C) 1999-2005 authors
 
11
 * Copyright (C) 2001-2002 Ximian, Inc.
 
12
 *
 
13
 * Released under GNU GPL, read the file 'COPYING' for more information
 
14
 */
 
15
 
 
16
#ifdef HAVE_CONFIG_H
 
17
# include <config.h>
 
18
#endif
 
19
#include <gdk/gdkkeysyms.h>
 
20
#include <glibmm/i18n.h>
 
21
#include "helper/sp-marshal.h"
 
22
#include "display/sodipodi-ctrl.h"
 
23
#include "desktop.h"
 
24
#include "desktop-handles.h"
 
25
#include "knot.h"
 
26
#include "document.h"
 
27
#include "prefs-utils.h"
 
28
#include "message-stack.h"
 
29
#include "message-context.h"
 
30
#include "event-context.h"
 
31
 
 
32
 
 
33
#define KNOT_EVENT_MASK (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | \
 
34
                         GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | \
 
35
                         GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK)
 
36
 
 
37
static bool nograb = false;
 
38
 
 
39
static bool grabbed = FALSE;
 
40
static bool moved = FALSE;
 
41
 
 
42
static gint xp = 0, yp = 0; // where drag started
 
43
static gint tolerance = 0;
 
44
static bool within_tolerance = false;
 
45
 
 
46
static bool transform_escaped = false; // true iff resize or rotate was cancelled by esc.
 
47
 
 
48
enum {
 
49
    PROP_0,
 
50
    
 
51
    PROP_SIZE,
 
52
    PROP_ANCHOR,
 
53
    PROP_SHAPE,
 
54
    PROP_MODE,
 
55
    PROP_FILL, PROP_FILL_MOUSEOVER, PROP_FILL_DRAGGING,
 
56
    PROP_STROKE, PROP_STROKE_MOUSEOVER, PROP_STROKE_DRAGGING,
 
57
    PROP_IMAGE, PROP_IMAGE_MOUSEOVER, PROP_IMAGE_DRAGGING,
 
58
    PROP_CURSOR, PROP_CURSOR_MOUSEOVER, PROP_CURSOR_DRAGGING,
 
59
    PROP_PIXBUF,
 
60
    PROP_TIP,
 
61
    
 
62
    PROP_LAST
 
63
};
 
64
 
 
65
enum {
 
66
    EVENT,
 
67
    CLICKED,
 
68
    DOUBLECLICKED,
 
69
    GRABBED,
 
70
    UNGRABBED,
 
71
    MOVED,
 
72
    REQUEST,
 
73
    DISTANCE,
 
74
    LAST_SIGNAL
 
75
};
 
76
 
 
77
static void sp_knot_class_init(SPKnotClass *klass);
 
78
static void sp_knot_init(SPKnot *knot);
 
79
static void sp_knot_dispose(GObject *object);
 
80
static void sp_knot_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
 
81
static void sp_knot_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
 
82
 
 
83
static int sp_knot_handler(SPCanvasItem *item, GdkEvent *event, SPKnot *knot);
 
84
static void sp_knot_set_flag(SPKnot *knot, guint flag, bool set);
 
85
static void sp_knot_update_ctrl(SPKnot *knot);
 
86
static void sp_knot_set_ctrl_state(SPKnot *knot);
 
87
 
 
88
static GObjectClass *parent_class;
 
89
static guint knot_signals[LAST_SIGNAL] = { 0 };
 
90
 
 
91
/**
 
92
 * Registers SPKnot class and returns its type number.
 
93
 */
 
94
GType sp_knot_get_type()
 
95
{
 
96
    static GType type = 0;
 
97
    if (!type) {
 
98
        GTypeInfo info = {
 
99
            sizeof(SPKnotClass),
 
100
            NULL,       /* base_init */
 
101
            NULL,       /* base_finalize */
 
102
            (GClassInitFunc) sp_knot_class_init,
 
103
            NULL,       /* class_finalize */
 
104
            NULL,       /* class_data */
 
105
            sizeof (SPKnot),
 
106
            16, /* n_preallocs */
 
107
            (GInstanceInitFunc) sp_knot_init,
 
108
            NULL
 
109
        };
 
110
        type = g_type_register_static (G_TYPE_OBJECT, "SPKnot", &info, (GTypeFlags) 0);
 
111
    }
 
112
    return type;
 
113
}
 
114
 
 
115
/**
 
116
 * SPKnot vtable initialization.
 
117
 */
 
118
static void sp_knot_class_init(SPKnotClass *klass)
 
119
{
 
120
    GObjectClass *object_class = (GObjectClass *) klass;
 
121
 
 
122
    parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
 
123
    
 
124
    object_class->dispose = sp_knot_dispose;
 
125
    object_class->set_property = sp_knot_set_property;
 
126
    object_class->get_property = sp_knot_get_property;
 
127
 
 
128
    /* Huh :) */
 
129
    
 
130
    g_object_class_install_property(object_class,
 
131
                                    PROP_SIZE,
 
132
                                    g_param_spec_uint("size", "Size", "",
 
133
                                                       0,
 
134
                                                       0xffffffff,
 
135
                                                       0xff000000,
 
136
                                                       (GParamFlags) G_PARAM_READWRITE));
 
137
    g_object_class_install_property(object_class,
 
138
                                    PROP_ANCHOR,
 
139
                                    g_param_spec_enum("anchor", "Anchor", "",
 
140
                                                       GTK_TYPE_ANCHOR_TYPE,
 
141
                                                       GTK_ANCHOR_CENTER,
 
142
                                                       (GParamFlags) G_PARAM_READWRITE));
 
143
    g_object_class_install_property(object_class,
 
144
                                    PROP_SHAPE,
 
145
                                    g_param_spec_int("shape", "Shape", "",
 
146
                                                     SP_KNOT_SHAPE_SQUARE,
 
147
                                                     SP_KNOT_SHAPE_IMAGE,
 
148
                                                     SP_KNOT_SHAPE_SQUARE,
 
149
                                                     (GParamFlags) G_PARAM_READWRITE));
 
150
    
 
151
    g_object_class_install_property(object_class,
 
152
                                    PROP_MODE,
 
153
                                    g_param_spec_int("mode", "Mode", "",
 
154
                                                     SP_KNOT_MODE_COLOR,
 
155
                                                     SP_KNOT_MODE_XOR,
 
156
                                                     SP_KNOT_MODE_XOR,
 
157
                                                     (GParamFlags) G_PARAM_READWRITE));
 
158
    
 
159
    g_object_class_install_property(object_class,
 
160
                                    PROP_FILL,
 
161
                                    g_param_spec_uint("fill", "Fill", "",
 
162
                                                      0,
 
163
                                                      0xffffffff,
 
164
                                                      0xff000000,
 
165
                                                      (GParamFlags) G_PARAM_READWRITE));
 
166
    
 
167
    g_object_class_install_property(object_class,
 
168
                                    PROP_FILL_MOUSEOVER,
 
169
                                    g_param_spec_uint("fill_mouseover", "Fill mouse over", "",
 
170
                                                      0,
 
171
                                                      0xffffffff,
 
172
                                                      0xff000000,
 
173
                                                      (GParamFlags) G_PARAM_READWRITE));
 
174
    
 
175
    g_object_class_install_property(object_class,
 
176
                                    PROP_FILL_DRAGGING,
 
177
                                    g_param_spec_uint("fill_dragging", "Fill dragging", "",
 
178
                                                      0,
 
179
                                                      0xffffffff,
 
180
                                                      0xff000000,
 
181
                                                      (GParamFlags) G_PARAM_READWRITE));
 
182
    
 
183
    g_object_class_install_property(object_class,
 
184
                                    PROP_STROKE,
 
185
                                    g_param_spec_uint("stroke", "Stroke", "",
 
186
                                                      0,
 
187
                                                      0xffffffff,
 
188
                                                      0x01000000,
 
189
                                                      (GParamFlags) G_PARAM_READWRITE));
 
190
    
 
191
    g_object_class_install_property(object_class,
 
192
                                    PROP_STROKE_MOUSEOVER,
 
193
                                    g_param_spec_uint("stroke_mouseover", "Stroke mouseover", "",
 
194
                                                      0,
 
195
                                                      0xffffffff,
 
196
                                                      0x01000000,
 
197
                                                      (GParamFlags) G_PARAM_READWRITE));
 
198
    
 
199
    g_object_class_install_property(object_class,
 
200
                                    PROP_STROKE_DRAGGING,
 
201
                                    g_param_spec_uint("stroke_dragging", "Stroke dragging", "",
 
202
                                                      0,
 
203
                                                      0xffffffff,
 
204
                                                      0x01000000,
 
205
                                                      (GParamFlags) G_PARAM_READWRITE));
 
206
    
 
207
    g_object_class_install_property(object_class,
 
208
                                    PROP_IMAGE,
 
209
                                    g_param_spec_pointer("image", "Image", "",
 
210
                                                         (GParamFlags) G_PARAM_READWRITE));
 
211
    
 
212
    g_object_class_install_property(object_class,
 
213
                                    PROP_IMAGE_MOUSEOVER,
 
214
                                    g_param_spec_pointer("image_mouseover", "Image mouseover", "",
 
215
                                                         (GParamFlags) G_PARAM_READWRITE));
 
216
    
 
217
    g_object_class_install_property(object_class,
 
218
                                    PROP_IMAGE_DRAGGING,
 
219
                                    g_param_spec_pointer("image_dragging", "Image dragging", "",
 
220
                                                         (GParamFlags) G_PARAM_READWRITE));
 
221
    
 
222
    g_object_class_install_property(object_class,
 
223
                                     PROP_CURSOR,
 
224
                                     g_param_spec_boxed("cursor", "Cursor", "",
 
225
                                                         GDK_TYPE_CURSOR,
 
226
                                                             (GParamFlags) G_PARAM_READWRITE));
 
227
    
 
228
    g_object_class_install_property(object_class,
 
229
                                    PROP_CURSOR_MOUSEOVER,
 
230
                                    g_param_spec_boxed("cursor_mouseover", "Cursor mouseover", "",
 
231
                                                       GDK_TYPE_CURSOR,
 
232
                                                       (GParamFlags) G_PARAM_READWRITE));
 
233
    
 
234
    g_object_class_install_property(object_class,
 
235
                                    PROP_CURSOR_DRAGGING,
 
236
                                    g_param_spec_boxed("cursor_dragging", "Cursor dragging", "",
 
237
                                                       GDK_TYPE_CURSOR,
 
238
                                                       (GParamFlags) G_PARAM_READWRITE));
 
239
    
 
240
    g_object_class_install_property(object_class,
 
241
                                    PROP_PIXBUF,
 
242
                                    g_param_spec_pointer("pixbuf", "Pixbuf", "",
 
243
                                                         (GParamFlags) G_PARAM_READWRITE));
 
244
    
 
245
    g_object_class_install_property(object_class,
 
246
                                    PROP_TIP,
 
247
                                    g_param_spec_pointer("tip", "Tip", "",
 
248
                                                         (GParamFlags) G_PARAM_READWRITE));
 
249
    
 
250
    knot_signals[EVENT] = g_signal_new("event",
 
251
                                       G_TYPE_FROM_CLASS(klass),
 
252
                                       G_SIGNAL_RUN_LAST,
 
253
                                       G_STRUCT_OFFSET(SPKnotClass, event),
 
254
                                       NULL, NULL,
 
255
                                       sp_marshal_BOOLEAN__POINTER,
 
256
                                       G_TYPE_BOOLEAN, 1,
 
257
                                       GDK_TYPE_EVENT);
 
258
    
 
259
    knot_signals[CLICKED] = g_signal_new("clicked",
 
260
                                         G_TYPE_FROM_CLASS(klass),
 
261
                                         G_SIGNAL_RUN_FIRST,
 
262
                                         G_STRUCT_OFFSET(SPKnotClass, clicked),
 
263
                                         NULL, NULL,
 
264
                                         sp_marshal_NONE__UINT,
 
265
                                         G_TYPE_NONE, 1,
 
266
                                         G_TYPE_UINT);
 
267
    
 
268
    knot_signals[DOUBLECLICKED] = g_signal_new("doubleclicked",
 
269
                                               G_TYPE_FROM_CLASS(klass),
 
270
                                               G_SIGNAL_RUN_FIRST,
 
271
                                               G_STRUCT_OFFSET(SPKnotClass, doubleclicked),
 
272
                                               NULL, NULL,
 
273
                                               sp_marshal_NONE__UINT,
 
274
                                               G_TYPE_NONE, 1,
 
275
                                               G_TYPE_UINT);
 
276
    
 
277
    knot_signals[GRABBED] = g_signal_new("grabbed",
 
278
                                          G_TYPE_FROM_CLASS(klass),
 
279
                                          G_SIGNAL_RUN_FIRST,
 
280
                                          G_STRUCT_OFFSET(SPKnotClass, grabbed),
 
281
                                          NULL, NULL,
 
282
                                          sp_marshal_NONE__UINT,
 
283
                                          G_TYPE_NONE, 1,
 
284
                                          G_TYPE_UINT);
 
285
    
 
286
    knot_signals[UNGRABBED] = g_signal_new("ungrabbed",
 
287
                                            G_TYPE_FROM_CLASS(klass),
 
288
                                            G_SIGNAL_RUN_FIRST,
 
289
                                            G_STRUCT_OFFSET(SPKnotClass, ungrabbed),
 
290
                                            NULL, NULL,
 
291
                                            sp_marshal_NONE__UINT,
 
292
                                            G_TYPE_NONE, 1,
 
293
                                            G_TYPE_UINT);
 
294
    
 
295
    knot_signals[MOVED] = g_signal_new("moved",
 
296
                                        G_TYPE_FROM_CLASS(klass),
 
297
                                        G_SIGNAL_RUN_FIRST,
 
298
                                        G_STRUCT_OFFSET(SPKnotClass, moved),
 
299
                                        NULL, NULL,
 
300
                                        sp_marshal_NONE__POINTER_UINT,
 
301
                                        G_TYPE_NONE, 2,
 
302
                                        G_TYPE_POINTER, G_TYPE_UINT);
 
303
    
 
304
    knot_signals[REQUEST] = g_signal_new("request",
 
305
                                         G_TYPE_FROM_CLASS(klass),
 
306
                                         G_SIGNAL_RUN_LAST,
 
307
                                         G_STRUCT_OFFSET(SPKnotClass, request),
 
308
                                         NULL, NULL,
 
309
                                         sp_marshal_BOOLEAN__POINTER_UINT,
 
310
                                         G_TYPE_BOOLEAN, 2,
 
311
                                         G_TYPE_POINTER, G_TYPE_UINT);
 
312
    
 
313
    knot_signals[DISTANCE] = g_signal_new("distance",
 
314
                                           G_TYPE_FROM_CLASS(klass),
 
315
                                           G_SIGNAL_RUN_LAST,
 
316
                                           G_STRUCT_OFFSET(SPKnotClass, distance),
 
317
                                           NULL, NULL,
 
318
                                           sp_marshal_DOUBLE__POINTER_UINT,
 
319
                                           G_TYPE_DOUBLE, 2,
 
320
                                           G_TYPE_POINTER, G_TYPE_UINT);
 
321
 
 
322
    const gchar *nograbenv = getenv("INKSCAPE_NO_GRAB");
 
323
    nograb = (nograbenv && *nograbenv && (*nograbenv != '0'));
 
324
}
 
325
 
 
326
/**
 
327
 * Callback for SPKnot initialization.
 
328
 */
 
329
static void sp_knot_init(SPKnot *knot)
 
330
{
 
331
    knot->desktop = NULL;
 
332
    knot->item = NULL;
 
333
    knot->flags = 0;
 
334
    
 
335
    knot->size = 8;
 
336
    knot->pos = NR::Point(0, 0);
 
337
    knot->grabbed_rel_pos = NR::Point(0, 0);
 
338
    knot->anchor = GTK_ANCHOR_CENTER;
 
339
    knot->shape = SP_KNOT_SHAPE_SQUARE;
 
340
    knot->mode = SP_KNOT_MODE_XOR;
 
341
    knot->tip = NULL;
 
342
    
 
343
    knot->fill[SP_KNOT_STATE_NORMAL] = 0xffffff00;
 
344
    knot->fill[SP_KNOT_STATE_MOUSEOVER] = 0xff0000ff;
 
345
    knot->fill[SP_KNOT_STATE_DRAGGING] = 0x0000ffff;
 
346
 
 
347
    knot->stroke[SP_KNOT_STATE_NORMAL] = 0x01000000;
 
348
    knot->stroke[SP_KNOT_STATE_MOUSEOVER] = 0x01000000;
 
349
    knot->stroke[SP_KNOT_STATE_DRAGGING] = 0x01000000;
 
350
 
 
351
    knot->image[SP_KNOT_STATE_NORMAL] = NULL;
 
352
    knot->image[SP_KNOT_STATE_MOUSEOVER] = NULL;
 
353
    knot->image[SP_KNOT_STATE_DRAGGING] = NULL;
 
354
 
 
355
    knot->cursor[SP_KNOT_STATE_NORMAL] = NULL;
 
356
    knot->cursor[SP_KNOT_STATE_MOUSEOVER] = NULL;
 
357
    knot->cursor[SP_KNOT_STATE_DRAGGING] = NULL;
 
358
 
 
359
    knot->saved_cursor = NULL;
 
360
    knot->pixbuf = NULL;
 
361
}
 
362
 
 
363
/**
 
364
 * Called before SPKnot destruction.
 
365
 */
 
366
static void sp_knot_dispose(GObject *object)
 
367
{
 
368
    SPKnot *knot = (SPKnot *) object;
 
369
 
 
370
    if ((knot->flags & SP_KNOT_GRABBED) && gdk_pointer_is_grabbed ()) {
 
371
        // This happens e.g. when deleting a node in node tool while dragging it
 
372
        gdk_pointer_ungrab (GDK_CURRENT_TIME);
 
373
    }
 
374
 
 
375
    if (knot->item) {
 
376
        gtk_object_destroy (GTK_OBJECT (knot->item));
 
377
        knot->item = NULL;
 
378
    }
 
379
 
 
380
    for (gint i = 0; i < SP_KNOT_VISIBLE_STATES; i++) {
 
381
        if (knot->cursor[i]) {
 
382
            gdk_cursor_unref(knot->cursor[i]);
 
383
            knot->cursor[i] = NULL;
 
384
        }
 
385
    }
 
386
 
 
387
    if (knot->tip) {
 
388
        g_free(knot->tip);
 
389
        knot->tip = NULL;
 
390
    }
 
391
 
 
392
    if (((GObjectClass *) (parent_class))->dispose) {
 
393
        (* ((GObjectClass *) (parent_class))->dispose) (object);
 
394
    }
 
395
}
 
396
 
 
397
/**
 
398
 * Callback to set property.
 
399
 */
 
400
static void sp_knot_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 
401
{
 
402
    GdkCursor *cursor;
 
403
 
 
404
    SPKnot *knot = SP_KNOT(object);
 
405
 
 
406
    switch (prop_id) {
 
407
        case PROP_SIZE:
 
408
            knot->size = g_value_get_uint(value);
 
409
            break;
 
410
        case PROP_ANCHOR:
 
411
            knot->anchor = (GtkAnchorType) g_value_get_enum(value);
 
412
            break;
 
413
        case PROP_SHAPE:
 
414
            knot->shape = (SPKnotShapeType) g_value_get_int(value);
 
415
            break;
 
416
        case PROP_MODE:
 
417
            knot->mode = (SPKnotModeType) g_value_get_int(value);
 
418
            break;
 
419
        case PROP_FILL:
 
420
            knot->fill[SP_KNOT_STATE_NORMAL] =
 
421
                knot->fill[SP_KNOT_STATE_MOUSEOVER] =
 
422
                knot->fill[SP_KNOT_STATE_DRAGGING] = g_value_get_uint(value);
 
423
            break;
 
424
        case PROP_FILL_MOUSEOVER:
 
425
            knot->fill[SP_KNOT_STATE_MOUSEOVER] = 
 
426
                knot->fill[SP_KNOT_STATE_DRAGGING] = g_value_get_uint(value);
 
427
            break;
 
428
        case PROP_FILL_DRAGGING:
 
429
            knot->fill[SP_KNOT_STATE_DRAGGING] = g_value_get_uint(value);
 
430
            break;
 
431
        case PROP_STROKE:
 
432
            knot->stroke[SP_KNOT_STATE_NORMAL] =
 
433
                knot->stroke[SP_KNOT_STATE_MOUSEOVER] =
 
434
                knot->stroke[SP_KNOT_STATE_DRAGGING] = g_value_get_uint(value);
 
435
            break;
 
436
        case PROP_STROKE_MOUSEOVER:
 
437
            knot->stroke[SP_KNOT_STATE_MOUSEOVER] = 
 
438
                knot->stroke[SP_KNOT_STATE_DRAGGING] = g_value_get_uint(value);
 
439
            break;
 
440
        case PROP_STROKE_DRAGGING:
 
441
            knot->stroke[SP_KNOT_STATE_DRAGGING] = g_value_get_uint(value);
 
442
            break;
 
443
        case PROP_IMAGE:
 
444
            knot->image[SP_KNOT_STATE_NORMAL] =
 
445
                knot->image[SP_KNOT_STATE_MOUSEOVER] =
 
446
                knot->image[SP_KNOT_STATE_DRAGGING] = (guchar*) g_value_get_pointer(value);
 
447
            break;
 
448
        case PROP_IMAGE_MOUSEOVER:
 
449
            knot->image[SP_KNOT_STATE_MOUSEOVER] = (guchar*) g_value_get_pointer(value);
 
450
            break;
 
451
        case PROP_IMAGE_DRAGGING:
 
452
            knot->image[SP_KNOT_STATE_DRAGGING] = (guchar*) g_value_get_pointer(value);
 
453
            break;
 
454
        case PROP_CURSOR:
 
455
            cursor = (GdkCursor*) g_value_get_boxed(value);
 
456
            for (gint i = 0; i < SP_KNOT_VISIBLE_STATES; i++) {
 
457
                if (knot->cursor[i]) {
 
458
                    gdk_cursor_unref(knot->cursor[i]);
 
459
                }
 
460
                knot->cursor[i] = cursor;
 
461
                if (cursor) {
 
462
                    gdk_cursor_ref(cursor);
 
463
                }
 
464
            }
 
465
            break;
 
466
        case PROP_CURSOR_MOUSEOVER:
 
467
            cursor = (GdkCursor*) g_value_get_boxed(value);
 
468
            if (knot->cursor[SP_KNOT_STATE_MOUSEOVER]) {
 
469
                gdk_cursor_unref(knot->cursor[SP_KNOT_STATE_MOUSEOVER]);
 
470
            }
 
471
            knot->cursor[SP_KNOT_STATE_MOUSEOVER] = cursor;
 
472
            if (cursor) {
 
473
                gdk_cursor_ref(cursor);
 
474
            }
 
475
            break;
 
476
        case PROP_CURSOR_DRAGGING:
 
477
            cursor = (GdkCursor*) g_value_get_boxed(value);
 
478
            if (knot->cursor[SP_KNOT_STATE_DRAGGING]) {
 
479
                gdk_cursor_unref(knot->cursor[SP_KNOT_STATE_DRAGGING]);
 
480
            }
 
481
            knot->cursor[SP_KNOT_STATE_DRAGGING] = cursor;
 
482
            if (cursor) {
 
483
                gdk_cursor_ref(cursor);
 
484
            }
 
485
            break;
 
486
        case PROP_PIXBUF:
 
487
            knot->pixbuf = g_value_get_pointer(value);
 
488
            break;
 
489
        case PROP_TIP:
 
490
            knot->tip = g_strdup((const gchar *) g_value_get_pointer(value));
 
491
            break;
 
492
        default:
 
493
            g_assert_not_reached();
 
494
            break;
 
495
    }
 
496
    
 
497
    sp_knot_update_ctrl(knot);
 
498
}
 
499
 
 
500
/// Not reached.
 
501
static void sp_knot_get_property(GObject *, guint, GValue *, GParamSpec *)
 
502
{
 
503
    g_assert_not_reached();
 
504
}
 
505
 
 
506
/**
 
507
 * Update knot for dragging and tell canvas an item was grabbed.
 
508
 */
 
509
void sp_knot_start_dragging(SPKnot *knot, NR::Point p, gint x, gint y, guint32 etime)
 
510
{
 
511
    // save drag origin
 
512
    xp = x; 
 
513
    yp = y;
 
514
    within_tolerance = true;
 
515
 
 
516
    knot->grabbed_rel_pos = p - knot->pos;
 
517
    knot->drag_origin = knot->pos;
 
518
    if (!nograb) {
 
519
        sp_canvas_item_grab(knot->item,
 
520
                            KNOT_EVENT_MASK,
 
521
                            knot->cursor[SP_KNOT_STATE_DRAGGING],
 
522
                            etime);
 
523
    }
 
524
    sp_knot_set_flag(knot, SP_KNOT_GRABBED, TRUE);
 
525
    grabbed = TRUE;
 
526
}
 
527
 
 
528
/**
 
529
 * Called to handle events on knots.
 
530
 */
 
531
static int sp_knot_handler(SPCanvasItem *item, GdkEvent *event, SPKnot *knot)
 
532
{
 
533
    g_assert(knot != NULL);
 
534
    g_assert(SP_IS_KNOT(knot));
 
535
 
 
536
    tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100);
 
537
 
 
538
    gboolean consumed = FALSE;
 
539
 
 
540
    /* Run client universal event handler, if present */
 
541
 
 
542
    g_signal_emit(G_OBJECT(knot), knot_signals[EVENT], 0, event, &consumed);
 
543
 
 
544
    if (consumed) {
 
545
        return TRUE;
 
546
    }
 
547
 
 
548
    switch (event->type) {
 
549
        case GDK_2BUTTON_PRESS:
 
550
            if (event->button.button == 1) {
 
551
                g_signal_emit(G_OBJECT(knot), knot_signals[DOUBLECLICKED], 0, event->button.state);
 
552
 
 
553
                grabbed = FALSE;
 
554
                moved = FALSE;
 
555
                consumed = TRUE;
 
556
            }
 
557
            break;
 
558
        case GDK_BUTTON_PRESS:
 
559
            if (event->button.button == 1) {
 
560
                NR::Point const p = knot->desktop->w2d(NR::Point(event->button.x, event->button.y));
 
561
                sp_knot_start_dragging(knot, p, (gint) event->button.x, (gint) event->button.y, event->button.time);
 
562
                consumed = TRUE;
 
563
            }
 
564
            break;
 
565
        case GDK_BUTTON_RELEASE:
 
566
            if (event->button.button == 1) {
 
567
                if (transform_escaped) {
 
568
                    transform_escaped = false;
 
569
                    consumed = TRUE;
 
570
                } else {
 
571
                    sp_knot_set_flag(knot, SP_KNOT_GRABBED, FALSE);
 
572
                    if (!nograb) {
 
573
                        sp_canvas_item_ungrab(knot->item, event->button.time);
 
574
                    }
 
575
                    if (moved) {
 
576
                        sp_knot_set_flag(knot,
 
577
                                         SP_KNOT_DRAGGING,
 
578
                                         FALSE);
 
579
                        g_signal_emit(G_OBJECT (knot),
 
580
                                      knot_signals[UNGRABBED], 0,
 
581
                                      event->button.state);
 
582
                    } else {
 
583
                        g_signal_emit(G_OBJECT (knot),
 
584
                                      knot_signals[CLICKED], 0,
 
585
                                      event->button.state);
 
586
                    }
 
587
                    grabbed = FALSE;
 
588
                    moved = FALSE;
 
589
                    consumed = TRUE;
 
590
                }
 
591
            }
 
592
            break;
 
593
        case GDK_MOTION_NOTIFY:
 
594
            if (grabbed) {
 
595
                consumed = TRUE;
 
596
                
 
597
                if ( within_tolerance
 
598
                     && ( abs( (gint) event->motion.x - xp ) < tolerance )
 
599
                     && ( abs( (gint) event->motion.y - yp ) < tolerance ) ) {
 
600
                    break; // do not drag if we're within tolerance from origin
 
601
                }
 
602
                
 
603
                // Once the user has moved farther than tolerance from the original location 
 
604
                // (indicating they intend to move the object, not click), then always process the 
 
605
                // motion notify coordinates as given (no snapping back to origin)
 
606
                within_tolerance = false; 
 
607
                
 
608
                if (!moved) {
 
609
                    g_signal_emit(G_OBJECT (knot),
 
610
                                  knot_signals[GRABBED], 0,
 
611
                                  event->motion.state);
 
612
                    sp_knot_set_flag(knot,
 
613
                                     SP_KNOT_DRAGGING,
 
614
                                     TRUE);
 
615
                }
 
616
                NR::Point const motion_w(event->motion.x, event->motion.y);
 
617
                NR::Point const motion_dt = knot->desktop->w2d(motion_w);
 
618
                NR::Point p = motion_dt - knot->grabbed_rel_pos;
 
619
                sp_knot_request_position (knot, &p, event->motion.state);
 
620
                knot->desktop->scroll_to_point (&motion_dt);
 
621
                moved = TRUE;
 
622
            }
 
623
            break;
 
624
        case GDK_ENTER_NOTIFY:
 
625
            sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, TRUE);
 
626
            sp_knot_set_flag(knot, SP_KNOT_GRABBED, FALSE);
 
627
 
 
628
            if (knot->tip) {
 
629
                knot->desktop->event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, knot->tip);
 
630
            }
 
631
 
 
632
            grabbed = FALSE;
 
633
            moved = FALSE;
 
634
            consumed = TRUE;
 
635
            break;
 
636
        case GDK_LEAVE_NOTIFY:
 
637
            sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, FALSE);
 
638
            sp_knot_set_flag(knot, SP_KNOT_GRABBED, FALSE);
 
639
 
 
640
            if (knot->tip) {
 
641
                knot->desktop->event_context->defaultMessageContext()->clear();
 
642
            }
 
643
 
 
644
            grabbed = FALSE;
 
645
            moved = FALSE;
 
646
            
 
647
            consumed = TRUE;
 
648
            break;
 
649
        case GDK_KEY_PRESS: // keybindings for knot
 
650
            switch (get_group0_keyval(&event->key)) {  
 
651
                case GDK_Escape:
 
652
                    sp_knot_set_flag(knot, SP_KNOT_GRABBED, FALSE);
 
653
                    if (!nograb) {
 
654
                        sp_canvas_item_ungrab(knot->item, event->button.time);
 
655
                    }
 
656
                    if (moved) {
 
657
                        sp_knot_set_flag(knot,
 
658
                                         SP_KNOT_DRAGGING,
 
659
                                         FALSE);
 
660
                        g_signal_emit(G_OBJECT(knot),
 
661
                                      knot_signals[UNGRABBED], 0,
 
662
                                      event->button.state);
 
663
                        sp_document_undo(SP_DT_DOCUMENT(knot->desktop));
 
664
                        knot->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Node or handle drag canceled."));
 
665
                        transform_escaped = true;
 
666
                        consumed = TRUE;
 
667
                    } 
 
668
                    grabbed = FALSE;
 
669
                    moved = FALSE;
 
670
                    break;
 
671
                default:
 
672
                    consumed = FALSE;
 
673
                    break;
 
674
            }
 
675
            break;
 
676
        default:
 
677
            break;
 
678
    }
 
679
 
 
680
 
 
681
    return consumed;
 
682
}
 
683
 
 
684
/**
 
685
 * Return new knot object.
 
686
 */
 
687
SPKnot *sp_knot_new(SPDesktop *desktop, const gchar *tip)
 
688
{
 
689
    g_return_val_if_fail(desktop != NULL, NULL);
 
690
 
 
691
    SPKnot * knot = (SPKnot*) g_object_new(SP_TYPE_KNOT, 0);
 
692
 
 
693
    knot->desktop = desktop;
 
694
    knot->flags = SP_KNOT_VISIBLE;
 
695
    if (tip) {
 
696
        knot->tip = g_strdup (tip);
 
697
    }
 
698
 
 
699
    knot->item = sp_canvas_item_new(SP_DT_CONTROLS (desktop),
 
700
                                    SP_TYPE_CTRL,
 
701
                                    "anchor", GTK_ANCHOR_CENTER,
 
702
                                    "size", 8.0,
 
703
                                    "filled", TRUE,
 
704
                                    "fill_color", 0xffffff00,
 
705
                                    "stroked", TRUE,
 
706
                                    "stroke_color", 0x01000000,
 
707
                                    "mode", SP_KNOT_MODE_XOR,
 
708
                                    NULL);
 
709
 
 
710
    gtk_signal_connect(GTK_OBJECT(knot->item), "event",
 
711
                       GTK_SIGNAL_FUNC(sp_knot_handler), knot);
 
712
 
 
713
    return knot;
 
714
}
 
715
 
 
716
/**
 
717
 * Show knot on its canvas.
 
718
 */
 
719
void sp_knot_show(SPKnot *knot)
 
720
{
 
721
    g_return_if_fail(knot != NULL);
 
722
    g_return_if_fail(SP_IS_KNOT (knot));
 
723
    
 
724
    sp_knot_set_flag(knot, SP_KNOT_VISIBLE, TRUE);
 
725
}
 
726
 
 
727
/**
 
728
 * Hide knot on its canvas.
 
729
 */
 
730
void sp_knot_hide(SPKnot *knot)
 
731
{
 
732
    g_return_if_fail(knot != NULL);
 
733
    g_return_if_fail(SP_IS_KNOT(knot));
 
734
 
 
735
    sp_knot_set_flag(knot, SP_KNOT_VISIBLE, FALSE);
 
736
}
 
737
 
 
738
/**
 
739
 * Request or set new position for knot.
 
740
 */
 
741
void sp_knot_request_position(SPKnot *knot, NR::Point *p, guint state)
 
742
{
 
743
    g_return_if_fail(knot != NULL);
 
744
    g_return_if_fail(SP_IS_KNOT(knot));
 
745
 
 
746
    gboolean done = FALSE;
 
747
 
 
748
    g_signal_emit(G_OBJECT (knot),
 
749
                  knot_signals[REQUEST], 0,
 
750
                  p,
 
751
                  state,
 
752
                  &done);
 
753
 
 
754
    /* If user did not complete, we simply move knot to new position */
 
755
 
 
756
    if (!done) {
 
757
        sp_knot_set_position (knot, p, state);
 
758
    }
 
759
}
 
760
 
 
761
/**
 
762
 * Return distance of point to knot's position; unused.
 
763
 */
 
764
gdouble sp_knot_distance(SPKnot * knot, NR::Point *p, guint state)
 
765
{
 
766
    g_return_val_if_fail(knot != NULL, 1e18);
 
767
    g_return_val_if_fail(SP_IS_KNOT(knot), 1e18);
 
768
 
 
769
    gdouble distance = NR::L2(*p - knot->pos);
 
770
 
 
771
    g_signal_emit(G_OBJECT(knot),
 
772
                  knot_signals[DISTANCE], 0,
 
773
                  p,
 
774
                  state,
 
775
                  &distance);
 
776
    
 
777
    return distance;
 
778
}
 
779
 
 
780
/**
 
781
 * Move knot to new position.
 
782
 */
 
783
void sp_knot_set_position(SPKnot *knot, NR::Point *p, guint state)
 
784
{
 
785
    g_return_if_fail(knot != NULL);
 
786
    g_return_if_fail(SP_IS_KNOT (knot));
 
787
 
 
788
    knot->pos = *p;
 
789
 
 
790
    if (knot->item) {
 
791
        SP_CTRL(knot->item)->moveto (*p);
 
792
    }
 
793
 
 
794
    g_signal_emit(G_OBJECT (knot),
 
795
                  knot_signals[MOVED], 0,
 
796
                  p,
 
797
                  state);
 
798
}
 
799
 
 
800
/**
 
801
 * Move knot to new position, without emitting a MOVED signal.
 
802
 */
 
803
void sp_knot_moveto(SPKnot *knot, NR::Point *p)
 
804
{
 
805
    g_return_if_fail(knot != NULL);
 
806
    g_return_if_fail(SP_IS_KNOT(knot));
 
807
 
 
808
    knot->pos = *p;
 
809
    
 
810
    if (knot->item) {
 
811
        SP_CTRL(knot->item)->moveto (*p);
 
812
    }
 
813
}
 
814
 
 
815
/**
 
816
 * Returns position of knot.
 
817
 */
 
818
NR::Point sp_knot_position(SPKnot const *knot)
 
819
{
 
820
    g_assert(knot != NULL);
 
821
    g_assert(SP_IS_KNOT (knot));
 
822
    
 
823
    return knot->pos;
 
824
}
 
825
 
 
826
/**
 
827
 * Set flag in knot, with side effects.
 
828
 */
 
829
static void sp_knot_set_flag(SPKnot *knot, guint flag, bool set)
 
830
{
 
831
    g_assert(knot != NULL);
 
832
    g_assert(SP_IS_KNOT(knot));
 
833
    
 
834
    if (set) {
 
835
        knot->flags |= flag;
 
836
    } else {
 
837
        knot->flags &= ~flag;
 
838
    }
 
839
    
 
840
    switch (flag) {
 
841
        case SP_KNOT_VISIBLE:
 
842
            if (set) {
 
843
                sp_canvas_item_show(knot->item);
 
844
            } else {
 
845
                sp_canvas_item_hide(knot->item);
 
846
            }
 
847
            break;
 
848
        case SP_KNOT_MOUSEOVER:
 
849
        case SP_KNOT_DRAGGING:
 
850
            sp_knot_set_ctrl_state(knot);
 
851
            break;
 
852
        case SP_KNOT_GRABBED:
 
853
            break;
 
854
        default:
 
855
            g_assert_not_reached();
 
856
            break;
 
857
    }
 
858
}
 
859
 
 
860
/**
 
861
 * Update knot's pixbuf and set its control state.
 
862
 */
 
863
static void sp_knot_update_ctrl(SPKnot *knot)
 
864
{
 
865
    if (!knot->item) {
 
866
        return;
 
867
    }
 
868
    
 
869
    gtk_object_set(GTK_OBJECT(knot->item), "shape", knot->shape, NULL);
 
870
    gtk_object_set(GTK_OBJECT(knot->item), "mode", knot->mode, NULL);
 
871
    gtk_object_set(GTK_OBJECT(knot->item), "size", (gdouble) knot->size, NULL);
 
872
    gtk_object_set(GTK_OBJECT(knot->item), "anchor", knot->anchor, NULL);
 
873
    if (knot->pixbuf) {
 
874
        gtk_object_set(GTK_OBJECT (knot->item), "pixbuf", knot->pixbuf, NULL);
 
875
    }
 
876
 
 
877
    sp_knot_set_ctrl_state(knot);
 
878
}
 
879
 
 
880
/**
 
881
 * Set knot control state (dragging/mouseover/normal).
 
882
 */
 
883
static void sp_knot_set_ctrl_state(SPKnot *knot)
 
884
{
 
885
    if (knot->flags & SP_KNOT_DRAGGING) {
 
886
        gtk_object_set(GTK_OBJECT (knot->item),
 
887
                       "fill_color",
 
888
                       knot->fill[SP_KNOT_STATE_DRAGGING],
 
889
                       NULL);
 
890
        gtk_object_set(GTK_OBJECT (knot->item),
 
891
                       "stroke_color",
 
892
                       knot->stroke[SP_KNOT_STATE_DRAGGING],
 
893
                       NULL);
 
894
    } else if (knot->flags & SP_KNOT_MOUSEOVER) {
 
895
        gtk_object_set(GTK_OBJECT(knot->item),
 
896
                       "fill_color",
 
897
                       knot->fill[SP_KNOT_STATE_MOUSEOVER],
 
898
                       NULL);
 
899
        gtk_object_set(GTK_OBJECT(knot->item),
 
900
                       "stroke_color",
 
901
                       knot->stroke[SP_KNOT_STATE_MOUSEOVER],
 
902
                       NULL);
 
903
    } else {
 
904
        gtk_object_set(GTK_OBJECT(knot->item),
 
905
                       "fill_color",
 
906
                        knot->fill[SP_KNOT_STATE_NORMAL],
 
907
                       NULL);
 
908
        gtk_object_set(GTK_OBJECT(knot->item),
 
909
                       "stroke_color",
 
910
                       knot->stroke[SP_KNOT_STATE_NORMAL],
 
911
                       NULL);
 
912
    }
 
913
}
 
914
 
 
915
 
 
916
 
 
917
/*
 
918
  Local Variables:
 
919
  mode:c++
 
920
  c-file-style:"stroustrup"
 
921
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
922
  indent-tabs-mode:nil
 
923
  fill-column:99
 
924
  End:
 
925
*/
 
926
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :