Code

26d60601c58d002ff2c4767f1a35af0341ddb52d
[inkscape.git] / src / sp-guide.cpp
1 #define __SP_GUIDE_C__
3 /*
4  * Inkscape guideline implementation
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Peter Moulder <pmoulder@mail.csse.monash.edu.au>
9  *   Johan Engelen
10  *
11  * Copyright (C) 2000-2002 authors
12  * Copyright (C) 2004 Monash University
13  * Copyright (C) 2007 Johan Engelen
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include "display/guideline.h"
22 #include "svg/svg.h"
23 #include "svg/stringstream.h"
24 #include "attributes.h"
25 #include "sp-guide.h"
26 #include <sp-item-notify-moveto.h>
27 #include <sp-item.h>
28 #include <sp-guide-constraint.h>
29 #include <glibmm/i18n.h>
30 #include <xml/repr.h>
31 #include <remove-last.h>
32 #include "sp-metrics.h"
33 #include "inkscape.h"
34 #include "desktop.h"
35 #include "sp-namedview.h"
36 #include <2geom/angle.h>
38 using std::vector;
40 enum {
41     PROP_0,
42     PROP_COLOR,
43     PROP_HICOLOR
44 };
46 static void sp_guide_class_init(SPGuideClass *gc);
47 static void sp_guide_init(SPGuide *guide);
48 static void sp_guide_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
49 static void sp_guide_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
51 static void sp_guide_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
52 static void sp_guide_release(SPObject *object);
53 static void sp_guide_set(SPObject *object, unsigned int key, const gchar *value);
55 static SPObjectClass *parent_class;
57 GType sp_guide_get_type(void)
58 {
59     static GType guide_type = 0;
61     if (!guide_type) {
62         GTypeInfo guide_info = {
63             sizeof(SPGuideClass),
64             NULL, NULL,
65             (GClassInitFunc) sp_guide_class_init,
66             NULL, NULL,
67             sizeof(SPGuide),
68             16,
69             (GInstanceInitFunc) sp_guide_init,
70             NULL,       /* value_table */
71         };
72         guide_type = g_type_register_static(SP_TYPE_OBJECT, "SPGuide", &guide_info, (GTypeFlags) 0);
73     }
75     return guide_type;
76 }
78 static void sp_guide_class_init(SPGuideClass *gc)
79 {
80     GObjectClass *gobject_class = (GObjectClass *) gc;
81     SPObjectClass *sp_object_class = (SPObjectClass *) gc;
83     parent_class = (SPObjectClass*) g_type_class_ref(SP_TYPE_OBJECT);
85     gobject_class->set_property = sp_guide_set_property;
86     gobject_class->get_property = sp_guide_get_property;
88     sp_object_class->build = sp_guide_build;
89     sp_object_class->release = sp_guide_release;
90     sp_object_class->set = sp_guide_set;
92     g_object_class_install_property(gobject_class,
93                                     PROP_COLOR,
94                                     g_param_spec_uint("color", "Color", "Color",
95                                                       0,
96                                                       0xffffffff,
97                                                       0xff000000,
98                                                       (GParamFlags) G_PARAM_READWRITE));
100     g_object_class_install_property(gobject_class,
101                                     PROP_HICOLOR,
102                                     g_param_spec_uint("hicolor", "HiColor", "HiColor",
103                                                       0,
104                                                       0xffffffff,
105                                                       0xff000000,
106                                                       (GParamFlags) G_PARAM_READWRITE));
109 static void sp_guide_init(SPGuide *guide)
111     guide->normal_to_line = component_vectors[NR::Y];
112     guide->point_on_line = Geom::Point(0.,0.);
113     guide->color = 0x0000ff7f;
114     guide->hicolor = 0xff00007f;
117 static void sp_guide_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec */*pspec*/)
119     SPGuide &guide = *SP_GUIDE(object);
121     switch (prop_id) {
122         case PROP_COLOR:
123             guide.color = g_value_get_uint(value);
124             for (GSList *l = guide.views; l != NULL; l = l->next) {
125                 sp_guideline_set_color(SP_GUIDELINE(l->data), guide.color);
126             }
127             break;
129         case PROP_HICOLOR:
130             guide.hicolor = g_value_get_uint(value);
131             break;
132     }
135 static void sp_guide_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec */*pspec*/)
137     SPGuide const &guide = *SP_GUIDE(object);
139     switch (prop_id) {
140         case PROP_COLOR:
141             g_value_set_uint(value, guide.color);
142             break;
143         case PROP_HICOLOR:
144             g_value_set_uint(value, guide.hicolor);
145             break;
146     }
149 static void sp_guide_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
151     if (((SPObjectClass *) (parent_class))->build) {
152         (* ((SPObjectClass *) (parent_class))->build)(object, document, repr);
153     }
155     sp_object_read_attr(object, "orientation");
156     sp_object_read_attr(object, "position");
159 static void sp_guide_release(SPObject *object)
161     SPGuide *guide = (SPGuide *) object;
163     while (guide->views) {
164         gtk_object_destroy(GTK_OBJECT(guide->views->data));
165         guide->views = g_slist_remove(guide->views, guide->views->data);
166     }
168     if (((SPObjectClass *) parent_class)->release) {
169         ((SPObjectClass *) parent_class)->release(object);
170     }
173 static void sp_guide_set(SPObject *object, unsigned int key, const gchar *value)
175     SPGuide *guide = SP_GUIDE(object);
177     switch (key) {
178     case SP_ATTR_ORIENTATION:
179         {
180             if (value && !strcmp(value, "horizontal")) {
181                 /* Visual representation of a horizontal line, constrain vertically (y coordinate). */
182                 guide->normal_to_line = component_vectors[NR::Y];
183             } else if (value && !strcmp(value, "vertical")) {
184                 guide->normal_to_line = component_vectors[NR::X];
185             } else if (value) {
186                 gchar ** strarray = g_strsplit(value, ",", 2);
187                 double newx, newy;
188                 unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
189                 success += sp_svg_number_read_d(strarray[1], &newy);
190                 g_strfreev (strarray);
191                 if (success == 2) {
192                     Geom::Point direction(newx, newy);
193                     direction.normalize();
194                     guide->normal_to_line = direction;
195                 } else {
196                     // default to vertical line for bad arguments
197                     guide->normal_to_line = component_vectors[NR::X];
198                 }
199             } else {
200                 // default to vertical line for bad arguments
201                 guide->normal_to_line = component_vectors[NR::X];
202             }
203             sp_guide_set_normal(*guide, guide->normal_to_line.to_2geom(), false);
204         }
205         break;
206     case SP_ATTR_POSITION:
207         {
208             gchar ** strarray = g_strsplit(value, ",", 2);
209             double newx, newy;
210             unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
211             success += sp_svg_number_read_d(strarray[1], &newy);
212             g_strfreev (strarray);
213             if (success == 2) {
214                 guide->point_on_line = Geom::Point(newx, newy);
215             } else if (success == 1) {
216                 // before 0.46 style guideline definition.
217                 const gchar *attr = SP_OBJECT_REPR(object)->attribute("orientation");
218                 if (attr && !strcmp(attr, "horizontal")) {
219                     guide->point_on_line = Geom::Point(0, newx);
220                 } else {
221                     guide->point_on_line = Geom::Point(newx, 0);
222                 }
223             }
225             // update position in non-committing way
226             // fixme: perhaps we need to add an update method instead, and request_update here
227             sp_guide_moveto(*guide, guide->point_on_line, false);
228         }
229         break;
230     default:
231             if (((SPObjectClass *) (parent_class))->set) {
232                 ((SPObjectClass *) (parent_class))->set(object, key, value);
233             }
234             break;
235     }
238 void sp_guide_show(SPGuide *guide, SPCanvasGroup *group, GCallback handler)
240     SPCanvasItem *item = sp_guideline_new(group, guide->point_on_line, guide->normal_to_line.to_2geom());
241     sp_guideline_set_color(SP_GUIDELINE(item), guide->color);
243     g_signal_connect(G_OBJECT(item), "event", G_CALLBACK(handler), guide);
245     guide->views = g_slist_prepend(guide->views, item);
248 void sp_guide_hide(SPGuide *guide, SPCanvas *canvas)
250     g_assert(guide != NULL);
251     g_assert(SP_IS_GUIDE(guide));
252     g_assert(canvas != NULL);
253     g_assert(SP_IS_CANVAS(canvas));
255     for (GSList *l = guide->views; l != NULL; l = l->next) {
256         if (canvas == SP_CANVAS_ITEM(l->data)->canvas) {
257             gtk_object_destroy(GTK_OBJECT(l->data));
258             guide->views = g_slist_remove(guide->views, l->data);
259             return;
260         }
261     }
263     g_assert_not_reached();
266 void sp_guide_sensitize(SPGuide *guide, SPCanvas *canvas, gboolean sensitive)
268     g_assert(guide != NULL);
269     g_assert(SP_IS_GUIDE(guide));
270     g_assert(canvas != NULL);
271     g_assert(SP_IS_CANVAS(canvas));
273     for (GSList *l = guide->views; l != NULL; l = l->next) {
274         if (canvas == SP_CANVAS_ITEM(l->data)->canvas) {
275             sp_guideline_set_sensitive(SP_GUIDELINE(l->data), sensitive);
276             return;
277         }
278     }
280     g_assert_not_reached();
283 Geom::Point sp_guide_position_from_pt(SPGuide const *guide, NR::Point const &pt)
285     return -(pt.to_2geom() - guide->point_on_line);
288 double sp_guide_distance_from_pt(SPGuide const *guide, Geom::Point const &pt)
290     return dot(pt - guide->point_on_line, guide->normal_to_line);
293 /**
294  * \arg commit False indicates temporary moveto in response to motion event while dragging,
295  *      true indicates a "committing" version: in response to button release event after
296  *      dragging a guideline, or clicking OK in guide editing dialog.
297  */
298 void sp_guide_moveto(SPGuide const &guide, Geom::Point const point_on_line, bool const commit)
300     g_assert(SP_IS_GUIDE(&guide));
302     for (GSList *l = guide.views; l != NULL; l = l->next) {
303         sp_guideline_set_position(SP_GUIDELINE(l->data), point_on_line);
304     }
306     /* Calling sp_repr_set_point must precede calling sp_item_notify_moveto in the commit
307        case, so that the guide's new position is available for sp_item_rm_unsatisfied_cns. */
308     if (commit) {
309         sp_repr_set_point(SP_OBJECT(&guide)->repr, "position", point_on_line);
310     }
312 /*  DISABLED CODE BECAUSE  SPGuideAttachment  IS NOT USE AT THE MOMENT (johan)
313     for (vector<SPGuideAttachment>::const_iterator i(guide.attached_items.begin()),
314              iEnd(guide.attached_items.end());
315          i != iEnd; ++i)
316     {
317         SPGuideAttachment const &att = *i;
318         sp_item_notify_moveto(*att.item, guide, att.snappoint_ix, position, commit);
319     }
320 */
323 /**
324  * \arg commit False indicates temporary moveto in response to motion event while dragging,
325  *      true indicates a "committing" version: in response to button release event after
326  *      dragging a guideline, or clicking OK in guide editing dialog.
327  */
328 void sp_guide_set_normal(SPGuide const &guide, Geom::Point const normal_to_line, bool const commit)
330     g_assert(SP_IS_GUIDE(&guide));
332     for (GSList *l = guide.views; l != NULL; l = l->next) {
333         sp_guideline_set_normal(SP_GUIDELINE(l->data), normal_to_line);
334     }
336     /* Calling sp_repr_set_svg_point must precede calling sp_item_notify_moveto in the commit
337        case, so that the guide's new position is available for sp_item_rm_unsatisfied_cns. */
338     if (commit) {
339         sp_repr_set_point(SP_OBJECT(&guide)->repr, "orientation", normal_to_line);
340     }
342 /*  DISABLED CODE BECAUSE  SPGuideAttachment  IS NOT USE AT THE MOMENT (johan)
343     for (vector<SPGuideAttachment>::const_iterator i(guide.attached_items.begin()),
344              iEnd(guide.attached_items.end());
345          i != iEnd; ++i)
346     {
347         SPGuideAttachment const &att = *i;
348         sp_item_notify_moveto(*att.item, guide, att.snappoint_ix, position, commit);
349     }
350 */
353 /**
354  * Returns a human-readable description of the guideline for use in dialog boxes and status bar.
355  *
356  * The caller is responsible for freeing the string.
357  */
358 char *sp_guide_description(SPGuide const *guide)
360     using NR::X;
361     using NR::Y;
362             
363     GString *position_string_x = SP_PX_TO_METRIC_STRING(guide->point_on_line[X], SP_ACTIVE_DESKTOP->namedview->getDefaultMetric());
364     GString *position_string_y = SP_PX_TO_METRIC_STRING(guide->point_on_line[Y], SP_ACTIVE_DESKTOP->namedview->getDefaultMetric());
366     if ( guide->normal_to_line == component_vectors[X] ) {
367         return g_strdup_printf(_("vertical guideline at %s"), position_string_x->str);
368     } else if ( guide->normal_to_line == component_vectors[Y] ) {
369         return g_strdup_printf(_("horizontal guideline at %s"), position_string_y->str);
370     } else {
371         double const radians = guide->angle();
372         double const degrees = Geom::rad_to_deg(radians);
373         int const degrees_int = (int) round(degrees);
374         return g_strdup_printf("%d degree guideline at (%s,%s)", degrees_int, position_string_x->str, position_string_y->str);
375         /* Alternative suggestion: "angled guideline". */
376     }
378     g_string_free(position_string_x, TRUE);
379     g_string_free(position_string_y, TRUE);
382 void sp_guide_remove(SPGuide *guide)
384     g_assert(SP_IS_GUIDE(guide));
386     for (vector<SPGuideAttachment>::const_iterator i(guide->attached_items.begin()),
387              iEnd(guide->attached_items.end());
388          i != iEnd; ++i)
389     {
390         SPGuideAttachment const &att = *i;
391         remove_last(att.item->constraints, SPGuideConstraint(guide, att.snappoint_ix));
392     }
393     guide->attached_items.clear();
395     sp_repr_unparent(SP_OBJECT(guide)->repr);
398 /*
399   Local Variables:
400   mode:c++
401   c-file-style:"stroustrup"
402   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
403   indent-tabs-mode:nil
404   fill-column:99
405   End:
406 */
407 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :