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"
30 #include "document.h"
31 #include "display/curve.h"
32 #include "display/canvas-bpath.h"
33 #include "message-stack.h"
35 #include "lpe-tool-context.h"
37 static void sp_lpetool_context_class_init(SPLPEToolContextClass *klass);
38 static void sp_lpetool_context_init(SPLPEToolContext *erc);
39 static void sp_lpetool_context_dispose(GObject *object);
41 static void sp_lpetool_context_setup(SPEventContext *ec);
42 static void sp_lpetool_context_set(SPEventContext *ec, gchar const *key, gchar const *val);
43 static gint sp_lpetool_context_root_handler(SPEventContext *ec, GdkEvent *event);
45 void sp_lpetool_context_selection_changed(Inkscape::Selection *selection, gpointer data);
47 const int num_subtools = 7;
49 Inkscape::LivePathEffect::EffectType lpesubtools[] = {
50 Inkscape::LivePathEffect::INVALID_LPE, // this must be here to account for the "all inactive" action
51 Inkscape::LivePathEffect::LINE_SEGMENT,
52 Inkscape::LivePathEffect::CIRCLE_3PTS,
53 Inkscape::LivePathEffect::CIRCLE_WITH_RADIUS,
54 Inkscape::LivePathEffect::PARALLEL,
55 Inkscape::LivePathEffect::PERP_BISECTOR,
56 Inkscape::LivePathEffect::ANGLE_BISECTOR,
57 };
59 static SPPenContextClass *lpetool_parent_class = 0;
61 GType sp_lpetool_context_get_type(void)
62 {
63 static GType type = 0;
64 if (!type) {
65 GTypeInfo info = {
66 sizeof(SPLPEToolContextClass),
67 0, // base_init
68 0, // base_finalize
69 (GClassInitFunc)sp_lpetool_context_class_init,
70 0, // class_finalize
71 0, // class_data
72 sizeof(SPLPEToolContext),
73 0, // n_preallocs
74 (GInstanceInitFunc)sp_lpetool_context_init,
75 0 // value_table
76 };
77 type = g_type_register_static(SP_TYPE_PEN_CONTEXT, "SPLPEToolContext", &info, static_cast<GTypeFlags>(0));
78 }
79 return type;
80 }
82 static void
83 sp_lpetool_context_class_init(SPLPEToolContextClass *klass)
84 {
85 GObjectClass *object_class = (GObjectClass *) klass;
86 SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
88 lpetool_parent_class = (SPPenContextClass*)g_type_class_peek_parent(klass);
90 object_class->dispose = sp_lpetool_context_dispose;
92 event_context_class->setup = sp_lpetool_context_setup;
93 event_context_class->set = sp_lpetool_context_set;
94 event_context_class->root_handler = sp_lpetool_context_root_handler;
95 }
97 static void
98 sp_lpetool_context_init(SPLPEToolContext *lc)
99 {
100 lc->cursor_shape = cursor_crosshairs_xpm;
101 lc->hot_x = 7;
102 lc->hot_y = 7;
104 lc->canvas_bbox = NULL;
106 new (&lc->sel_changed_connection) sigc::connection();
107 }
109 static void
110 sp_lpetool_context_dispose(GObject *object)
111 {
112 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(object);
113 delete lc->shape_editor;
115 if (lc->canvas_bbox) {
116 gtk_object_destroy(GTK_OBJECT(lc->canvas_bbox));
117 lc->canvas_bbox = NULL;
118 }
120 lc->sel_changed_connection.disconnect();
121 lc->sel_changed_connection.~connection();
123 G_OBJECT_CLASS(lpetool_parent_class)->dispose(object);
124 }
126 static void
127 sp_lpetool_context_setup(SPEventContext *ec)
128 {
129 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
131 if (((SPEventContextClass *) lpetool_parent_class)->setup)
132 ((SPEventContextClass *) lpetool_parent_class)->setup(ec);
134 Inkscape::Selection *selection = sp_desktop_selection (ec->desktop);
135 SPItem *item = selection->singleItem();
137 lc->sel_changed_connection.disconnect();
138 lc->sel_changed_connection =
139 selection->connectChanged(sigc::bind(sigc::ptr_fun(&sp_lpetool_context_selection_changed), (gpointer)lc));
141 lc->shape_editor = new ShapeEditor(ec->desktop);
143 lpetool_context_switch_mode(lc, Inkscape::LivePathEffect::INVALID_LPE);
144 lpetool_context_reset_limiting_bbox(lc);
146 // TODO temp force:
147 ec->enableSelectionCue();
149 if (item) {
150 lc->shape_editor->set_item(item, SH_NODEPATH);
151 lc->shape_editor->set_item(item, SH_KNOTHOLDER);
152 }
154 if (prefs_get_int_attribute("tools.lpetool", "selcue", 0) != 0) {
155 ec->enableSelectionCue();
156 }
158 lc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
160 lc->shape_editor->update_statusbar();
161 }
163 /**
164 \brief Callback that processes the "changed" signal on the selection;
165 destroys old and creates new nodepath and reassigns listeners to the new selected item's repr
166 */
167 void
168 sp_lpetool_context_selection_changed(Inkscape::Selection *selection, gpointer data)
169 {
170 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(data);
172 // TODO: update ShapeEditorsCollective instead
173 lc->shape_editor->unset_item(SH_NODEPATH);
174 lc->shape_editor->unset_item(SH_KNOTHOLDER);
175 SPItem *item = selection->singleItem();
176 lc->shape_editor->set_item(item, SH_NODEPATH);
177 lc->shape_editor->set_item(item, SH_KNOTHOLDER);
178 lc->shape_editor->update_statusbar();
179 }
181 static void
182 sp_lpetool_context_set(SPEventContext *ec, gchar const *key, gchar const *val)
183 {
184 // FIXME: how to set this correcly? the value from preferences-skeleton.h doesn't seem to get
185 // read (it wants to set drag = 1)
186 lpetool_parent_class->set(ec, key, "drag");
188 /**
189 //pass on up to parent class to handle common attributes.
190 if ( lpetool_parent_class->set ) {
191 lpetool_parent_class->set(ec, key, val);
192 }
193 **/
194 }
196 gint
197 sp_lpetool_context_root_handler(SPEventContext *event_context, GdkEvent *event)
198 {
199 //g_print ("sp_lpetool_context_root_handler()\n");
200 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(event_context);
201 SPDesktop *desktop = event_context->desktop;
202 Inkscape::Selection *selection = sp_desktop_selection (desktop);
204 bool ret = false;
206 if (sp_pen_context_has_waiting_LPE(lc)) {
207 // quit when we are waiting for a LPE to be applied
208 g_print ("LPETool has waiting LPE. We call the pen tool parent context and return\n");
209 ret = ((SPEventContextClass *) lpetool_parent_class)->root_handler(event_context, event);
210 return ret;
211 }
213 switch (event->type) {
214 case GDK_BUTTON_PRESS:
215 g_print ("GDK_BUTTON_PRESS\n");
216 if (lc->mode == Inkscape::LivePathEffect::INVALID_LPE) {
217 // don't do anything for now if we are inactive
218 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Choose a construction tool from the toolbar."));
219 g_print ("Flash statusbar\n");
220 ret = true;
221 break;
222 }
224 if (event->button.button == 1 && !event_context->space_panning) {
225 g_print (" ... (passed if construct)\n");
226 // save drag origin
227 event_context->xp = (gint) event->button.x;
228 event_context->yp = (gint) event->button.y;
229 event_context->within_tolerance = true;
230 lc->shape_editor->cancel_hit();
232 using namespace Inkscape::LivePathEffect;
234 int mode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
235 EffectType type = lpesubtools[mode];
236 g_print ("Activating mode %d\n", mode);
238 // save drag origin
239 bool over_stroke = lc->shape_editor->is_over_stroke(NR::Point(event->button.x, event->button.y), true);
240 g_print ("over_stroke: %s\n", over_stroke ? "true" : "false");
242 sp_pen_context_wait_for_LPE_mouse_clicks(lc, type, Inkscape::LivePathEffect::Effect::acceptsNumClicks(type));
244 // we pass the mouse click on to pen tool as the first click which it should collect
245 ret = ((SPEventContextClass *) lpetool_parent_class)->root_handler(event_context, event);
246 }
247 break;
248 case GDK_MOTION_NOTIFY:
249 {
250 if (!lc->shape_editor->has_nodepath() || selection->singleItem() == NULL) {
251 break;
252 }
254 bool over_stroke = false;
255 over_stroke = lc->shape_editor->is_over_stroke(NR::Point(event->motion.x, event->motion.y), false);
257 if (over_stroke) {
258 event_context->cursor_shape = cursor_node_xpm;
259 event_context->hot_x = 1;
260 event_context->hot_y = 1;
261 sp_event_context_update_cursor(event_context);
262 } else {
263 lc->cursor_shape = cursor_crosshairs_xpm;
264 lc->hot_x = 7;
265 lc->hot_y = 7;
266 sp_event_context_update_cursor(event_context);
267 }
268 }
269 break;
272 case GDK_BUTTON_RELEASE:
273 {
274 /**
275 break;
276 **/
277 }
279 case GDK_KEY_PRESS:
280 /**
281 switch (get_group0_keyval (&event->key)) {
282 }
283 break;
284 **/
286 case GDK_KEY_RELEASE:
287 /**
288 switch (get_group0_keyval(&event->key)) {
289 case GDK_Control_L:
290 case GDK_Control_R:
291 dc->_message_context->clear();
292 break;
293 default:
294 break;
295 }
296 **/
298 default:
299 break;
300 }
302 if (!ret) {
303 if (((SPEventContextClass *) lpetool_parent_class)->root_handler) {
304 ret = ((SPEventContextClass *) lpetool_parent_class)->root_handler(event_context, event);
305 }
306 }
308 return ret;
309 }
311 /*
312 * Finds the index in the list of geometric subtools corresponding to the given LPE type.
313 * Returns -1 if no subtool is found.
314 */
315 int
316 lpetool_mode_to_index(Inkscape::LivePathEffect::EffectType const type) {
317 for (int i = 0; i < num_subtools; ++i) {
318 if (lpesubtools[i] == type) {
319 return i;
320 }
321 }
322 return -1;
323 }
325 /*
326 * Attempts to perform the construction of the given type (i.e., to apply the corresponding LPE) to
327 * a single selected item. Returns whether we succeeded.
328 */
329 bool
330 lpetool_try_construction(SPLPEToolContext *lc, Inkscape::LivePathEffect::EffectType const type)
331 {
332 Inkscape::Selection *selection = sp_desktop_selection(lc->desktop);
333 SPItem *item = selection->singleItem();
335 // TODO: should we check whether type represents a valid geometric construction?
336 if (item && SP_IS_LPE_ITEM(item) && Inkscape::LivePathEffect::Effect::acceptsNumClicks(type) == 0) {
337 Inkscape::LivePathEffect::Effect::createAndApply(type, sp_desktop_document(lc->desktop), item);
338 return true;
339 }
340 return false;
341 }
343 void
344 lpetool_context_switch_mode(SPLPEToolContext *lc, Inkscape::LivePathEffect::EffectType const type)
345 {
346 int index = lpetool_mode_to_index(type);
347 if (index != -1) {
348 lc->mode = type;
349 lc->desktop->setToolboxSelectOneValue ("lpetool_mode_action", index);
350 } else {
351 g_warning ("Invalid mode selected: %d", type);
352 return;
353 }
354 }
356 /*
357 * Reads the limiting bounding box from preferences and draws it on the screen
358 */
359 // TODO: Note that currently the bbox is not user-settable; we simply use the page borders
360 void
361 lpetool_context_reset_limiting_bbox(SPLPEToolContext *lc)
362 {
363 if (lc->canvas_bbox) {
364 gtk_object_destroy(GTK_OBJECT(lc->canvas_bbox));
365 lc->canvas_bbox = NULL;
366 }
368 if (prefs_get_int_attribute("tools.lpetool", "show_bbox", 1) == 0)
369 return;
371 SPDocument *document = sp_desktop_document(lc->desktop);
372 Geom::Coord w = sp_document_width(document);
373 Geom::Coord h = sp_document_height(document);
375 Geom::Point A(0,0);
376 Geom::Point B(w,h);
378 Geom::Rect rect(A, B);
379 SPCurve *curve = SPCurve::new_from_rect(rect);
381 lc->canvas_bbox = sp_canvas_bpath_new (sp_desktop_controls(lc->desktop), curve);
382 sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(lc->canvas_bbox), 0x0000ffff, 0.8, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT, 5, 5);
383 }
385 /*
386 Local Variables:
387 mode:c++
388 c-file-style:"stroustrup"
389 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
390 indent-tabs-mode:nil
391 fill-column:99
392 End:
393 */
394 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :