[AdgMarker] set_trail+set_n_segment=set_segment
[adg.git] / adg / adg-marker.c
blobb138f9c65eea01f7689b59f3ac4f095dd4012360
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 * Use adg_marker_set_pos() to select the position where the marker
34 * should be put: %0 means the start point of the segment while %1
35 * means the end point.
37 * The #AdgMarker:model property and APIs are intended only for marker
38 * implementation purposes.
39 **/
41 /**
42 * AdgMarker:
44 * All fields are privates and should not be used directly.
45 * Use its public methods instead.
46 **/
49 #include "adg-marker.h"
50 #include "adg-marker-private.h"
51 #include "adg-intl.h"
53 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_marker_parent_class)
56 enum {
57 PROP_0,
58 PROP_TRAIL,
59 PROP_N_SEGMENT,
60 PROP_POS,
61 PROP_SIZE,
62 PROP_MODEL
66 static void dispose (GObject *object);
67 static void get_property (GObject *object,
68 guint prop_id,
69 GValue *value,
70 GParamSpec *pspec);
71 static void set_property (GObject *object,
72 guint prop_id,
73 const GValue *value,
74 GParamSpec *pspec);
75 static void get_local_matrix (AdgEntity *entity,
76 AdgMatrix *matrix);
77 static gboolean invalidate (AdgEntity *entity);
78 static gboolean set_segment (AdgMarker *marker,
79 AdgTrail *trail,
80 guint n_segment);
81 static void unset_trail (AdgMarker *marker);
82 static gboolean set_n_segment (AdgMarker *marker,
83 guint n_segment);
84 static gboolean set_pos (AdgMarker *marker,
85 gdouble pos);
86 static gboolean set_size (AdgMarker *marker,
87 gdouble size);
88 static gboolean set_model (AdgMarker *marker,
89 AdgModel *model);
90 static AdgModel * create_model (AdgMarker *marker);
93 G_DEFINE_ABSTRACT_TYPE(AdgMarker, adg_marker, ADG_TYPE_ENTITY);
96 static void
97 adg_marker_class_init(AdgMarkerClass *klass)
99 GObjectClass *gobject_class;
100 AdgEntityClass *entity_class;
101 GParamSpec *param;
103 gobject_class = (GObjectClass *) klass;
104 entity_class = (AdgEntityClass *) klass;
106 g_type_class_add_private(klass, sizeof(AdgMarkerPrivate));
108 gobject_class->dispose = dispose;
109 gobject_class->set_property = set_property;
110 gobject_class->get_property = get_property;
112 entity_class->get_local_matrix = get_local_matrix;
113 entity_class->invalidate = invalidate;
115 klass->create_model = create_model;
117 param = g_param_spec_object("trail",
118 P_("Trail"),
119 P_("The subject AdgTrail for this marker"),
120 ADG_TYPE_TRAIL,
121 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
122 g_object_class_install_property(gobject_class, PROP_TRAIL, param);
124 param = g_param_spec_uint("n-segment",
125 P_("Segment Index"),
126 P_("The segment on trail where this marker should be applied (where 0 means undefined segment, 1 the first segment and so on)"),
127 0, G_MAXUINT, 0,
128 G_PARAM_READWRITE);
129 g_object_class_install_property(gobject_class, PROP_N_SEGMENT, param);
131 param = g_param_spec_double("pos",
132 P_("Position"),
133 P_("The position ratio inside the segment where to put the marker (0 means the start point while 1 means the end point)"),
134 0, 1, 0,
135 G_PARAM_READWRITE);
136 g_object_class_install_property(gobject_class, PROP_POS, param);
138 param = g_param_spec_double("size",
139 P_("Marker Size"),
140 P_("The size (in global space) of the marker"),
141 0, G_MAXDOUBLE, 10,
142 G_PARAM_READWRITE);
143 g_object_class_install_property(gobject_class, PROP_SIZE, param);
145 param = g_param_spec_object("model",
146 P_("Model"),
147 P_("A general purpose model usable by the marker implementations"),
148 ADG_TYPE_MODEL,
149 G_PARAM_READWRITE);
150 g_object_class_install_property(gobject_class, PROP_MODEL, param);
153 static void
154 adg_marker_init(AdgMarker *marker)
156 AdgMarkerPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(marker,
157 ADG_TYPE_MARKER,
158 AdgMarkerPrivate);
159 data->trail = NULL;
160 data->n_segment = 0;
161 data->backup_segment = NULL;
162 memset(&data->segment, 0, sizeof(data->segment));
163 data->pos = 0;
164 data->size = 10;
165 data->model = NULL;
167 marker->data = data;
170 static void
171 dispose(GObject *object)
173 AdgMarker *marker = (AdgMarker *) object;
175 adg_marker_set_model(marker, NULL);
176 adg_marker_set_segment(marker, NULL, 0);
178 if (PARENT_OBJECT_CLASS->dispose != NULL)
179 PARENT_OBJECT_CLASS->dispose(object);
183 static void
184 get_property(GObject *object,
185 guint prop_id, GValue *value, GParamSpec *pspec)
187 AdgMarkerPrivate *data = ((AdgMarker *) object)->data;
189 switch (prop_id) {
190 case PROP_TRAIL:
191 g_value_set_object(value, data->trail);
192 break;
193 case PROP_N_SEGMENT:
194 g_value_set_uint(value, data->n_segment);
195 break;
196 case PROP_POS:
197 g_value_set_double(value, data->pos);
198 break;
199 case PROP_SIZE:
200 g_value_set_double(value, data->size);
201 break;
202 case PROP_MODEL:
203 g_value_set_object(value, data->model);
204 break;
205 default:
206 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
207 break;
211 static void
212 set_property(GObject *object,
213 guint prop_id, const GValue *value, GParamSpec *pspec)
215 AdgMarker *marker = (AdgMarker *) object;
217 switch (prop_id) {
218 case PROP_TRAIL:
219 set_segment(marker, g_value_get_object(value), 1);
220 break;
221 case PROP_N_SEGMENT:
222 set_n_segment(marker, g_value_get_uint(value));
223 break;
224 case PROP_POS:
225 set_pos(marker, g_value_get_double(value));
226 break;
227 case PROP_SIZE:
228 set_size(marker, g_value_get_double(value));
229 break;
230 case PROP_MODEL:
231 set_model(marker, g_value_get_object(value));
232 break;
233 default:
234 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
235 break;
241 * adg_marker_get_trail:
242 * @marker: an #AdgMarker
244 * Gets the trail where this marker should be applied.
246 * Returns: the trail owned by @marker or %NULL on errors
248 AdgTrail *
249 adg_marker_get_trail(AdgMarker *marker)
251 AdgMarkerPrivate *data;
253 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
255 data = marker->data;
257 return data->trail;
261 * adg_marker_get_n_segment:
262 * @marker: an #AdgMarker
264 * Returns the segment of the associated trail where this marker
265 * will be applied, where %1 is the first segment.
267 * Returns: an index greather than %0 on success or %0 on errors
269 guint
270 adg_marker_get_n_segment(AdgMarker *marker)
272 AdgMarkerPrivate *data;
274 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
276 data = marker->data;
278 return data->n_segment;
282 * adg_marker_get_segment:
283 * @marker: an #AdgMarker
285 * <note><para>
286 * This function is only useful in marker implementations.
287 * </para></note>
289 * Gets the segment where the marker will be applied. This segment
290 * is eventually a modified version of the backup segment, after
291 * having applied the marker.
293 * Returns: the segment or %NULL on errors
295 AdgSegment *
296 adg_marker_get_segment(AdgMarker *marker)
298 AdgMarkerPrivate *data;
300 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
302 data = marker->data;
304 return &data->segment;
308 * adg_marker_set_segment:
309 * @marker: an #AdgMarker
310 * @trail: the #AdgTrail
311 * @n_segment: a segment index
313 * Sets the new segment where the marker should be applied. The weak
314 * reference to the old trail (if an old trail was present) is dropped
315 * while a new weak reference is added to @trail. If @trail is destroyed,
316 * the weak reference callback will automatically unset #AdgMarker:trai
317 * and will set #AdgMarker:n-segment to %0.
319 void
320 adg_marker_set_segment(AdgMarker *marker, AdgTrail *trail, guint n_segment)
322 g_return_if_fail(ADG_IS_MARKER(marker));
324 if (set_segment(marker, trail, n_segment))
325 g_object_notify((GObject *) marker, "trail");
329 * adg_marker_get_backup_segment:
330 * @marker: an #AdgMarker
332 * <note><para>
333 * This function is only useful in marker implementations.
334 * </para></note>
336 * Gets the original segment where the marker has been applied.
337 * Applying a marker could modify the underlying trail, usually
338 * by trimming the original segment of a #AdgMarker:size dependent
339 * length from the ends. The marker instance holds a copy of the
340 * original segment, generated by adg_marker_backup_segment(),
341 * to be used in recomputation, for example when the marker
342 * changes its size.
344 * When the subject segment is changed (either by changing
345 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
346 * is automatically restored.
348 * Returns: the original segment or %NULL on errors
350 const AdgSegment *
351 adg_marker_get_backup_segment(AdgMarker *marker)
353 AdgMarkerPrivate *data;
355 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
357 data = marker->data;
359 return data->backup_segment;
363 * adg_marker_backup_segment:
364 * @marker: an #AdgMarker
366 * <note><para>
367 * This function is only useful in marker implementations.
368 * </para></note>
370 * Duplicates the current subject segment for backup purpose: this
371 * segment can be accessed by adg_marker_get_backup_segment().
372 * Obviously, a current segment should exist (either the
373 * #AdgMarker:trail and #AdgMarker:n-segment properties must be
374 * properly defined) or this method will fail without further
375 * processing.
377 * When the subject segment is changed (either by changing
378 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
379 * is automatically restored.
381 void
382 adg_marker_backup_segment(AdgMarker *marker)
384 AdgMarkerPrivate *data;
386 g_return_if_fail(ADG_IS_MARKER(marker));
388 data = marker->data;
390 if (data->n_segment > 0) {
391 g_return_if_fail(data->trail != NULL);
393 g_free(data->backup_segment);
395 /* Backup the segment, if a segment to backup exists */
396 if (adg_trail_get_segment(data->trail,
397 &data->segment, data->n_segment))
398 data->backup_segment = adg_segment_deep_dup(&data->segment);
403 * adg_marker_get_pos:
404 * @marker: an #AdgMarker
406 * Gets the current position of @marker. The returned value is a ratio
407 * position referred to the segment associated to @marker: %0 means the
408 * start point and %1 means the end point of the segment.
410 * Returns: the marker position
412 gdouble
413 adg_marker_get_pos(AdgMarker *marker)
415 AdgMarkerPrivate *data;
417 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
419 data = marker->data;
421 return data->pos;
425 * adg_marker_set_pos:
426 * @marker: an #AdgMarker
427 * @pos: the new pos
429 * Sets a new position on @marker. Check out adg_marker_get_pos() for
430 * details on what @pos represents.
432 void
433 adg_marker_set_pos(AdgMarker *marker, gdouble pos)
435 g_return_if_fail(ADG_IS_MARKER(marker));
437 if (set_pos(marker, pos))
438 g_object_notify((GObject *) marker, "pos");
442 * adg_marker_get_size:
443 * @marker: an #AdgMarker
445 * Gets the current size of @marker.
447 * Returns: the marker size, in global space
449 gdouble
450 adg_marker_get_size(AdgMarker *marker)
452 AdgMarkerPrivate *data;
454 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
456 data = marker->data;
458 return data->size;
462 * adg_marker_set_size:
463 * @marker: an #AdgMarker
464 * @size: the new size
466 * Sets a new size on @marker. The @size is an implementation-dependent
467 * property: it has meaning only when used by an #AdgMarker derived type.
469 void
470 adg_marker_set_size(AdgMarker *marker, gdouble size)
472 g_return_if_fail(ADG_IS_MARKER(marker));
474 if (set_size(marker, size))
475 g_object_notify((GObject *) marker, "size");
479 * adg_marker_model:
480 * @marker: an #AdgMarker
482 * <note><para>
483 * This function is only useful in marker implementations.
484 * </para></note>
486 * Gets the model of @marker. If the model is not found, it is
487 * automatically created by calling the create_model() virtual method.
489 * Returns: the current model owned by @marker or %NULL on errors
491 AdgModel *
492 adg_marker_model(AdgMarker *marker)
494 AdgMarkerPrivate *data;
496 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
498 data = marker->data;
500 if (data->model == NULL) {
501 /* Model not found: regenerate it */
502 AdgMarkerClass *marker_class = ADG_MARKER_GET_CLASS(marker);
504 adg_marker_set_model(marker, marker_class->create_model(marker));
507 return data->model;
511 * adg_marker_get_model:
512 * @marker: an #AdgMarker
514 * <note><para>
515 * This function is only useful in marker implementations.
516 * </para></note>
518 * Gets the current model of @marker. This is an accessor method:
519 * if you need to get the model for rendering, use adg_marker_model()
520 * instead.
522 * Returns: the cached model owned by @marker or %NULL on errors
524 AdgModel *
525 adg_marker_get_model(AdgMarker *marker)
527 AdgMarkerPrivate *data;
529 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
531 data = marker->data;
533 return data->model;
537 * adg_marker_set_model:
538 * @marker: an #AdgMarker
539 * @model: a new #AdgModel
541 * <note><para>
542 * This function is only useful in marker implementations.
543 * </para></note>
545 * Sets a new model for @marker. The reference to the old model (if an
546 * old model was present) is dropped while a new reference is added to
547 * @model.
549 void
550 adg_marker_set_model(AdgMarker *marker, AdgModel *model)
552 g_return_if_fail(ADG_IS_MARKER(marker));
554 if (set_model(marker, model))
555 g_object_notify((GObject *) marker, "model");
559 static void
560 get_local_matrix(AdgEntity *entity, AdgMatrix *matrix)
562 AdgMarkerPrivate *data;
563 CpmlPair pair;
564 CpmlVector vector;
565 AdgMatrix tmp;
567 data = ((AdgMarker *) entity)->data;
568 cpml_segment_pair_at(&data->segment, &pair, data->pos);
569 cpml_segment_vector_at(&data->segment, &vector, data->pos);
570 cpml_vector_set_length(&vector, data->size);
572 if (data->pos > 0.5) {
573 vector.x = -vector.x;
574 vector.y = -vector.y;
577 cairo_matrix_init (&tmp, vector.x, vector.y, -vector.y, vector.x, 0, 0);
579 cairo_matrix_init_translate(matrix, pair.x, pair.y);
580 cairo_matrix_multiply (matrix, &tmp, matrix);
583 static gboolean
584 invalidate(AdgEntity *entity)
586 AdgMarker *marker = (AdgMarker *) entity;
588 adg_marker_set_model(marker, NULL);
590 return TRUE;
594 static gboolean
595 set_segment(AdgMarker *marker, AdgTrail *trail, guint n_segment)
597 AdgMarkerPrivate *data;
598 AdgEntity *entity;
600 data = marker->data;
601 entity = (AdgEntity *) marker;
603 if (trail == data->trail && n_segment == data->n_segment)
604 return FALSE;
606 if (data->trail != NULL) {
607 /* Restore the original segment in the old trail */
608 set_n_segment(marker, 0);
610 g_object_weak_unref((GObject *) data->trail,
611 (GWeakNotify) unset_trail, marker);
612 adg_model_remove_dependency((AdgModel *) data->trail, entity);
615 data->trail = trail;
616 data->n_segment = 0;
618 if (data->trail != NULL) {
619 g_object_weak_ref((GObject *) data->trail,
620 (GWeakNotify) unset_trail, marker);
621 adg_model_add_dependency((AdgModel *) data->trail, entity);
623 set_n_segment(marker, n_segment);
626 return TRUE;
629 static void
630 unset_trail(AdgMarker *marker)
632 AdgMarkerPrivate *data = marker->data;
634 if (data->trail != NULL) {
635 data->trail = NULL;
636 set_n_segment(marker, 0);
640 static gboolean
641 set_n_segment(AdgMarker *marker, guint n_segment)
643 AdgMarkerPrivate *data = marker->data;
645 if (n_segment == data->n_segment)
646 return FALSE;
648 if (data->backup_segment != NULL) {
649 /* Restore the original segment, if any */
650 if (data->trail != NULL)
651 adg_segment_deep_copy(&data->segment, data->backup_segment);
653 g_free(data->backup_segment);
654 data->backup_segment = NULL;
657 data->n_segment = n_segment;
659 return adg_trail_get_segment(data->trail, &data->segment, n_segment);
662 static gboolean
663 set_pos(AdgMarker *marker, gdouble pos)
665 AdgMarkerPrivate *data = marker->data;
667 if (pos == data->pos)
668 return FALSE;
670 data->pos = pos;
672 return TRUE;
675 static gboolean
676 set_size(AdgMarker *marker, gdouble size)
678 AdgMarkerPrivate *data = marker->data;
680 if (size == data->size)
681 return FALSE;
683 data->size = size;
685 return TRUE;
688 static gboolean
689 set_model(AdgMarker *marker, AdgModel *model)
691 AdgMarkerPrivate *data = marker->data;
693 if (model == data->model)
694 return FALSE;
696 if (data->model != NULL)
697 g_object_unref((GObject *) data->model);
699 data->model = model;
701 if (data->model != NULL)
702 g_object_ref((GObject *) data->model);
704 return TRUE;
707 static AdgModel *
708 create_model(AdgMarker *marker)
710 g_warning("%s: `create_model' method not implemented for type `%s'",
711 G_STRLOC, g_type_name(G_OBJECT_TYPE(marker)));
712 return NULL;