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 <libnr/nr-matrix-div.h>
25 class SPDesktop;
27 static void knot_clicked_handler (SPKnot *knot, guint state, gpointer data);
28 static void knot_moved_handler(SPKnot *knot, NR::Point const *p, guint state, gpointer data);
29 static void knot_ungrabbed_handler (SPKnot *knot, unsigned int state, SPKnotHolder *kh);
30 static void sp_knot_holder_class_init(SPKnotHolderClass *klass);
32 void sp_knot_holder_dispose(GObject *object);
34 #ifdef KNOT_HOLDER_DEBUG
36 static void sp_knot_holder_debug(GtkObject *object, gpointer data)
37 {
38 g_print("sp-knot-holder-debug: [type=%s] [data=%s]\n", gtk_type_name(GTK_OBJECT_TYPE(object)), (const gchar *) data);
39 }
40 #endif
42 static GObjectClass *parent_class;
44 /**
45 * Registers SPKnotHolder class and returns its type number.
46 */
47 GType sp_knot_holder_get_type()
48 {
49 static GType type = 0;
50 if (!type) {
51 GTypeInfo info = {
52 sizeof(SPKnotHolderClass),
53 NULL, /* base_init */
54 NULL, /* base_finalize */
55 (GClassInitFunc) sp_knot_holder_class_init,
56 NULL, /* class_finalize */
57 NULL, /* class_data */
58 sizeof (SPKnotHolder),
59 16, /* n_preallocs */
60 NULL,
61 NULL
62 };
63 type = g_type_register_static (G_TYPE_OBJECT, "SPKnotHolder", &info, (GTypeFlags) 0);
64 }
65 return type;
66 }
68 /**
69 * SPKnotHolder vtable initialization.
70 */
71 static void sp_knot_holder_class_init(SPKnotHolderClass *klass){
72 GObjectClass *object_class = (GObjectClass *) klass;
73 parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
74 object_class->dispose = sp_knot_holder_dispose;
75 }
77 SPKnotHolder *sp_knot_holder_new(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler)
78 {
79 Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
81 g_return_val_if_fail(desktop != NULL, NULL);
82 g_return_val_if_fail(item != NULL, NULL);
83 g_return_val_if_fail(SP_IS_ITEM(item), NULL);
85 SPKnotHolder *knot_holder = (SPKnotHolder*)g_object_new (SP_TYPE_KNOT_HOLDER, 0);
86 knot_holder->desktop = desktop;
87 knot_holder->item = item;
88 g_object_ref(G_OBJECT(item));
89 knot_holder->entity = NULL;
91 knot_holder->released = relhandler;
93 knot_holder->repr = repr;
94 knot_holder->local_change = FALSE;
96 #ifdef KNOT_HOLDER_DEBUG
97 g_signal_connect(G_OBJECT(desktop), "destroy", sp_knot_holder_debug, (gpointer) "SPKnotHolder::item");
98 #endif
100 return knot_holder;
101 }
103 void sp_knot_holder_dispose(GObject *object) {
104 SPKnotHolder *kh = G_TYPE_CHECK_INSTANCE_CAST((object), SP_TYPE_KNOT_HOLDER, SPKnotHolder);
106 g_object_unref(G_OBJECT(kh->item));
107 while (kh->entity) {
108 SPKnotHolderEntity *e = (SPKnotHolderEntity *) kh->entity->data;
109 g_signal_handler_disconnect(GTK_OBJECT (e->knot), e->_click_handler_id);
110 g_signal_handler_disconnect(GTK_OBJECT (e->knot), e->_ungrab_handler_id);
111 /* unref should call destroy */
112 g_object_unref(G_OBJECT(e->knot));
113 g_free(e);
114 kh->entity = g_slist_remove(kh->entity, e);
115 }
116 }
118 void sp_knot_holder_destroy(SPKnotHolder *kh) {
119 g_object_unref(G_OBJECT(kh));
120 }
122 void sp_knot_holder_add(
123 SPKnotHolder *knot_holder,
124 SPKnotHolderSetFunc knot_set,
125 SPKnotHolderGetFunc knot_get,
126 void (* knot_click) (SPItem *item, guint state),
127 const gchar *tip
128 )
129 {
130 sp_knot_holder_add_full(knot_holder, knot_set, knot_get, knot_click, SP_KNOT_SHAPE_DIAMOND, SP_KNOT_MODE_XOR, tip);
131 }
133 void sp_knot_holder_add_full(
134 SPKnotHolder *knot_holder,
135 SPKnotHolderSetFunc knot_set,
136 SPKnotHolderGetFunc knot_get,
137 void (* knot_click) (SPItem *item, guint state),
138 SPKnotShapeType shape,
139 SPKnotModeType mode,
140 const gchar *tip
141 )
142 {
143 g_return_if_fail(knot_holder != NULL);
144 g_return_if_fail(knot_set != NULL);
145 g_return_if_fail(knot_get != NULL);
147 SPItem *item = SP_ITEM(knot_holder->item);
149 /* create new SPKnotHolderEntry */
150 SPKnotHolderEntity *e = g_new(SPKnotHolderEntity, 1);
151 e->knot = sp_knot_new(knot_holder->desktop, tip);
152 e->knot_set = knot_set;
153 e->knot_get = knot_get;
154 if (knot_click) {
155 e->knot_click = knot_click;
156 } else {
157 e->knot_click = NULL;
158 }
160 g_object_set(G_OBJECT (e->knot->item), "shape", shape, NULL);
161 g_object_set(G_OBJECT (e->knot->item), "mode", mode, NULL);
163 // TODO: add a color argument
164 //e->knot->fill [SP_KNOT_STATE_NORMAL] = 0x00ff0000;
165 //g_object_set (G_OBJECT (e->knot->item), "fill_color", 0x00ff0000, NULL);
167 knot_holder->entity = g_slist_append(knot_holder->entity, e);
169 /* Move to current point. */
170 NR::Point dp = e->knot_get(item) * sp_item_i2d_affine(item);
171 sp_knot_set_position(e->knot, &dp, SP_KNOT_STATE_NORMAL);
173 e->handler_id = g_signal_connect(G_OBJECT(e->knot), "moved", G_CALLBACK(knot_moved_handler), knot_holder);
174 e->_click_handler_id = g_signal_connect(G_OBJECT(e->knot), "clicked", G_CALLBACK(knot_clicked_handler), knot_holder);
175 e->_ungrab_handler_id = g_signal_connect(G_OBJECT(e->knot), "ungrabbed", G_CALLBACK(knot_ungrabbed_handler), knot_holder);
177 #ifdef KNOT_HOLDER_DEBUG
178 g_signal_connect(ob, "destroy", sp_knot_holder_debug, "SPKnotHolder::knot");
179 #endif
180 sp_knot_show(e->knot);
181 }
183 /**
184 * \param p In desktop coordinates.
185 */
187 static void knotholder_update_knots(SPKnotHolder *knot_holder, SPItem *item)
188 {
189 NR::Matrix const i2d(sp_item_i2d_affine(item));
191 for (GSList *el = knot_holder->entity; el; el = el->next) {
192 SPKnotHolderEntity *e = (SPKnotHolderEntity *) el->data;
193 GObject *kob = G_OBJECT(e->knot);
195 NR::Point dp( e->knot_get(item) * i2d );
196 g_signal_handler_block(kob, e->handler_id);
197 sp_knot_set_position(e->knot, &dp, SP_KNOT_STATE_NORMAL);
198 g_signal_handler_unblock(kob, e->handler_id);
199 }
200 }
202 static void knot_clicked_handler(SPKnot *knot, guint state, gpointer data)
203 {
204 SPKnotHolder *knot_holder = (SPKnotHolder *) data;
205 SPItem *item = SP_ITEM (knot_holder->item);
207 g_object_ref(G_OBJECT(knot_holder));
208 for (GSList *el = knot_holder->entity; el; el = el->next) {
209 SPKnotHolderEntity *e = (SPKnotHolderEntity *) el->data;
210 if (e->knot == knot) {
211 if (e->knot_click) {
212 e->knot_click(item, state);
213 }
214 break;
215 }
216 }
218 if (SP_IS_SHAPE(item)) {
219 sp_shape_set_shape(SP_SHAPE(item));
220 }
222 knotholder_update_knots(knot_holder, item);
223 g_object_unref(G_OBJECT(knot_holder));
225 // for drag, this is done by ungrabbed_handler, but for click we must do it here
226 sp_document_done(SP_OBJECT_DOCUMENT(knot_holder->item));
227 }
229 static void knot_moved_handler(SPKnot *knot, NR::Point const *p, guint state, gpointer data)
230 {
231 SPKnotHolder *knot_holder = (SPKnotHolder *) data;
232 SPItem *item = SP_ITEM (knot_holder->item);
233 // this was a local change and the knotholder does not need to be recreated:
234 knot_holder->local_change = TRUE;
236 for (GSList *el = knot_holder->entity; el; el = el->next) {
237 SPKnotHolderEntity *e = (SPKnotHolderEntity *) el->data;
238 if (e->knot == knot) {
239 NR::Point const q = *p / sp_item_i2d_affine(item);
240 e->knot_set(item, q, e->knot->drag_origin / sp_item_i2d_affine(item), state);
241 break;
242 }
243 }
245 if (SP_IS_SHAPE (item)) {
246 sp_shape_set_shape(SP_SHAPE (item));
247 }
249 knotholder_update_knots(knot_holder, item);
250 }
252 static void knot_ungrabbed_handler(SPKnot *knot, unsigned int state, SPKnotHolder *kh)
253 {
254 if (kh->released) {
255 kh->released(kh->item);
256 } else {
257 SPObject *object = (SPObject *) kh->item;
258 object->updateRepr(object->repr, SP_OBJECT_WRITE_EXT);
259 sp_document_done(SP_OBJECT_DOCUMENT (object));
260 }
261 }
263 /*
264 Local Variables:
265 mode:c++
266 c-file-style:"stroustrup"
267 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
268 indent-tabs-mode:nil
269 fill-column:99
270 End:
271 */
272 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :