[AdgWidget] Reordered methods
[adg.git] / adg / adg-marker.c
blob41b6c604f4b9c88b1ed8844da62c972a28e7ebf2
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)
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_get_trail:
247 * @marker: an #AdgMarker
249 * Gets the trail where this marker should be applied.
251 * Returns: the trail owned by @marker or %NULL on errors
253 AdgTrail *
254 adg_marker_get_trail(AdgMarker *marker)
256 AdgMarkerPrivate *data;
258 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
260 data = marker->data;
262 return data->trail;
266 * adg_marker_get_n_segment:
267 * @marker: an #AdgMarker
269 * Returns the segment of the associated trail where this marker
270 * will be applied, where %1 is the first segment.
272 * Returns: an index greather than %0 on success or %0 on errors
274 guint
275 adg_marker_get_n_segment(AdgMarker *marker)
277 AdgMarkerPrivate *data;
279 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
281 data = marker->data;
283 return data->n_segment;
287 * adg_marker_get_segment:
288 * @marker: an #AdgMarker
290 * <note><para>
291 * This function is only useful in marker implementations.
292 * </para></note>
294 * Gets the segment where the marker will be applied. This segment
295 * is eventually a modified version of the backup segment, after
296 * having applied the marker.
298 * Returns: the segment or %NULL on errors
300 const AdgSegment *
301 adg_marker_get_segment(AdgMarker *marker)
303 AdgMarkerPrivate *data;
305 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
307 data = marker->data;
309 return &data->segment;
313 * adg_marker_set_segment:
314 * @marker: an #AdgMarker
315 * @trail: the #AdgTrail
316 * @n_segment: a segment index
318 * Sets the new segment where the marker should be applied. The weak
319 * reference to the old trail (if an old trail was present) is dropped
320 * while a new weak reference is added to @trail. If @trail is destroyed,
321 * the weak reference callback will automatically unset #AdgMarker:trai
322 * and will set #AdgMarker:n-segment to %0.
324 void
325 adg_marker_set_segment(AdgMarker *marker, AdgTrail *trail, guint n_segment)
327 g_return_if_fail(ADG_IS_MARKER(marker));
329 if (set_segment(marker, trail, n_segment))
330 g_object_notify((GObject *) marker, "trail");
334 * adg_marker_get_backup_segment:
335 * @marker: an #AdgMarker
337 * <note><para>
338 * This function is only useful in marker implementations.
339 * </para></note>
341 * Gets the original segment where the marker has been applied.
342 * Applying a marker could modify the underlying trail, usually
343 * by trimming the original segment of a #AdgMarker:size dependent
344 * length from the ends. The marker instance holds a copy of the
345 * original segment, generated by adg_marker_backup_segment(),
346 * to be used in recomputation, for example when the marker
347 * changes its size.
349 * When the subject segment is changed (either by changing
350 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
351 * is automatically restored.
353 * Returns: the original segment or %NULL on errors
355 const AdgSegment *
356 adg_marker_get_backup_segment(AdgMarker *marker)
358 AdgMarkerPrivate *data;
360 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
362 data = marker->data;
364 return data->backup_segment;
368 * adg_marker_backup_segment:
369 * @marker: an #AdgMarker
371 * <note><para>
372 * This function is only useful in marker implementations.
373 * </para></note>
375 * Duplicates the current subject segment for backup purpose: this
376 * segment can be accessed by adg_marker_get_backup_segment().
377 * Obviously, a current segment should exist (either the
378 * #AdgMarker:trail and #AdgMarker:n-segment properties must be
379 * properly defined) or this method will fail without further
380 * processing.
382 * When the subject segment is changed (either by changing
383 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
384 * is automatically restored.
386 void
387 adg_marker_backup_segment(AdgMarker *marker)
389 AdgMarkerPrivate *data;
391 g_return_if_fail(ADG_IS_MARKER(marker));
393 data = marker->data;
395 if (data->n_segment > 0) {
396 g_return_if_fail(data->trail != NULL);
398 g_free(data->backup_segment);
400 /* Backup the segment, if a segment to backup exists */
401 if (adg_trail_put_segment(data->trail, data->n_segment,
402 &data->segment))
403 data->backup_segment = adg_segment_deep_dup(&data->segment);
408 * adg_marker_get_pos:
409 * @marker: an #AdgMarker
411 * Gets the current position of @marker. The returned value is a ratio
412 * position referred to the segment associated to @marker: %0 means the
413 * start point and %1 means the end point of the segment.
415 * Returns: the marker position
417 gdouble
418 adg_marker_get_pos(AdgMarker *marker)
420 AdgMarkerPrivate *data;
422 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
424 data = marker->data;
426 return data->pos;
430 * adg_marker_set_pos:
431 * @marker: an #AdgMarker
432 * @pos: the new pos
434 * Sets a new position on @marker. Check out adg_marker_get_pos() for
435 * details on what @pos represents.
437 void
438 adg_marker_set_pos(AdgMarker *marker, gdouble pos)
440 g_return_if_fail(ADG_IS_MARKER(marker));
442 if (set_pos(marker, pos))
443 g_object_notify((GObject *) marker, "pos");
447 * adg_marker_get_size:
448 * @marker: an #AdgMarker
450 * Gets the current size of @marker.
452 * Returns: the marker size, in global space
454 gdouble
455 adg_marker_get_size(AdgMarker *marker)
457 AdgMarkerPrivate *data;
459 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
461 data = marker->data;
463 return data->size;
467 * adg_marker_set_size:
468 * @marker: an #AdgMarker
469 * @size: the new size
471 * Sets a new size on @marker. The @size is an implementation-dependent
472 * property: it has meaning only when used by an #AdgMarker derived type.
474 void
475 adg_marker_set_size(AdgMarker *marker, gdouble size)
477 g_return_if_fail(ADG_IS_MARKER(marker));
479 if (set_size(marker, size))
480 g_object_notify((GObject *) marker, "size");
484 * adg_marker_get_model:
485 * @marker: an #AdgMarker
487 * <note><para>
488 * This function is only useful in marker implementations.
489 * </para></note>
491 * Gets the current model of @marker. This is an accessor method:
492 * if you need to get the model for rendering, use adg_marker_model()
493 * instead.
495 * Returns: the cached model owned by @marker or %NULL on errors
497 AdgModel *
498 adg_marker_get_model(AdgMarker *marker)
500 AdgMarkerPrivate *data;
502 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
504 data = marker->data;
506 return data->model;
510 * adg_marker_set_model:
511 * @marker: an #AdgMarker
512 * @model: a new #AdgModel
514 * <note><para>
515 * This function is only useful in marker implementations.
516 * </para></note>
518 * Sets a new model for @marker. The reference to the old model (if an
519 * old model was present) is dropped while a new reference is added to
520 * @model.
522 void
523 adg_marker_set_model(AdgMarker *marker, AdgModel *model)
525 g_return_if_fail(ADG_IS_MARKER(marker));
527 if (set_model(marker, model))
528 g_object_notify((GObject *) marker, "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_pair_at(&data->segment, &pair, data->pos);
578 cpml_segment_vector_at(&data->segment, &vector, data->pos);
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;