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 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif
20 #include "forward.h"
21 #include "pixmaps/cursor-node.xpm"
22 #include "pixmaps/cursor-crosshairs.xpm"
23 #include <gtk/gtk.h>
24 #include "desktop.h"
25 #include "message-context.h"
26 #include "prefs-utils.h"
27 #include "shape-editor.h"
28 #include "selection.h"
29 #include "desktop-handles.h"
31 #include "lpe-tool-context.h"
33 static void sp_lpetool_context_class_init(SPLPEToolContextClass *klass);
34 static void sp_lpetool_context_init(SPLPEToolContext *erc);
35 static void sp_lpetool_context_dispose(GObject *object);
37 static void sp_lpetool_context_setup(SPEventContext *ec);
38 static void sp_lpetool_context_set(SPEventContext *ec, gchar const *key, gchar const *val);
39 static gint sp_lpetool_context_root_handler(SPEventContext *ec, GdkEvent *event);
41 void sp_lpetool_context_selection_changed(Inkscape::Selection *selection, gpointer data);
43 const int num_subtools = 4;
45 Inkscape::LivePathEffect::EffectType lpesubtools[] = {
46 Inkscape::LivePathEffect::LINE_SEGMENT,
47 Inkscape::LivePathEffect::ANGLE_BISECTOR,
48 Inkscape::LivePathEffect::CIRCLE_3PTS,
49 Inkscape::LivePathEffect::PERP_BISECTOR,
50 };
52 static SPPenContextClass *lpetool_parent_class = 0;
54 GType sp_lpetool_context_get_type(void)
55 {
56 static GType type = 0;
57 if (!type) {
58 GTypeInfo info = {
59 sizeof(SPLPEToolContextClass),
60 0, // base_init
61 0, // base_finalize
62 (GClassInitFunc)sp_lpetool_context_class_init,
63 0, // class_finalize
64 0, // class_data
65 sizeof(SPLPEToolContext),
66 0, // n_preallocs
67 (GInstanceInitFunc)sp_lpetool_context_init,
68 0 // value_table
69 };
70 type = g_type_register_static(SP_TYPE_PEN_CONTEXT, "SPLPEToolContext", &info, static_cast<GTypeFlags>(0));
71 }
72 return type;
73 }
75 static void
76 sp_lpetool_context_class_init(SPLPEToolContextClass *klass)
77 {
78 GObjectClass *object_class = (GObjectClass *) klass;
79 SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
81 lpetool_parent_class = (SPPenContextClass*)g_type_class_peek_parent(klass);
83 object_class->dispose = sp_lpetool_context_dispose;
85 event_context_class->setup = sp_lpetool_context_setup;
86 event_context_class->set = sp_lpetool_context_set;
87 event_context_class->root_handler = sp_lpetool_context_root_handler;
88 }
90 static void
91 sp_lpetool_context_init(SPLPEToolContext *lc)
92 {
93 lc->cursor_shape = cursor_crosshairs_xpm;
94 lc->hot_x = 7;
95 lc->hot_y = 7;
97 new (&lc->sel_changed_connection) sigc::connection();
99 //lc->tool_state = LPETOOL_STATE_NODE;
100 }
102 static void
103 sp_lpetool_context_dispose(GObject *object)
104 {
105 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(object);
106 delete lc->shape_editor;
108 lc->sel_changed_connection.disconnect();
109 lc->sel_changed_connection.~connection();
111 G_OBJECT_CLASS(lpetool_parent_class)->dispose(object);
112 }
114 static void
115 sp_lpetool_context_setup(SPEventContext *ec)
116 {
117 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
119 if (((SPEventContextClass *) lpetool_parent_class)->setup)
120 ((SPEventContextClass *) lpetool_parent_class)->setup(ec);
122 Inkscape::Selection *selection = sp_desktop_selection (ec->desktop);
123 SPItem *item = selection->singleItem();
125 lc->sel_changed_connection.disconnect();
126 lc->sel_changed_connection =
127 selection->connectChanged(sigc::bind(sigc::ptr_fun(&sp_lpetool_context_selection_changed), (gpointer)lc));
129 //lc->my_nc = new NodeContextCpp(lc->desktop, lc->prefs_repr, lc->key);
130 lc->shape_editor = new ShapeEditor(ec->desktop);
132 // TODO temp force:
133 ec->enableSelectionCue();
135 if (item) {
136 lc->shape_editor->set_item(item, SH_NODEPATH);
137 lc->shape_editor->set_item(item, SH_KNOTHOLDER);
138 }
140 if (prefs_get_int_attribute("tools.lpetool", "selcue", 0) != 0) {
141 ec->enableSelectionCue();
142 }
144 lc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
146 lc->shape_editor->update_statusbar();
147 }
149 /**
150 \brief Callback that processes the "changed" signal on the selection;
151 destroys old and creates new nodepath and reassigns listeners to the new selected item's repr
152 */
153 void
154 sp_lpetool_context_selection_changed(Inkscape::Selection *selection, gpointer data)
155 {
156 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(data);
158 // TODO: update ShapeEditorsCollective instead
159 lc->shape_editor->unset_item(SH_NODEPATH);
160 lc->shape_editor->unset_item(SH_KNOTHOLDER);
161 SPItem *item = selection->singleItem();
162 lc->shape_editor->set_item(item, SH_NODEPATH);
163 lc->shape_editor->set_item(item, SH_KNOTHOLDER);
164 lc->shape_editor->update_statusbar();
165 }
167 static void
168 sp_lpetool_context_set(SPEventContext *ec, gchar const *key, gchar const *val)
169 {
170 // FIXME: how to set this correcly? the value from preferences-skeleton.h doesn't seem to get
171 // read (it wants to set drag = 1)
172 lpetool_parent_class->set(ec, key, "drag");
174 /**
175 //pass on up to parent class to handle common attributes.
176 if ( lpetool_parent_class->set ) {
177 lpetool_parent_class->set(ec, key, val);
178 }
179 **/
180 }
182 /**
183 void
184 sp_erc_update_toolbox (SPDesktop *desktop, const gchar *id, double value)
185 {
186 desktop->setToolboxAdjustmentValue (id, value);
187 }
188 **/
190 gint
191 sp_lpetool_context_root_handler(SPEventContext *event_context, GdkEvent *event)
192 {
193 //g_print ("sp_lpetool_context_root_handler()\n");
194 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(event_context);
195 SPDesktop *desktop = event_context->desktop;
196 Inkscape::Selection *selection = sp_desktop_selection (desktop);
198 //gint ret = FALSE;
199 bool ret = false;
201 if (sp_pen_context_has_waiting_LPE(lc)) {
202 // quit when we are waiting for a LPE to be applied
203 g_print ("LPETool has waiting LPE. We call the pen tool parent context and return\n");
204 ret = ((SPEventContextClass *) lpetool_parent_class)->root_handler(event_context, event);
205 return ret;
206 }
208 switch (event->type) {
209 case GDK_BUTTON_PRESS:
210 g_print ("GDK_BUTTON_PRESS\n");
211 if (event->button.button == 1 && !event_context->space_panning) {
212 g_print (" ... (passed if construct)\n");
213 // save drag origin
214 event_context->xp = (gint) event->button.x;
215 event_context->yp = (gint) event->button.y;
216 event_context->within_tolerance = true;
217 lc->shape_editor->cancel_hit();
219 using namespace Inkscape::LivePathEffect;
221 int mode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
222 EffectType type = lpesubtools[mode];
223 g_print ("Activating mode %d\n", mode);
225 // save drag origin
226 bool over_stroke = lc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), true);
227 g_print ("over_stroke: %s\n", over_stroke ? "true" : "false");
229 sp_pen_context_wait_for_LPE_mouse_clicks(lc, type, Effect::acceptsNumClicks(type));
231 // we pass the mouse click on to pen tool as the first click which it should collect
232 ret = ((SPEventContextClass *) lpetool_parent_class)->root_handler(event_context, event);
234 /**
235 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(dc);
237 NR::Point const button_w(event->button.x,
238 event->button.y);
239 NR::Point const button_dt(desktop->w2d(button_w));
241 if (Inkscape::have_viable_layer(desktop, dc->_message_context) == false) {
242 return TRUE;
243 }
245 sp_lpetool_reset(dc, button_dt);
246 sp_lpetool_extinput(dc, event);
247 sp_lpetool_apply(dc, button_dt);
248 dc->accumulated->reset();
249 if (dc->repr) {
250 dc->repr = NULL;
251 }
253 Inkscape::Rubberband::get()->start(desktop, button_dt);
254 Inkscape::Rubberband::get()->setMode(RUBBERBAND_MODE_TOUCHPATH);
256 // initialize first point
257 dc->npoints = 0;
259 sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
260 ( GDK_KEY_PRESS_MASK |
261 GDK_BUTTON_RELEASE_MASK |
262 GDK_POINTER_MOTION_MASK |
263 GDK_BUTTON_PRESS_MASK ),
264 NULL,
265 event->button.time);
267 ret = TRUE;
269 sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
270 dc->is_drawing = true;
271 **/
272 }
273 break;
274 case GDK_MOTION_NOTIFY:
275 {
276 if (!lc->shape_editor->has_nodepath() || selection->singleItem() == NULL) {
277 break;
278 }
280 bool over_stroke = false;
281 over_stroke = lc->shape_editor->is_over_stroke(NR::Point(event->motion.x, event->motion.y), false);
283 if (over_stroke) {
284 event_context->cursor_shape = cursor_node_xpm;
285 event_context->hot_x = 1;
286 event_context->hot_y = 1;
287 sp_event_context_update_cursor(event_context);
288 } else {
289 lc->cursor_shape = cursor_crosshairs_xpm;
290 lc->hot_x = 7;
291 lc->hot_y = 7;
292 sp_event_context_update_cursor(event_context);
293 }
294 /**
295 {
296 NR::Point const motion_w(event->motion.x,
297 event->motion.y);
298 NR::Point motion_dt(desktop->w2d(motion_w));
299 sp_lpetool_extinput(dc, event);
301 dc->_message_context->clear();
303 if ( dc->is_drawing && (event->motion.state & GDK_BUTTON1_MASK) && !event_context->space_panning) {
304 dc->dragging = TRUE;
306 dc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Drawing</b> an lpetool stroke"));
308 if (!sp_lpetool_apply(dc, motion_dt)) {
309 ret = TRUE;
310 break;
311 }
313 if ( dc->cur != dc->last ) {
314 sp_lpetool_brush(dc);
315 g_assert( dc->npoints > 0 );
316 fit_and_split(dc, FALSE);
317 }
318 ret = TRUE;
319 }
320 Inkscape::Rubberband::get()->move(motion_dt);
321 }
322 **/
323 }
324 break;
327 case GDK_BUTTON_RELEASE:
328 /**
329 {
330 NR::Point const motion_w(event->button.x, event->button.y);
331 NR::Point const motion_dt(desktop->w2d(motion_w));
333 sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
334 sp_canvas_end_forced_full_redraws(desktop->canvas);
335 dc->is_drawing = false;
337 if (dc->dragging && event->button.button == 1 && !event_context->space_panning) {
338 dc->dragging = FALSE;
340 NR::Maybe<NR::Rect> const b = Inkscape::Rubberband::get()->getRectangle();
342 sp_lpetool_apply(dc, motion_dt);
344 // Remove all temporary line segments
345 while (dc->segments) {
346 gtk_object_destroy(GTK_OBJECT(dc->segments->data));
347 dc->segments = g_slist_remove(dc->segments, dc->segments->data);
348 }
350 // Create object
351 fit_and_split(dc, TRUE);
352 accumulate_lpetool(dc);
353 set_to_accumulated(dc); // performs document_done
355 // reset accumulated curve
356 dc->accumulated->reset();
358 clear_current(dc);
359 if (dc->repr) {
360 dc->repr = NULL;
361 }
363 Inkscape::Rubberband::get()->stop();
364 dc->_message_context->clear();
365 ret = TRUE;
366 }
367 break;
368 }
369 **/
371 case GDK_KEY_PRESS:
372 /**
373 switch (get_group0_keyval (&event->key)) {
374 case GDK_Up:
375 case GDK_KP_Up:
376 if (!MOD__CTRL_ONLY) {
377 dc->angle += 5.0;
378 if (dc->angle > 90.0)
379 dc->angle = 90.0;
380 sp_erc_update_toolbox (desktop, "lpetool-angle", dc->angle);
381 ret = TRUE;
382 }
383 break;
384 case GDK_Down:
385 case GDK_KP_Down:
386 if (!MOD__CTRL_ONLY) {
387 dc->angle -= 5.0;
388 if (dc->angle < -90.0)
389 dc->angle = -90.0;
390 sp_erc_update_toolbox (desktop, "lpetool-angle", dc->angle);
391 ret = TRUE;
392 }
393 break;
394 case GDK_Right:
395 case GDK_KP_Right:
396 if (!MOD__CTRL_ONLY) {
397 dc->width += 0.01;
398 if (dc->width > 1.0)
399 dc->width = 1.0;
400 sp_erc_update_toolbox (desktop, "altx-lpetool", dc->width * 100); // the same spinbutton is for alt+x
401 ret = TRUE;
402 }
403 break;
404 case GDK_Left:
405 case GDK_KP_Left:
406 if (!MOD__CTRL_ONLY) {
407 dc->width -= 0.01;
408 if (dc->width < 0.01)
409 dc->width = 0.01;
410 sp_erc_update_toolbox (desktop, "altx-lpetool", dc->width * 100);
411 ret = TRUE;
412 }
413 break;
414 case GDK_Home:
415 case GDK_KP_Home:
416 dc->width = 0.01;
417 sp_erc_update_toolbox (desktop, "altx-lpetool", dc->width * 100);
418 ret = TRUE;
419 break;
420 case GDK_End:
421 case GDK_KP_End:
422 dc->width = 1.0;
423 sp_erc_update_toolbox (desktop, "altx-lpetool", dc->width * 100);
424 ret = TRUE;
425 break;
426 case GDK_x:
427 case GDK_X:
428 if (MOD__ALT_ONLY) {
429 desktop->setToolboxFocusTo ("altx-lpetool");
430 ret = TRUE;
431 }
432 break;
433 case GDK_Escape:
434 Inkscape::Rubberband::get()->stop();
435 if (dc->is_drawing) {
436 // if drawing, cancel, otherwise pass it up for deselecting
437 lpetool_cancel (dc);
438 ret = TRUE;
439 }
440 break;
441 case GDK_z:
442 case GDK_Z:
443 if (MOD__CTRL_ONLY && dc->is_drawing) {
444 // if drawing, cancel, otherwise pass it up for undo
445 lpetool_cancel (dc);
446 ret = TRUE;
447 }
448 break;
449 default:
450 break;
451 }
452 **/
453 break;
455 case GDK_KEY_RELEASE:
456 /**
457 switch (get_group0_keyval(&event->key)) {
458 case GDK_Control_L:
459 case GDK_Control_R:
460 dc->_message_context->clear();
461 break;
462 default:
463 break;
464 }
465 **/
467 default:
468 break;
469 }
471 if (!ret) {
472 if (((SPEventContextClass *) lpetool_parent_class)->root_handler) {
473 ret = ((SPEventContextClass *) lpetool_parent_class)->root_handler(event_context, event);
474 }
475 }
477 return ret;
478 }
480 /*
481 Local Variables:
482 mode:c++
483 c-file-style:"stroustrup"
484 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
485 indent-tabs-mode:nil
486 fill-column:99
487 End:
488 */
489 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :