[AdgMarker] Avoid get_segment() when n_segment == 0
[adg.git] / adg / adg-marker.c
blobf313b90c138309558837dc3340e57c0c50fefb4d
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 /**
22 * SECTION:adg-marker
23 * @short_description: Base entity for markers
25 * A marker is an entity to be applied at the start or end of a segment.
26 * Typical examples include arrows, ticks, dots and so on.
28 * The #AdgMarker:trail and #AdgMarker:n-segment properties specify the
29 * segment where the marker should be applied. Similarly to the
30 * #AdgStroke type, if the associated trail is destroyed the above
31 * properties are unset.
33 * The local map is used internally to align the marker to the trail end,
34 * so adg_entity_set_local_map() and friends is reserved. Therefore, if
35 * the trail is modified and the marker had no way to know it, you should
36 * call adg_entity_local_changed() to update the marker position.
38 * Use adg_marker_set_pos() to select the position where the marker
39 * should be put: %0 means the start point of the segment while %1
40 * means the end point.
42 * The #AdgMarker:model property and APIs are intended only for marker
43 * implementation purposes.
44 **/
46 /**
47 * AdgMarker:
49 * All fields are privates and should not be used directly.
50 * Use its public methods instead.
51 **/
54 #include "adg-marker.h"
55 #include "adg-marker-private.h"
56 #include "adg-intl.h"
58 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_marker_parent_class)
61 enum {
62 PROP_0,
63 PROP_TRAIL,
64 PROP_N_SEGMENT,
65 PROP_POS,
66 PROP_SIZE,
67 PROP_MODEL
71 static void dispose (GObject *object);
72 static void get_property (GObject *object,
73 guint prop_id,
74 GValue *value,
75 GParamSpec *pspec);
76 static void set_property (GObject *object,
77 guint prop_id,
78 const GValue *value,
79 GParamSpec *pspec);
80 static void local_changed (AdgEntity *entity);
81 static void invalidate (AdgEntity *entity);
82 static gboolean set_segment (AdgMarker *marker,
83 AdgTrail *trail,
84 guint n_segment);
85 static void unset_trail (AdgMarker *marker);
86 static gboolean set_n_segment (AdgMarker *marker,
87 guint n_segment);
88 static gboolean set_pos (AdgMarker *marker,
89 gdouble pos);
90 static gboolean set_size (AdgMarker *marker,
91 gdouble size);
92 static gboolean set_model (AdgMarker *marker,
93 AdgModel *model);
94 static AdgModel * create_model (AdgMarker *marker);
97 G_DEFINE_ABSTRACT_TYPE(AdgMarker, adg_marker, ADG_TYPE_ENTITY);
100 static void
101 adg_marker_class_init(AdgMarkerClass *klass)
103 GObjectClass *gobject_class;
104 AdgEntityClass *entity_class;
105 GParamSpec *param;
107 gobject_class = (GObjectClass *) klass;
108 entity_class = (AdgEntityClass *) klass;
110 g_type_class_add_private(klass, sizeof(AdgMarkerPrivate));
112 gobject_class->dispose = dispose;
113 gobject_class->set_property = set_property;
114 gobject_class->get_property = get_property;
116 entity_class->local_changed = local_changed;
117 entity_class->invalidate = invalidate;
119 klass->create_model = create_model;
121 param = g_param_spec_object("trail",
122 P_("Trail"),
123 P_("The subject AdgTrail for this marker"),
124 ADG_TYPE_TRAIL,
125 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
126 g_object_class_install_property(gobject_class, PROP_TRAIL, param);
128 param = g_param_spec_uint("n-segment",
129 P_("Segment Index"),
130 P_("The segment on trail where this marker should be applied (where 0 means undefined segment, 1 the first segment and so on)"),
131 0, G_MAXUINT, 0,
132 G_PARAM_READWRITE);
133 g_object_class_install_property(gobject_class, PROP_N_SEGMENT, param);
135 param = g_param_spec_double("pos",
136 P_("Position"),
137 P_("The position ratio inside the segment where to put the marker (0 means the start point while 1 means the end point)"),
138 0, 1, 0,
139 G_PARAM_READWRITE);
140 g_object_class_install_property(gobject_class, PROP_POS, param);
142 param = g_param_spec_double("size",
143 P_("Marker Size"),
144 P_("The size (in global space) of the marker"),
145 0, G_MAXDOUBLE, 10,
146 G_PARAM_READWRITE);
147 g_object_class_install_property(gobject_class, PROP_SIZE, param);
149 param = g_param_spec_object("model",
150 P_("Model"),
151 P_("A general purpose model usable by the marker implementations"),
152 ADG_TYPE_MODEL,
153 G_PARAM_READWRITE);
154 g_object_class_install_property(gobject_class, PROP_MODEL, param);
157 static void
158 adg_marker_init(AdgMarker *marker)
160 AdgMarkerPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(marker,
161 ADG_TYPE_MARKER,
162 AdgMarkerPrivate);
163 data->trail = NULL;
164 data->n_segment = 0;
165 data->backup_segment = NULL;
166 memset(&data->segment, 0, sizeof(data->segment));
167 data->pos = 0;
168 data->size = 10;
169 data->model = NULL;
171 marker->data = data;
174 static void
175 dispose(GObject *object)
177 AdgMarker *marker = (AdgMarker *) object;
179 adg_marker_set_model(marker, NULL);
180 adg_marker_set_segment(marker, NULL, 0);
182 if (PARENT_OBJECT_CLASS->dispose != NULL)
183 PARENT_OBJECT_CLASS->dispose(object);
187 static void
188 get_property(GObject *object,
189 guint prop_id, GValue *value, GParamSpec *pspec)
191 AdgMarkerPrivate *data = ((AdgMarker *) object)->data;
193 switch (prop_id) {
194 case PROP_TRAIL:
195 g_value_set_object(value, data->trail);
196 break;
197 case PROP_N_SEGMENT:
198 g_value_set_uint(value, data->n_segment);
199 break;
200 case PROP_POS:
201 g_value_set_double(value, data->pos);
202 break;
203 case PROP_SIZE:
204 g_value_set_double(value, data->size);
205 break;
206 case PROP_MODEL:
207 g_value_set_object(value, data->model);
208 break;
209 default:
210 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
211 break;
215 static void
216 set_property(GObject *object,
217 guint prop_id, const GValue *value, GParamSpec *pspec)
219 AdgMarker *marker = (AdgMarker *) object;
221 switch (prop_id) {
222 case PROP_TRAIL:
223 set_segment(marker, g_value_get_object(value), 1);
224 break;
225 case PROP_N_SEGMENT:
226 set_n_segment(marker, g_value_get_uint(value));
227 break;
228 case PROP_POS:
229 set_pos(marker, g_value_get_double(value));
230 break;
231 case PROP_SIZE:
232 set_size(marker, g_value_get_double(value));
233 break;
234 case PROP_MODEL:
235 set_model(marker, g_value_get_object(value));
236 break;
237 default:
238 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
239 break;
245 * adg_marker_get_trail:
246 * @marker: an #AdgMarker
248 * Gets the trail where this marker should be applied.
250 * Returns: the trail owned by @marker or %NULL on errors
252 AdgTrail *
253 adg_marker_get_trail(AdgMarker *marker)
255 AdgMarkerPrivate *data;
257 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
259 data = marker->data;
261 return data->trail;
265 * adg_marker_get_n_segment:
266 * @marker: an #AdgMarker
268 * Returns the segment of the associated trail where this marker
269 * will be applied, where %1 is the first segment.
271 * Returns: an index greather than %0 on success or %0 on errors
273 guint
274 adg_marker_get_n_segment(AdgMarker *marker)
276 AdgMarkerPrivate *data;
278 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
280 data = marker->data;
282 return data->n_segment;
286 * adg_marker_get_segment:
287 * @marker: an #AdgMarker
289 * <note><para>
290 * This function is only useful in marker implementations.
291 * </para></note>
293 * Gets the segment where the marker will be applied. This segment
294 * is eventually a modified version of the backup segment, after
295 * having applied the marker.
297 * Returns: the segment or %NULL on errors
299 AdgSegment *
300 adg_marker_get_segment(AdgMarker *marker)
302 AdgMarkerPrivate *data;
304 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
306 data = marker->data;
308 return &data->segment;
312 * adg_marker_set_segment:
313 * @marker: an #AdgMarker
314 * @trail: the #AdgTrail
315 * @n_segment: a segment index
317 * Sets the new segment where the marker should be applied. The weak
318 * reference to the old trail (if an old trail was present) is dropped
319 * while a new weak reference is added to @trail. If @trail is destroyed,
320 * the weak reference callback will automatically unset #AdgMarker:trai
321 * and will set #AdgMarker:n-segment to %0.
323 void
324 adg_marker_set_segment(AdgMarker *marker, AdgTrail *trail, guint n_segment)
326 g_return_if_fail(ADG_IS_MARKER(marker));
328 if (set_segment(marker, trail, n_segment))
329 g_object_notify((GObject *) marker, "trail");
333 * adg_marker_get_backup_segment:
334 * @marker: an #AdgMarker
336 * <note><para>
337 * This function is only useful in marker implementations.
338 * </para></note>
340 * Gets the original segment where the marker has been applied.
341 * Applying a marker could modify the underlying trail, usually
342 * by trimming the original segment of a #AdgMarker:size dependent
343 * length from the ends. The marker instance holds a copy of the
344 * original segment, generated by adg_marker_backup_segment(),
345 * to be used in recomputation, for example when the marker
346 * changes its size.
348 * When the subject segment is changed (either by changing
349 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
350 * is automatically restored.
352 * Returns: the original segment or %NULL on errors
354 const AdgSegment *
355 adg_marker_get_backup_segment(AdgMarker *marker)
357 AdgMarkerPrivate *data;
359 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
361 data = marker->data;
363 return data->backup_segment;
367 * adg_marker_backup_segment:
368 * @marker: an #AdgMarker
370 * <note><para>
371 * This function is only useful in marker implementations.
372 * </para></note>
374 * Duplicates the current subject segment for backup purpose: this
375 * segment can be accessed by adg_marker_get_backup_segment().
376 * Obviously, a current segment should exist (either the
377 * #AdgMarker:trail and #AdgMarker:n-segment properties must be
378 * properly defined) or this method will fail without further
379 * processing.
381 * When the subject segment is changed (either by changing
382 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
383 * is automatically restored.
385 void
386 adg_marker_backup_segment(AdgMarker *marker)
388 AdgMarkerPrivate *data;
390 g_return_if_fail(ADG_IS_MARKER(marker));
392 data = marker->data;
394 if (data->n_segment > 0) {
395 g_return_if_fail(data->trail != NULL);
397 g_free(data->backup_segment);
399 /* Backup the segment, if a segment to backup exists */
400 if (adg_trail_get_segment(data->trail,
401 &data->segment, data->n_segment))
402 data->backup_segment = adg_segment_deep_dup(&data->segment);
407 * adg_marker_get_pos:
408 * @marker: an #AdgMarker
410 * Gets the current position of @marker. The returned value is a ratio
411 * position referred to the segment associated to @marker: %0 means the
412 * start point and %1 means the end point of the segment.
414 * Returns: the marker position
416 gdouble
417 adg_marker_get_pos(AdgMarker *marker)
419 AdgMarkerPrivate *data;
421 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
423 data = marker->data;
425 return data->pos;
429 * adg_marker_set_pos:
430 * @marker: an #AdgMarker
431 * @pos: the new pos
433 * Sets a new position on @marker. Check out adg_marker_get_pos() for
434 * details on what @pos represents.
436 void
437 adg_marker_set_pos(AdgMarker *marker, gdouble pos)
439 g_return_if_fail(ADG_IS_MARKER(marker));
441 if (set_pos(marker, pos))
442 g_object_notify((GObject *) marker, "pos");
446 * adg_marker_get_size:
447 * @marker: an #AdgMarker
449 * Gets the current size of @marker.
451 * Returns: the marker size, in global space
453 gdouble
454 adg_marker_get_size(AdgMarker *marker)
456 AdgMarkerPrivate *data;
458 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
460 data = marker->data;
462 return data->size;
466 * adg_marker_set_size:
467 * @marker: an #AdgMarker
468 * @size: the new size
470 * Sets a new size on @marker. The @size is an implementation-dependent
471 * property: it has meaning only when used by an #AdgMarker derived type.
473 void
474 adg_marker_set_size(AdgMarker *marker, gdouble size)
476 g_return_if_fail(ADG_IS_MARKER(marker));
478 if (set_size(marker, size))
479 g_object_notify((GObject *) marker, "size");
483 * adg_marker_model:
484 * @marker: an #AdgMarker
486 * <note><para>
487 * This function is only useful in marker implementations.
488 * </para></note>
490 * Gets the model of @marker. If the model is not found, it is
491 * automatically created by calling the create_model() virtual method.
493 * Returns: the current model owned by @marker or %NULL on errors
495 AdgModel *
496 adg_marker_model(AdgMarker *marker)
498 AdgMarkerPrivate *data;
500 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
502 data = marker->data;
504 if (data->model == NULL) {
505 /* Model not found: regenerate it */
506 AdgMarkerClass *marker_class = ADG_MARKER_GET_CLASS(marker);
508 adg_marker_set_model(marker, marker_class->create_model(marker));
511 return data->model;
515 * adg_marker_get_model:
516 * @marker: an #AdgMarker
518 * <note><para>
519 * This function is only useful in marker implementations.
520 * </para></note>
522 * Gets the current model of @marker. This is an accessor method:
523 * if you need to get the model for rendering, use adg_marker_model()
524 * instead.
526 * Returns: the cached model owned by @marker or %NULL on errors
528 AdgModel *
529 adg_marker_get_model(AdgMarker *marker)
531 AdgMarkerPrivate *data;
533 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
535 data = marker->data;
537 return data->model;
541 * adg_marker_set_model:
542 * @marker: an #AdgMarker
543 * @model: a new #AdgModel
545 * <note><para>
546 * This function is only useful in marker implementations.
547 * </para></note>
549 * Sets a new model for @marker. The reference to the old model (if an
550 * old model was present) is dropped while a new reference is added to
551 * @model.
553 void
554 adg_marker_set_model(AdgMarker *marker, AdgModel *model)
556 g_return_if_fail(ADG_IS_MARKER(marker));
558 if (set_model(marker, model))
559 g_object_notify((GObject *) marker, "model");
563 static void
564 local_changed(AdgEntity *entity)
566 AdgMarkerPrivate *data;
567 CpmlPair pair;
568 CpmlVector vector;
569 AdgMatrix matrix, tmp;
571 data = ((AdgMarker *) entity)->data;
572 if (data->trail == NULL)
573 return;
575 cpml_segment_pair_at(&data->segment, &pair, data->pos);
576 cpml_segment_vector_at(&data->segment, &vector, data->pos);
577 cpml_vector_set_length(&vector, data->size);
579 if (data->pos > 0.5) {
580 vector.x = -vector.x;
581 vector.y = -vector.y;
584 cairo_matrix_init(&tmp, vector.x, vector.y, -vector.y, vector.x, 0, 0);
586 cairo_matrix_init_translate(&matrix, pair.x, pair.y);
587 cairo_matrix_multiply(&matrix, &tmp, &matrix);
589 adg_entity_set_local_matrix(entity, &matrix);
592 static void
593 invalidate(AdgEntity *entity)
595 adg_marker_set_model((AdgMarker *) entity, NULL);
599 static gboolean
600 set_segment(AdgMarker *marker, AdgTrail *trail, guint n_segment)
602 AdgMarkerPrivate *data;
603 AdgEntity *entity;
605 data = marker->data;
606 entity = (AdgEntity *) marker;
608 if (trail == data->trail && n_segment == data->n_segment)
609 return FALSE;
611 if (data->trail != NULL) {
612 /* Restore the original segment in the old trail */
613 set_n_segment(marker, 0);
615 g_object_weak_unref((GObject *) data->trail,
616 (GWeakNotify) unset_trail, marker);
617 adg_model_remove_dependency((AdgModel *) data->trail, entity);
620 data->trail = trail;
621 data->n_segment = 0;
623 if (data->trail != NULL) {
624 g_object_weak_ref((GObject *) data->trail,
625 (GWeakNotify) unset_trail, marker);
626 adg_model_add_dependency((AdgModel *) data->trail, entity);
628 set_n_segment(marker, n_segment);
631 return TRUE;
634 static void
635 unset_trail(AdgMarker *marker)
637 AdgMarkerPrivate *data = marker->data;
639 if (data->trail != NULL) {
640 data->trail = NULL;
641 set_n_segment(marker, 0);
645 static gboolean
646 set_n_segment(AdgMarker *marker, guint n_segment)
648 AdgMarkerPrivate *data = marker->data;
650 if (n_segment == data->n_segment)
651 return FALSE;
653 if (data->backup_segment != NULL) {
654 /* Restore the original segment, if any */
655 if (data->trail != NULL)
656 adg_segment_deep_copy(&data->segment, data->backup_segment);
658 g_free(data->backup_segment);
659 data->backup_segment = NULL;
662 data->n_segment = n_segment;
664 if (n_segment == 0) {
665 memset(&data->segment, 0, sizeof(data->segment));
666 return TRUE;
669 return adg_trail_get_segment(data->trail, &data->segment, n_segment);
672 static gboolean
673 set_pos(AdgMarker *marker, gdouble pos)
675 AdgMarkerPrivate *data = marker->data;
677 if (pos == data->pos)
678 return FALSE;
680 data->pos = pos;
682 return TRUE;
685 static gboolean
686 set_size(AdgMarker *marker, gdouble size)
688 AdgMarkerPrivate *data = marker->data;
690 if (size == data->size)
691 return FALSE;
693 data->size = size;
695 return TRUE;
698 static gboolean
699 set_model(AdgMarker *marker, AdgModel *model)
701 AdgMarkerPrivate *data = marker->data;
703 if (model == data->model)
704 return FALSE;
706 if (data->model != NULL)
707 g_object_unref((GObject *) data->model);
709 data->model = model;
711 if (data->model != NULL) {
712 g_object_ref((GObject *) data->model);
713 adg_entity_local_changed((AdgEntity *) marker);
716 return TRUE;
719 static AdgModel *
720 create_model(AdgMarker *marker)
722 g_warning("%s: `create_model' method not implemented for type `%s'",
723 G_STRLOC, g_type_name(G_OBJECT_TYPE(marker)));
724 return NULL;