[ADG] s/defined (/defined(/
[adg.git] / adg / adg-marker.c
blobb41335c2a17155f6c23ce4f142ab514b9428bc57
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-internal.h"
55 #include "adg-marker.h"
56 #include "adg-marker-private.h"
58 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_marker_parent_class)
59 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_marker_parent_class)
62 enum {
63 PROP_0,
64 PROP_TRAIL,
65 PROP_N_SEGMENT,
66 PROP_POS,
67 PROP_SIZE,
68 PROP_MODEL
72 static void dispose (GObject *object);
73 static void get_property (GObject *object,
74 guint prop_id,
75 GValue *value,
76 GParamSpec *pspec);
77 static void set_property (GObject *object,
78 guint prop_id,
79 const GValue *value,
80 GParamSpec *pspec);
81 static void local_changed (AdgEntity *entity);
82 static void invalidate (AdgEntity *entity);
83 static gboolean set_segment (AdgMarker *marker,
84 AdgTrail *trail,
85 guint n_segment);
86 static void unset_trail (AdgMarker *marker);
87 static gboolean set_n_segment (AdgMarker *marker,
88 guint n_segment);
89 static gboolean set_pos (AdgMarker *marker,
90 gdouble pos);
91 static gboolean set_size (AdgMarker *marker,
92 gdouble size);
93 static gboolean set_model (AdgMarker *marker,
94 AdgModel *model);
95 static AdgModel * create_model (AdgMarker *marker);
98 G_DEFINE_ABSTRACT_TYPE(AdgMarker, adg_marker, ADG_TYPE_ENTITY);
101 static void
102 adg_marker_class_init(AdgMarkerClass *klass)
104 GObjectClass *gobject_class;
105 AdgEntityClass *entity_class;
106 GParamSpec *param;
108 gobject_class = (GObjectClass *) klass;
109 entity_class = (AdgEntityClass *) klass;
111 g_type_class_add_private(klass, sizeof(AdgMarkerPrivate));
113 gobject_class->dispose = dispose;
114 gobject_class->set_property = set_property;
115 gobject_class->get_property = get_property;
117 entity_class->local_changed = local_changed;
118 entity_class->invalidate = invalidate;
120 klass->create_model = create_model;
122 param = g_param_spec_object("trail",
123 P_("Trail"),
124 P_("The subject AdgTrail for this marker"),
125 ADG_TYPE_TRAIL,
126 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
127 g_object_class_install_property(gobject_class, PROP_TRAIL, param);
129 param = g_param_spec_uint("n-segment",
130 P_("Segment Index"),
131 P_("The segment on trail where this marker should be applied (where 0 means undefined segment, 1 the first segment and so on)"),
132 0, G_MAXUINT, 0,
133 G_PARAM_READWRITE);
134 g_object_class_install_property(gobject_class, PROP_N_SEGMENT, param);
136 param = g_param_spec_double("pos",
137 P_("Position"),
138 P_("The position ratio inside the segment where to put the marker (0 means the start point while 1 means the end point)"),
139 0, 1, 0,
140 G_PARAM_READWRITE);
141 g_object_class_install_property(gobject_class, PROP_POS, param);
143 param = g_param_spec_double("size",
144 P_("Marker Size"),
145 P_("The size (in global space) of the marker"),
146 0, G_MAXDOUBLE, 10,
147 G_PARAM_READWRITE);
148 g_object_class_install_property(gobject_class, PROP_SIZE, param);
150 param = g_param_spec_object("model",
151 P_("Model"),
152 P_("A general purpose model usable by the marker implementations"),
153 ADG_TYPE_MODEL,
154 G_PARAM_READWRITE);
155 g_object_class_install_property(gobject_class, PROP_MODEL, param);
158 static void
159 adg_marker_init(AdgMarker *marker)
161 AdgMarkerPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(marker,
162 ADG_TYPE_MARKER,
163 AdgMarkerPrivate);
164 data->trail = NULL;
165 data->n_segment = 0;
166 data->backup_segment = NULL;
167 memset(&data->segment, 0, sizeof(data->segment));
168 data->pos = 0;
169 data->size = 10;
170 data->model = NULL;
172 marker->data = data;
175 static void
176 dispose(GObject *object)
178 AdgMarker *marker = (AdgMarker *) object;
180 adg_marker_set_model(marker, NULL);
181 adg_marker_set_segment(marker, NULL, 0);
183 if (PARENT_OBJECT_CLASS->dispose)
184 PARENT_OBJECT_CLASS->dispose(object);
188 static void
189 get_property(GObject *object,
190 guint prop_id, GValue *value, GParamSpec *pspec)
192 AdgMarkerPrivate *data = ((AdgMarker *) object)->data;
194 switch (prop_id) {
195 case PROP_TRAIL:
196 g_value_set_object(value, data->trail);
197 break;
198 case PROP_N_SEGMENT:
199 g_value_set_uint(value, data->n_segment);
200 break;
201 case PROP_POS:
202 g_value_set_double(value, data->pos);
203 break;
204 case PROP_SIZE:
205 g_value_set_double(value, data->size);
206 break;
207 case PROP_MODEL:
208 g_value_set_object(value, data->model);
209 break;
210 default:
211 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
212 break;
216 static void
217 set_property(GObject *object,
218 guint prop_id, const GValue *value, GParamSpec *pspec)
220 AdgMarker *marker = (AdgMarker *) object;
222 switch (prop_id) {
223 case PROP_TRAIL:
224 set_segment(marker, g_value_get_object(value), 1);
225 break;
226 case PROP_N_SEGMENT:
227 set_n_segment(marker, g_value_get_uint(value));
228 break;
229 case PROP_POS:
230 set_pos(marker, g_value_get_double(value));
231 break;
232 case PROP_SIZE:
233 set_size(marker, g_value_get_double(value));
234 break;
235 case PROP_MODEL:
236 set_model(marker, g_value_get_object(value));
237 break;
238 default:
239 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
240 break;
246 * adg_marker_set_segment:
247 * @marker: an #AdgMarker
248 * @trail: the #AdgTrail
249 * @n_segment: a segment index
251 * Sets the new segment where the marker should be applied. The weak
252 * reference to the old trail (if an old trail was present) is dropped
253 * while a new weak reference is added to @trail. If @trail is destroyed,
254 * the weak reference callback will automatically unset #AdgMarker:trai
255 * and will set #AdgMarker:n-segment to %0.
257 void
258 adg_marker_set_segment(AdgMarker *marker, AdgTrail *trail, guint n_segment)
260 g_return_if_fail(ADG_IS_MARKER(marker));
262 if (set_segment(marker, trail, n_segment))
263 g_object_notify((GObject *) marker, "trail");
267 * adg_marker_get_trail:
268 * @marker: an #AdgMarker
270 * Gets the trail where this marker should be applied.
272 * Returns: the trail owned by @marker or %NULL on errors
274 AdgTrail *
275 adg_marker_get_trail(AdgMarker *marker)
277 AdgMarkerPrivate *data;
279 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
281 data = marker->data;
283 return data->trail;
287 * adg_marker_get_n_segment:
288 * @marker: an #AdgMarker
290 * Returns the segment of the associated trail where this marker
291 * will be applied, where %1 is the first segment.
293 * Returns: an index greather than %0 on success or %0 on errors
295 guint
296 adg_marker_get_n_segment(AdgMarker *marker)
298 AdgMarkerPrivate *data;
300 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
302 data = marker->data;
304 return data->n_segment;
308 * adg_marker_get_segment:
309 * @marker: an #AdgMarker
311 * <note><para>
312 * This function is only useful in marker implementations.
313 * </para></note>
315 * Gets the segment where the marker will be applied. This segment
316 * is eventually a modified version of the backup segment, after
317 * having applied the marker.
319 * Returns: the segment or %NULL on errors
321 const AdgSegment *
322 adg_marker_get_segment(AdgMarker *marker)
324 AdgMarkerPrivate *data;
326 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
328 data = marker->data;
330 return &data->segment;
334 * adg_marker_backup_segment:
335 * @marker: an #AdgMarker
337 * <note><para>
338 * This function is only useful in marker implementations.
339 * </para></note>
341 * Duplicates the current subject segment for backup purpose: this
342 * segment can be accessed by adg_marker_get_backup_segment().
343 * Obviously, a current segment should exist (either the
344 * #AdgMarker:trail and #AdgMarker:n-segment properties must be
345 * properly defined) or this method will fail without further
346 * processing.
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 void
353 adg_marker_backup_segment(AdgMarker *marker)
355 AdgMarkerPrivate *data;
357 g_return_if_fail(ADG_IS_MARKER(marker));
359 data = marker->data;
361 if (data->n_segment > 0) {
362 g_return_if_fail(data->trail != NULL);
364 g_free(data->backup_segment);
366 /* Backup the segment, if a segment to backup exists */
367 if (adg_trail_put_segment(data->trail, data->n_segment,
368 &data->segment))
369 data->backup_segment = adg_segment_deep_dup(&data->segment);
374 * adg_marker_get_backup_segment:
375 * @marker: an #AdgMarker
377 * <note><para>
378 * This function is only useful in marker implementations.
379 * </para></note>
381 * Gets the original segment where the marker has been applied.
382 * Applying a marker could modify the underlying trail, usually
383 * by trimming the original segment of a #AdgMarker:size dependent
384 * length from the ends. The marker instance holds a copy of the
385 * original segment, generated by adg_marker_backup_segment(),
386 * to be used in recomputation, for example when the marker
387 * changes its size.
389 * When the subject segment is changed (either by changing
390 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
391 * is automatically restored.
393 * Returns: the original segment or %NULL on errors
395 const AdgSegment *
396 adg_marker_get_backup_segment(AdgMarker *marker)
398 AdgMarkerPrivate *data;
400 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
402 data = marker->data;
404 return data->backup_segment;
408 * adg_marker_set_pos:
409 * @marker: an #AdgMarker
410 * @pos: the new pos
412 * Sets a new position on @marker. Check out adg_marker_get_pos() for
413 * details on what @pos represents.
415 void
416 adg_marker_set_pos(AdgMarker *marker, gdouble pos)
418 g_return_if_fail(ADG_IS_MARKER(marker));
420 if (set_pos(marker, pos))
421 g_object_notify((GObject *) marker, "pos");
425 * adg_marker_get_pos:
426 * @marker: an #AdgMarker
428 * Gets the current position of @marker. The returned value is a ratio
429 * position referred to the segment associated to @marker: %0 means the
430 * start point and %1 means the end point of the segment.
432 * Returns: the marker position
434 gdouble
435 adg_marker_get_pos(AdgMarker *marker)
437 AdgMarkerPrivate *data;
439 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
441 data = marker->data;
443 return data->pos;
447 * adg_marker_set_size:
448 * @marker: an #AdgMarker
449 * @size: the new size
451 * Sets a new size on @marker. The @size is an implementation-dependent
452 * property: it has meaning only when used by an #AdgMarker derived type.
454 void
455 adg_marker_set_size(AdgMarker *marker, gdouble size)
457 g_return_if_fail(ADG_IS_MARKER(marker));
459 if (set_size(marker, size))
460 g_object_notify((GObject *) marker, "size");
464 * adg_marker_get_size:
465 * @marker: an #AdgMarker
467 * Gets the current size of @marker.
469 * Returns: the marker size, in global space
471 gdouble
472 adg_marker_get_size(AdgMarker *marker)
474 AdgMarkerPrivate *data;
476 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
478 data = marker->data;
480 return data->size;
484 * adg_marker_set_model:
485 * @marker: an #AdgMarker
486 * @model: a new #AdgModel
488 * <note><para>
489 * This function is only useful in marker implementations.
490 * </para></note>
492 * Sets a new model for @marker. The reference to the old model (if an
493 * old model was present) is dropped while a new reference is added to
494 * @model.
496 void
497 adg_marker_set_model(AdgMarker *marker, AdgModel *model)
499 g_return_if_fail(ADG_IS_MARKER(marker));
501 if (set_model(marker, model))
502 g_object_notify((GObject *) marker, "model");
506 * adg_marker_get_model:
507 * @marker: an #AdgMarker
509 * <note><para>
510 * This function is only useful in marker implementations.
511 * </para></note>
513 * Gets the current model of @marker. This is an accessor method:
514 * if you need to get the model for rendering, use adg_marker_model()
515 * instead.
517 * Returns: the cached model owned by @marker or %NULL on errors
519 AdgModel *
520 adg_marker_get_model(AdgMarker *marker)
522 AdgMarkerPrivate *data;
524 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
526 data = marker->data;
528 return data->model;
532 * adg_marker_model:
533 * @marker: an #AdgMarker
535 * <note><para>
536 * This function is only useful in marker implementations.
537 * </para></note>
539 * Gets the model of @marker. If the model is not found, it is
540 * automatically created by calling the create_model() virtual method.
542 * Returns: the current model owned by @marker or %NULL on errors
544 AdgModel *
545 adg_marker_model(AdgMarker *marker)
547 AdgMarkerPrivate *data;
549 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
551 data = marker->data;
553 if (data->model == NULL) {
554 /* Model not found: regenerate it */
555 AdgMarkerClass *marker_class = ADG_MARKER_GET_CLASS(marker);
557 if (marker_class->create_model)
558 adg_marker_set_model(marker, marker_class->create_model(marker));
561 return data->model;
565 static void
566 local_changed(AdgEntity *entity)
568 AdgMarkerPrivate *data;
569 CpmlPair pair;
570 CpmlVector vector;
571 AdgMatrix map;
573 data = ((AdgMarker *) entity)->data;
574 if (data->trail == NULL)
575 return;
577 cpml_segment_put_pair_at(&data->segment, data->pos, &pair);
578 cpml_segment_put_vector_at(&data->segment, data->pos, &vector);
579 cpml_vector_set_length(&vector, data->size);
581 if (data->pos > 0.5) {
582 vector.x = -vector.x;
583 vector.y = -vector.y;
586 cairo_matrix_init(&map, vector.x, vector.y,
587 -vector.y, vector.x, pair.x, pair.y);
589 adg_entity_set_local_map(entity, &map);
591 if (PARENT_ENTITY_CLASS->local_changed)
592 PARENT_ENTITY_CLASS->local_changed(entity);
595 static void
596 invalidate(AdgEntity *entity)
598 adg_marker_set_model((AdgMarker *) entity, NULL);
602 static gboolean
603 set_segment(AdgMarker *marker, AdgTrail *trail, guint n_segment)
605 AdgMarkerPrivate *data;
606 AdgEntity *entity;
608 data = marker->data;
609 entity = (AdgEntity *) marker;
611 if (trail == data->trail && n_segment == data->n_segment)
612 return FALSE;
614 if (data->trail != NULL) {
615 /* Restore the original segment in the old trail */
616 set_n_segment(marker, 0);
618 g_object_weak_unref((GObject *) data->trail,
619 (GWeakNotify) unset_trail, marker);
620 adg_model_remove_dependency((AdgModel *) data->trail, entity);
623 data->trail = trail;
624 data->n_segment = 0;
626 if (data->trail != NULL) {
627 g_object_weak_ref((GObject *) data->trail,
628 (GWeakNotify) unset_trail, marker);
629 adg_model_add_dependency((AdgModel *) data->trail, entity);
631 set_n_segment(marker, n_segment);
634 return TRUE;
637 static void
638 unset_trail(AdgMarker *marker)
640 AdgMarkerPrivate *data = marker->data;
642 if (data->trail != NULL)
643 set_segment(marker, NULL, 0);
646 static gboolean
647 set_n_segment(AdgMarker *marker, guint n_segment)
649 AdgMarkerPrivate *data = marker->data;
651 if (n_segment == data->n_segment)
652 return FALSE;
654 if (data->backup_segment != NULL) {
655 /* Restore the original segment, if any */
656 if (data->trail != NULL)
657 adg_segment_deep_copy(&data->segment, data->backup_segment);
659 g_free(data->backup_segment);
660 data->backup_segment = NULL;
663 data->n_segment = n_segment;
665 if (n_segment == 0) {
666 memset(&data->segment, 0, sizeof(data->segment));
667 return TRUE;
670 return adg_trail_put_segment(data->trail, n_segment, &data->segment);
673 static gboolean
674 set_pos(AdgMarker *marker, gdouble pos)
676 AdgMarkerPrivate *data = marker->data;
678 if (pos == data->pos)
679 return FALSE;
681 data->pos = pos;
683 return TRUE;
686 static gboolean
687 set_size(AdgMarker *marker, gdouble size)
689 AdgMarkerPrivate *data = marker->data;
691 if (size == data->size)
692 return FALSE;
694 data->size = size;
696 return TRUE;
699 static gboolean
700 set_model(AdgMarker *marker, AdgModel *model)
702 AdgMarkerPrivate *data = marker->data;
704 if (model == data->model)
705 return FALSE;
707 if (data->model != NULL)
708 g_object_unref((GObject *) data->model);
710 data->model = model;
712 if (data->model != NULL) {
713 g_object_ref((GObject *) data->model);
714 adg_entity_local_changed((AdgEntity *) marker);
717 return TRUE;
720 static AdgModel *
721 create_model(AdgMarker *marker)
723 g_warning("%s: `create_model' method not implemented for type `%s'",
724 G_STRLOC, g_type_name(G_OBJECT_TYPE(marker)));
725 return NULL;