1 #define __KNOT_HOLDER_C__
3 /*
4 * Container for SPKnot visual handles
5 *
6 * Authors:
7 * Mitsuru Oka <oka326@parkcity.ne.jp>
8 * bulia byak <buliabyak@users.sf.net>
9 *
10 * Copyright (C) 2001-2005 authors
11 *
12 * Released under GNU GPL, read the file 'COPYING' for more information
13 */
15 #define noKNOT_HOLDER_DEBUG
18 #include "document.h"
19 #include "sp-shape.h"
20 #include "knot.h"
21 #include "knotholder.h"
22 #include "knot-holder-entity.h"
23 #include "rect-context.h"
24 #include "sp-rect.h"
25 #include "arc-context.h"
26 #include "sp-ellipse.h"
27 #include "star-context.h"
28 #include "sp-star.h"
29 #include "spiral-context.h"
30 #include "sp-spiral.h"
31 #include "sp-offset.h"
33 #include <libnr/nr-matrix-div.h>
34 #include <glibmm/i18n.h>
36 class SPDesktop;
38 static void knot_clicked_handler (SPKnot *knot, guint state, gpointer data);
39 static void knot_moved_handler(SPKnot *knot, NR::Point const *p, guint state, gpointer data);
40 static void knot_ungrabbed_handler (SPKnot *knot, unsigned int state, SPKnotHolder *kh);
41 static void sp_knot_holder_class_init(SPKnotHolderClass *klass);
43 void sp_knot_holder_dispose(GObject *object);
45 #ifdef KNOT_HOLDER_DEBUG
47 static void sp_knot_holder_debug(GtkObject *object, gpointer data)
48 {
49 g_print("sp-knot-holder-debug: [type=%s] [data=%s]\n", gtk_type_name(GTK_OBJECT_TYPE(object)), (const gchar *) data);
50 }
51 #endif
53 static GObjectClass *parent_class;
55 /**
56 * Registers SPKnotHolder class and returns its type number.
57 */
58 GType sp_knot_holder_get_type()
59 {
60 static GType type = 0;
61 if (!type) {
62 GTypeInfo info = {
63 sizeof(SPKnotHolderClass),
64 NULL, /* base_init */
65 NULL, /* base_finalize */
66 (GClassInitFunc) sp_knot_holder_class_init,
67 NULL, /* class_finalize */
68 NULL, /* class_data */
69 sizeof (SPKnotHolder),
70 16, /* n_preallocs */
71 NULL,
72 NULL
73 };
74 type = g_type_register_static (G_TYPE_OBJECT, "SPKnotHolder", &info, (GTypeFlags) 0);
75 }
76 return type;
77 }
79 /**
80 * SPKnotHolder vtable initialization.
81 */
82 static void sp_knot_holder_class_init(SPKnotHolderClass *klass){
83 parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
84 klass->dispose = sp_knot_holder_dispose;
85 }
87 SPKnotHolder *sp_knot_holder_new(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler)
88 {
89 Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
91 g_return_val_if_fail(desktop != NULL, NULL);
92 g_return_val_if_fail(item != NULL, NULL);
93 g_return_val_if_fail(SP_IS_ITEM(item), NULL);
95 SPKnotHolder *knot_holder = (SPKnotHolder*)g_object_new (SP_TYPE_KNOT_HOLDER, 0);
96 knot_holder->desktop = desktop;
97 knot_holder->item = item;
98 g_object_ref(G_OBJECT(item));
99 knot_holder->entity = NULL;
101 knot_holder->released = relhandler;
103 knot_holder->repr = repr;
104 knot_holder->local_change = FALSE;
106 #ifdef KNOT_HOLDER_DEBUG
107 g_signal_connect(G_OBJECT(desktop), "destroy", sp_knot_holder_debug, (gpointer) "SPKnotHolder::item");
108 #endif
110 return knot_holder;
111 }
113 void sp_knot_holder_dispose(GObject *object) {
114 SPKnotHolder *kh = G_TYPE_CHECK_INSTANCE_CAST((object), SP_TYPE_KNOT_HOLDER, SPKnotHolder);
116 g_object_unref(G_OBJECT(kh->item));
117 while (kh->entity) {
118 SPKnotHolderEntity *e = (SPKnotHolderEntity *) kh->entity->data;
119 g_signal_handler_disconnect(e->knot, e->_click_handler_id);
120 g_signal_handler_disconnect(e->knot, e->_ungrab_handler_id);
121 /* unref should call destroy */
122 g_object_unref(e->knot);
123 g_free(e);
124 kh->entity = g_slist_remove(kh->entity, e);
125 }
126 }
128 void sp_knot_holder_destroy(SPKnotHolder *kh) {
129 g_object_unref(kh);
130 }
132 void sp_knot_holder_add(
133 SPKnotHolder *knot_holder,
134 SPKnotHolderSetFunc knot_set,
135 SPKnotHolderGetFunc knot_get,
136 void (* knot_click) (SPItem *item, guint state),
137 const gchar *tip
138 )
139 {
140 sp_knot_holder_add_full(knot_holder, knot_set, knot_get, knot_click, SP_KNOT_SHAPE_DIAMOND, SP_KNOT_MODE_XOR, tip);
141 }
143 void sp_knot_holder_add_full(
144 SPKnotHolder *knot_holder,
145 SPKnotHolderSetFunc knot_set,
146 SPKnotHolderGetFunc knot_get,
147 void (* knot_click) (SPItem *item, guint state),
148 SPKnotShapeType shape,
149 SPKnotModeType mode,
150 const gchar *tip
151 )
152 {
153 g_return_if_fail(knot_holder != NULL);
154 g_return_if_fail(knot_set != NULL);
155 g_return_if_fail(knot_get != NULL);
157 SPItem *item = SP_ITEM(knot_holder->item);
159 /* create new SPKnotHolderEntry */
160 SPKnotHolderEntity *e = g_new(SPKnotHolderEntity, 1);
161 e->knot = sp_knot_new(knot_holder->desktop, tip);
162 e->knot_set = knot_set;
163 e->knot_get = knot_get;
164 if (knot_click) {
165 e->knot_click = knot_click;
166 } else {
167 e->knot_click = NULL;
168 }
170 g_object_set(G_OBJECT (e->knot->item), "shape", shape, NULL);
171 g_object_set(G_OBJECT (e->knot->item), "mode", mode, NULL);
173 // TODO: add a color argument
174 //e->knot->fill [SP_KNOT_STATE_NORMAL] = 0x00ff0000;
175 //g_object_set (G_OBJECT (e->knot->item), "fill_color", 0x00ff0000, NULL);
177 knot_holder->entity = g_slist_append(knot_holder->entity, e);
179 /* Move to current point. */
180 NR::Point dp = e->knot_get(item) * sp_item_i2d_affine(item);
181 sp_knot_set_position(e->knot, &dp, SP_KNOT_STATE_NORMAL);
183 e->handler_id = g_signal_connect(e->knot, "moved", G_CALLBACK(knot_moved_handler), knot_holder);
184 e->_click_handler_id = g_signal_connect(e->knot, "clicked", G_CALLBACK(knot_clicked_handler), knot_holder);
185 e->_ungrab_handler_id = g_signal_connect(e->knot, "ungrabbed", G_CALLBACK(knot_ungrabbed_handler), knot_holder);
187 #ifdef KNOT_HOLDER_DEBUG
188 g_signal_connect(ob, "destroy", sp_knot_holder_debug, "SPKnotHolder::knot");
189 #endif
190 sp_knot_show(e->knot);
191 }
193 /**
194 * \param p In desktop coordinates.
195 */
197 static void knotholder_update_knots(SPKnotHolder *knot_holder, SPItem *item)
198 {
199 NR::Matrix const i2d(sp_item_i2d_affine(item));
201 for (GSList *el = knot_holder->entity; el; el = el->next) {
202 SPKnotHolderEntity *e = (SPKnotHolderEntity *) el->data;
203 GObject *kob = e->knot;
205 NR::Point dp( e->knot_get(item) * i2d );
206 g_signal_handler_block(kob, e->handler_id);
207 sp_knot_set_position(e->knot, &dp, SP_KNOT_STATE_NORMAL);
208 g_signal_handler_unblock(kob, e->handler_id);
209 }
210 }
212 static void knot_clicked_handler(SPKnot *knot, guint state, gpointer data)
213 {
214 SPKnotHolder *knot_holder = (SPKnotHolder *) data;
215 SPItem *item = SP_ITEM (knot_holder->item);
217 g_object_ref(knot_holder);
218 for (GSList *el = knot_holder->entity; el; el = el->next) {
219 SPKnotHolderEntity *e = (SPKnotHolderEntity *) el->data;
220 if (e->knot == knot) {
221 if (e->knot_click) {
222 e->knot_click(item, state);
223 }
224 break;
225 }
226 }
228 if (SP_IS_SHAPE(item)) {
229 sp_shape_set_shape(SP_SHAPE(item));
230 }
232 knotholder_update_knots(knot_holder, item);
233 g_object_unref(knot_holder);
235 unsigned int object_verb = SP_VERB_NONE;
237 if (SP_IS_RECT(item))
238 object_verb = SP_VERB_CONTEXT_RECT;
239 else if (SP_IS_GENERICELLIPSE(item))
240 object_verb = SP_VERB_CONTEXT_ARC;
241 else if (SP_IS_STAR(item))
242 object_verb = SP_VERB_CONTEXT_STAR;
243 else if (SP_IS_SPIRAL(item))
244 object_verb = SP_VERB_CONTEXT_SPIRAL;
245 else if (SP_IS_OFFSET(item)) {
246 if (SP_OFFSET(item)->sourceHref)
247 object_verb = SP_VERB_SELECTION_LINKED_OFFSET;
248 else
249 object_verb = SP_VERB_SELECTION_DYNAMIC_OFFSET;
250 }
252 // for drag, this is done by ungrabbed_handler, but for click we must do it here
253 sp_document_done(SP_OBJECT_DOCUMENT(knot_holder->item), object_verb,
254 _("Change handle"));
255 }
257 static void knot_moved_handler(SPKnot *knot, NR::Point const *p, guint state, gpointer data)
258 {
259 SPKnotHolder *knot_holder = (SPKnotHolder *) data;
260 SPItem *item = SP_ITEM (knot_holder->item);
261 // this was a local change and the knotholder does not need to be recreated:
262 knot_holder->local_change = TRUE;
264 for (GSList *el = knot_holder->entity; el; el = el->next) {
265 SPKnotHolderEntity *e = (SPKnotHolderEntity *) el->data;
266 if (e->knot == knot) {
267 NR::Point const q = *p / sp_item_i2d_affine(item);
268 e->knot_set(item, q, e->knot->drag_origin / sp_item_i2d_affine(item), state);
269 break;
270 }
271 }
273 if (SP_IS_SHAPE (item)) {
274 sp_shape_set_shape(SP_SHAPE (item));
275 }
277 knotholder_update_knots(knot_holder, item);
278 }
280 static void knot_ungrabbed_handler(SPKnot *knot, unsigned int state, SPKnotHolder *kh)
281 {
282 if (kh->released) {
283 kh->released(kh->item);
284 } else {
285 SPObject *object = (SPObject *) kh->item;
286 object->updateRepr(object->repr, SP_OBJECT_WRITE_EXT);
288 unsigned int object_verb = SP_VERB_NONE;
290 if (SP_IS_RECT(object))
291 object_verb = SP_VERB_CONTEXT_RECT;
292 else if (SP_IS_GENERICELLIPSE(object))
293 object_verb = SP_VERB_CONTEXT_ARC;
294 else if (SP_IS_STAR(object))
295 object_verb = SP_VERB_CONTEXT_STAR;
296 else if (SP_IS_SPIRAL(object))
297 object_verb = SP_VERB_CONTEXT_SPIRAL;
298 else if (SP_IS_OFFSET(object)) {
299 if (SP_OFFSET(object)->sourceHref)
300 object_verb = SP_VERB_SELECTION_LINKED_OFFSET;
301 else
302 object_verb = SP_VERB_SELECTION_DYNAMIC_OFFSET;
303 }
305 sp_document_done(SP_OBJECT_DOCUMENT (object), object_verb,
306 _("Move handle"));
307 }
308 }
310 /*
311 Local Variables:
312 mode:c++
313 c-file-style:"stroustrup"
314 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
315 indent-tabs-mode:nil
316 fill-column:99
317 End:
318 */
319 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :