1 /*
2 * LPEToolContext: a context for a generic tool composed of subtools that are given by LPEs
3 *
4 * Authors:
5 * Maximilian Albert <maximilian.albert@gmail.com>
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 *
8 * Copyright (C) 1998 The Free Software Foundation
9 * Copyright (C) 1999-2005 authors
10 * Copyright (C) 2001-2002 Ximian, Inc.
11 * Copyright (C) 2008 Maximilian Albert
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #include "config.h"
18 #include "forward.h"
19 #include "pixmaps/cursor-pencil.xpm"
20 #include <gtk/gtk.h>
21 #include "desktop.h"
22 #include "message-context.h"
23 #include "prefs-utils.h"
24 #include "shape-editor.h"
25 #include "selection.h"
26 #include "desktop-handles.h"
28 /**
30 #include <gdk/gdkkeysyms.h>
31 #include <glibmm/i18n.h>
32 #include <string>
33 #include <cstring>
34 #include <numeric>
36 #include "svg/svg.h"
37 #include "display/canvas-bpath.h"
38 #include "display/bezier-utils.h"
40 #include <glib/gmem.h>
41 #include "macros.h"
42 #include "document.h"
43 #include "selection.h"
44 #include "desktop-events.h"
45 #include "desktop-handles.h"
46 #include "desktop-affine.h"
47 #include "desktop-style.h"
48 #include "xml/repr.h"
49 #include "context-fns.h"
50 #include "sp-item.h"
51 #include "inkscape.h"
52 #include "color.h"
53 #include "rubberband.h"
54 #include "splivarot.h"
55 #include "sp-item-group.h"
56 #include "sp-shape.h"
57 #include "sp-path.h"
58 #include "sp-text.h"
59 #include "display/canvas-bpath.h"
60 #include "display/canvas-arena.h"
61 #include "livarot/Shape.h"
62 #include <2geom/isnan.h>
63 #include <2geom/pathvector.h>
64 **/
66 #include "lpe-tool-context.h"
68 static void sp_lpetool_context_class_init(SPLPEToolContextClass *klass);
69 static void sp_lpetool_context_init(SPLPEToolContext *erc);
70 static void sp_lpetool_context_dispose(GObject *object);
72 static void sp_lpetool_context_setup(SPEventContext *ec);
73 static void sp_lpetool_context_set(SPEventContext *ec, gchar const *key, gchar const *val);
74 static gint sp_lpetool_context_root_handler(SPEventContext *ec, GdkEvent *event);
76 const int num_subtools = 4;
78 Inkscape::LivePathEffect::EffectType lpesubtools[] = {
79 Inkscape::LivePathEffect::LINE_SEGMENT,
80 Inkscape::LivePathEffect::ANGLE_BISECTOR,
81 Inkscape::LivePathEffect::CIRCLE_3PTS,
82 Inkscape::LivePathEffect::PERP_BISECTOR,
83 };
85 static SPPenContextClass *lpetool_parent_class = 0;
87 GType sp_lpetool_context_get_type(void)
88 {
89 static GType type = 0;
90 if (!type) {
91 GTypeInfo info = {
92 sizeof(SPLPEToolContextClass),
93 0, // base_init
94 0, // base_finalize
95 (GClassInitFunc)sp_lpetool_context_class_init,
96 0, // class_finalize
97 0, // class_data
98 sizeof(SPLPEToolContext),
99 0, // n_preallocs
100 (GInstanceInitFunc)sp_lpetool_context_init,
101 0 // value_table
102 };
103 type = g_type_register_static(SP_TYPE_PEN_CONTEXT, "SPLPEToolContext", &info, static_cast<GTypeFlags>(0));
104 }
105 return type;
106 }
108 static void
109 sp_lpetool_context_class_init(SPLPEToolContextClass *klass)
110 {
111 GObjectClass *object_class = (GObjectClass *) klass;
112 SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
114 lpetool_parent_class = (SPPenContextClass*)g_type_class_peek_parent(klass);
116 object_class->dispose = sp_lpetool_context_dispose;
118 event_context_class->setup = sp_lpetool_context_setup;
119 event_context_class->set = sp_lpetool_context_set;
120 event_context_class->root_handler = sp_lpetool_context_root_handler;
121 }
123 static void
124 sp_lpetool_context_init(SPLPEToolContext *lc)
125 {
126 /**
127 lc->NodeContextCpp::cursor_shape = cursor_pencil_xpm;
128 lc->NodeContextCpp::hot_x = 4;
129 lc->NodeContextCpp::hot_y = 4;
130 **/
131 lc->cursor_shape = cursor_pencil_xpm;
132 lc->hot_x = 4;
133 lc->hot_y = 4;
135 //lc->tool_state = LPETOOL_STATE_NODE;
136 }
138 static void
139 sp_lpetool_context_dispose(GObject *object)
140 {
141 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(object);
142 delete lc->shape_editor;
144 G_OBJECT_CLASS(lpetool_parent_class)->dispose(object);
145 }
147 static void
148 sp_lpetool_context_setup(SPEventContext *ec)
149 {
150 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
152 if (((SPEventContextClass *) lpetool_parent_class)->setup)
153 ((SPEventContextClass *) lpetool_parent_class)->setup(ec);
155 Inkscape::Selection *selection = sp_desktop_selection (ec->desktop);
156 SPItem *item = selection->singleItem();
158 //lc->my_nc = new NodeContextCpp(lc->desktop, lc->prefs_repr, lc->key);
159 lc->shape_editor = new ShapeEditor(ec->desktop);
161 // TODO temp force:
162 ec->enableSelectionCue();
164 if (item) {
165 lc->shape_editor->set_item(item, SH_NODEPATH);
166 lc->shape_editor->set_item(item, SH_KNOTHOLDER);
167 }
169 if (prefs_get_int_attribute("tools.lpetool", "selcue", 0) != 0) {
170 ec->enableSelectionCue();
171 }
173 lc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
175 lc->shape_editor->update_statusbar();
176 }
178 /**
179 \brief Callback that processes the "changed" signal on the selection;
180 destroys old and creates new nodepath and reassigns listeners to the new selected item's repr
181 */
182 void
183 sp_lpetool_context_selection_changed(Inkscape::Selection *selection, gpointer data)
184 {
185 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(data);
187 // TODO: update ShapeEditorsCollective instead
188 lc->shape_editor->unset_item(SH_NODEPATH);
189 lc->shape_editor->unset_item(SH_KNOTHOLDER);
190 SPItem *item = selection->singleItem();
191 lc->shape_editor->set_item(item, SH_NODEPATH);
192 lc->shape_editor->set_item(item, SH_KNOTHOLDER);
193 lc->shape_editor->update_statusbar();
194 }
196 static void
197 sp_lpetool_context_set(SPEventContext *ec, gchar const *key, gchar const *val)
198 {
199 // FIXME: how to set this correcly? the value from preferences-skeleton.h doesn't seem to get
200 // read (it wants to set drag = 1)
201 lpetool_parent_class->set(ec, key, "drag");
203 /**
204 //pass on up to parent class to handle common attributes.
205 if ( lpetool_parent_class->set ) {
206 lpetool_parent_class->set(ec, key, val);
207 }
208 **/
209 }
211 /**
212 void
213 sp_erc_update_toolbox (SPDesktop *desktop, const gchar *id, double value)
214 {
215 desktop->setToolboxAdjustmentValue (id, value);
216 }
217 **/
219 gint
220 sp_lpetool_context_root_handler(SPEventContext *event_context, GdkEvent *event)
221 {
222 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(event_context);
223 //SPDesktop *desktop = event_context->desktop;
225 //gint ret = FALSE;
226 bool ret = false;
228 if (sp_pen_context_has_waiting_LPE(lc)) {
229 // quit when we are waiting for a LPE to be applied
230 g_print ("LPETool has waiting LPE. We call the pen tool parent context and return\n");
232 if (((SPEventContextClass *) lpetool_parent_class)->root_handler) {
233 ret = ((SPEventContextClass *) lpetool_parent_class)->root_handler(event_context, event);
234 }
236 return ret;
237 }
239 switch (event->type) {
240 case GDK_BUTTON_PRESS:
241 if (event->button.button == 1 && !event_context->space_panning) {
242 // save drag origin
243 event_context->xp = (gint) event->button.x;
244 event_context->yp = (gint) event->button.y;
245 event_context->within_tolerance = true;
246 lc->shape_editor->cancel_hit();
248 using namespace Inkscape::LivePathEffect;
250 int mode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
251 EffectType type = lpesubtools[mode];
252 g_print ("Activating mode %d\n", mode);
254 // save drag origin
255 bool over_stroke = lc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), true);
256 g_print ("over_stroke: %s\n", over_stroke ? "true" : "false");
258 sp_pen_context_wait_for_LPE_mouse_clicks(lc, type, Effect::acceptsNumClicks(type));
260 // we pass the mouse click on to pen tool as the first click which it should collect
261 if (((SPEventContextClass *) lpetool_parent_class)->root_handler) {
262 ret = ((SPEventContextClass *) lpetool_parent_class)->root_handler(event_context, event);
263 }
265 ret = true;
266 break;
268 /**
269 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(dc);
271 NR::Point const button_w(event->button.x,
272 event->button.y);
273 NR::Point const button_dt(desktop->w2d(button_w));
275 if (Inkscape::have_viable_layer(desktop, dc->_message_context) == false) {
276 return TRUE;
277 }
279 sp_lpetool_reset(dc, button_dt);
280 sp_lpetool_extinput(dc, event);
281 sp_lpetool_apply(dc, button_dt);
282 dc->accumulated->reset();
283 if (dc->repr) {
284 dc->repr = NULL;
285 }
287 Inkscape::Rubberband::get()->start(desktop, button_dt);
288 Inkscape::Rubberband::get()->setMode(RUBBERBAND_MODE_TOUCHPATH);
290 // initialize first point
291 dc->npoints = 0;
293 sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
294 ( GDK_KEY_PRESS_MASK |
295 GDK_BUTTON_RELEASE_MASK |
296 GDK_POINTER_MOTION_MASK |
297 GDK_BUTTON_PRESS_MASK ),
298 NULL,
299 event->button.time);
301 ret = TRUE;
303 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
304 dc->is_drawing = true;
305 **/
306 }
307 break;
308 case GDK_MOTION_NOTIFY:
309 /**
310 {
311 NR::Point const motion_w(event->motion.x,
312 event->motion.y);
313 NR::Point motion_dt(desktop->w2d(motion_w));
314 sp_lpetool_extinput(dc, event);
316 dc->_message_context->clear();
318 if ( dc->is_drawing && (event->motion.state & GDK_BUTTON1_MASK) && !event_context->space_panning) {
319 dc->dragging = TRUE;
321 dc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Drawing</b> an lpetool stroke"));
323 if (!sp_lpetool_apply(dc, motion_dt)) {
324 ret = TRUE;
325 break;
326 }
328 if ( dc->cur != dc->last ) {
329 sp_lpetool_brush(dc);
330 g_assert( dc->npoints > 0 );
331 fit_and_split(dc, FALSE);
332 }
333 ret = TRUE;
334 }
335 Inkscape::Rubberband::get()->move(motion_dt);
336 }
337 **/
338 break;
341 case GDK_BUTTON_RELEASE:
342 /**
343 {
344 NR::Point const motion_w(event->button.x, event->button.y);
345 NR::Point const motion_dt(desktop->w2d(motion_w));
347 sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
348 sp_canvas_end_forced_full_redraws(desktop->canvas);
349 dc->is_drawing = false;
351 if (dc->dragging && event->button.button == 1 && !event_context->space_panning) {
352 dc->dragging = FALSE;
354 NR::Maybe<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
356 sp_lpetool_apply(dc, motion_dt);
358 // Remove all temporary line segments
359 while (dc->segments) {
360 gtk_object_destroy(GTK_OBJECT(dc->segments->data));
361 dc->segments = g_slist_remove(dc->segments, dc->segments->data);
362 }
364 // Create object
365 fit_and_split(dc, TRUE);
366 accumulate_lpetool(dc);
367 set_to_accumulated(dc); // performs document_done
369 // reset accumulated curve
370 dc->accumulated->reset();
372 clear_current(dc);
373 if (dc->repr) {
374 dc->repr = NULL;
375 }
377 Inkscape::Rubberband::get()->stop();
378 dc->_message_context->clear();
379 ret = TRUE;
380 }
381 break;
382 }
383 **/
385 case GDK_KEY_PRESS:
386 /**
387 switch (get_group0_keyval (&event->key)) {
388 case GDK_Up:
389 case GDK_KP_Up:
390 if (!MOD__CTRL_ONLY) {
391 dc->angle += 5.0;
392 if (dc->angle > 90.0)
393 dc->angle = 90.0;
394 sp_erc_update_toolbox (desktop, "lpetool-angle", dc->angle);
395 ret = TRUE;
396 }
397 break;
398 case GDK_Down:
399 case GDK_KP_Down:
400 if (!MOD__CTRL_ONLY) {
401 dc->angle -= 5.0;
402 if (dc->angle < -90.0)
403 dc->angle = -90.0;
404 sp_erc_update_toolbox (desktop, "lpetool-angle", dc->angle);
405 ret = TRUE;
406 }
407 break;
408 case GDK_Right:
409 case GDK_KP_Right:
410 if (!MOD__CTRL_ONLY) {
411 dc->width += 0.01;
412 if (dc->width > 1.0)
413 dc->width = 1.0;
414 sp_erc_update_toolbox (desktop, "altx-lpetool", dc->width * 100); // the same spinbutton is for alt+x
415 ret = TRUE;
416 }
417 break;
418 case GDK_Left:
419 case GDK_KP_Left:
420 if (!MOD__CTRL_ONLY) {
421 dc->width -= 0.01;
422 if (dc->width < 0.01)
423 dc->width = 0.01;
424 sp_erc_update_toolbox (desktop, "altx-lpetool", dc->width * 100);
425 ret = TRUE;
426 }
427 break;
428 case GDK_Home:
429 case GDK_KP_Home:
430 dc->width = 0.01;
431 sp_erc_update_toolbox (desktop, "altx-lpetool", dc->width * 100);
432 ret = TRUE;
433 break;
434 case GDK_End:
435 case GDK_KP_End:
436 dc->width = 1.0;
437 sp_erc_update_toolbox (desktop, "altx-lpetool", dc->width * 100);
438 ret = TRUE;
439 break;
440 case GDK_x:
441 case GDK_X:
442 if (MOD__ALT_ONLY) {
443 desktop->setToolboxFocusTo ("altx-lpetool");
444 ret = TRUE;
445 }
446 break;
447 case GDK_Escape:
448 Inkscape::Rubberband::get()->stop();
449 if (dc->is_drawing) {
450 // if drawing, cancel, otherwise pass it up for deselecting
451 lpetool_cancel (dc);
452 ret = TRUE;
453 }
454 break;
455 case GDK_z:
456 case GDK_Z:
457 if (MOD__CTRL_ONLY && dc->is_drawing) {
458 // if drawing, cancel, otherwise pass it up for undo
459 lpetool_cancel (dc);
460 ret = TRUE;
461 }
462 break;
463 default:
464 break;
465 }
466 **/
467 break;
469 case GDK_KEY_RELEASE:
470 /**
471 switch (get_group0_keyval(&event->key)) {
472 case GDK_Control_L:
473 case GDK_Control_R:
474 dc->_message_context->clear();
475 break;
476 default:
477 break;
478 }
479 **/
481 default:
482 break;
483 }
485 /**
486 if (!ret) {
487 if (((SPEventContextClass *) lpetool_parent_class)->root_handler) {
488 ret = ((SPEventContextClass *) lpetool_parent_class)->root_handler(event_context, event);
489 }
490 }
491 **/
493 return ret;
494 }
496 /*
497 Local Variables:
498 mode:c++
499 c-file-style:"stroustrup"
500 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
501 indent-tabs-mode:nil
502 fill-column:99
503 End:
504 */
505 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :