[AdgStroke] Using the line style dress color
[adg.git] / adg / adg-marker.c
blobda7ac9ffdafc76fcff6d8cae447f252035eefe19
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_trail (AdgMarker *marker,
79 AdgTrail *trail);
80 static void unset_trail (AdgMarker *marker);
81 static gboolean set_n_segment (AdgMarker *marker,
82 guint n_segment);
83 static gboolean set_pos (AdgMarker *marker,
84 gdouble pos);
85 static gboolean set_size (AdgMarker *marker,
86 gdouble size);
87 static gboolean set_model (AdgMarker *marker,
88 AdgModel *model);
89 static AdgModel * create_model (AdgMarker *marker);
92 G_DEFINE_ABSTRACT_TYPE(AdgMarker, adg_marker, ADG_TYPE_ENTITY);
95 static void
96 adg_marker_class_init(AdgMarkerClass *klass)
98 GObjectClass *gobject_class;
99 AdgEntityClass *entity_class;
100 GParamSpec *param;
102 gobject_class = (GObjectClass *) klass;
103 entity_class = (AdgEntityClass *) klass;
105 g_type_class_add_private(klass, sizeof(AdgMarkerPrivate));
107 gobject_class->dispose = dispose;
108 gobject_class->set_property = set_property;
109 gobject_class->get_property = get_property;
111 entity_class->get_local_matrix = get_local_matrix;
112 entity_class->invalidate = invalidate;
114 klass->create_model = create_model;
116 param = g_param_spec_object("trail",
117 P_("Trail"),
118 P_("The subject AdgTrail for this marker"),
119 ADG_TYPE_TRAIL,
120 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
121 g_object_class_install_property(gobject_class, PROP_TRAIL, param);
123 param = g_param_spec_uint("n-segment",
124 P_("Segment Index"),
125 P_("The segment on trail where this marker should be applied (where 0 means undefined segment, 1 the first segment and so on)"),
126 0, G_MAXUINT, 0,
127 G_PARAM_READWRITE);
128 g_object_class_install_property(gobject_class, PROP_N_SEGMENT, param);
130 param = g_param_spec_double("pos",
131 P_("Position"),
132 P_("The position ratio inside the segment where to put the marker (0 means the start point while 1 means the end point)"),
133 0, 1, 0,
134 G_PARAM_READWRITE);
135 g_object_class_install_property(gobject_class, PROP_POS, param);
137 param = g_param_spec_double("size",
138 P_("Marker Size"),
139 P_("The size (in global space) of the marker"),
140 0, G_MAXDOUBLE, 10,
141 G_PARAM_READWRITE);
142 g_object_class_install_property(gobject_class, PROP_SIZE, param);
144 param = g_param_spec_object("model",
145 P_("Model"),
146 P_("A general purpose model usable by the marker implementations"),
147 ADG_TYPE_MODEL,
148 G_PARAM_READWRITE);
149 g_object_class_install_property(gobject_class, PROP_MODEL, param);
152 static void
153 adg_marker_init(AdgMarker *marker)
155 AdgMarkerPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(marker,
156 ADG_TYPE_MARKER,
157 AdgMarkerPrivate);
158 data->trail = NULL;
159 data->n_segment = 0;
160 data->backup_segment = NULL;
161 memset(&data->segment, 0, sizeof(data->segment));
162 data->pos = 0;
163 data->size = 10;
164 data->model = NULL;
166 marker->data = data;
169 static void
170 dispose(GObject *object)
172 AdgMarker *marker = (AdgMarker *) object;
174 adg_marker_set_model(marker, NULL);
175 adg_marker_set_trail(marker, NULL);
177 if (PARENT_OBJECT_CLASS->dispose != NULL)
178 PARENT_OBJECT_CLASS->dispose(object);
182 static void
183 get_property(GObject *object,
184 guint prop_id, GValue *value, GParamSpec *pspec)
186 AdgMarkerPrivate *data = ((AdgMarker *) object)->data;
188 switch (prop_id) {
189 case PROP_TRAIL:
190 g_value_set_object(value, data->trail);
191 break;
192 case PROP_N_SEGMENT:
193 g_value_set_uint(value, data->n_segment);
194 break;
195 case PROP_POS:
196 g_value_set_double(value, data->pos);
197 break;
198 case PROP_SIZE:
199 g_value_set_double(value, data->size);
200 break;
201 case PROP_MODEL:
202 g_value_set_object(value, data->model);
203 break;
204 default:
205 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
206 break;
210 static void
211 set_property(GObject *object,
212 guint prop_id, const GValue *value, GParamSpec *pspec)
214 AdgMarker *marker = (AdgMarker *) object;
216 switch (prop_id) {
217 case PROP_TRAIL:
218 set_trail(marker, g_value_get_object(value));
219 break;
220 case PROP_N_SEGMENT:
221 set_n_segment(marker, g_value_get_uint(value));
222 break;
223 case PROP_POS:
224 set_pos(marker, g_value_get_double(value));
225 break;
226 case PROP_SIZE:
227 set_size(marker, g_value_get_double(value));
228 break;
229 case PROP_MODEL:
230 set_model(marker, g_value_get_object(value));
231 break;
232 default:
233 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
234 break;
240 * adg_marker_get_trail:
241 * @marker: an #AdgMarker
243 * Gets the trail where this marker should be applied.
245 * Returns: the trail owned by @marker or %NULL on errors
247 AdgTrail *
248 adg_marker_get_trail(AdgMarker *marker)
250 AdgMarkerPrivate *data;
252 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
254 data = marker->data;
256 return data->trail;
260 * adg_marker_set_trail:
261 * @marker: an #AdgMarker
262 * @trail: the new #AdgTrail
264 * Sets a new trail where the marker should be applied. The weak reference
265 * to the old trail (if an old trail was present) is dropped while a new
266 * weak reference is added to @trail. If @trail is destroyed, the weak
267 * reference callback will automatically unset #AdgMarker:trail and will
268 * set #AdgMarker:n-segment to %0.
270 * After setting a new trail, the #AdgMarker:n-segment property is
271 * reset to %1. This means the first segment of the trail is always
272 * selected by default.
274 void
275 adg_marker_set_trail(AdgMarker *marker, AdgTrail *trail)
277 g_return_if_fail(ADG_IS_MARKER(marker));
279 if (set_trail(marker, trail))
280 g_object_notify((GObject *) marker, "trail");
284 * adg_marker_get_n_segment:
285 * @marker: an #AdgMarker
287 * Returns the segment of the associated trail where this marker
288 * will be applied, where %1 is the first segment.
290 * Returns: an index greather than %0 on success or %0 on errors
292 guint
293 adg_marker_get_n_segment(AdgMarker *marker)
295 AdgMarkerPrivate *data;
297 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
299 data = marker->data;
301 return data->n_segment;
305 * adg_marker_set_n_segment:
306 * @marker: an #AdgMarker
307 * @n_segment: a new segment index
309 * Sets a new segment to use. @n_segment is expected to be greather than
310 * %0 and to not exceed the number of segments in the underlying trail.
311 * By convention, %1 is the first segment.
313 void
314 adg_marker_set_n_segment(AdgMarker *marker, guint n_segment)
316 g_return_if_fail(ADG_IS_MARKER(marker));
318 if (set_n_segment(marker, n_segment))
319 g_object_notify((GObject *) marker, "n-segment");
323 * adg_marker_get_backup_segment:
324 * @marker: an #AdgMarker
326 * <note><para>
327 * This function is only useful in marker implementations.
328 * </para></note>
330 * Gets the original segment where the marker has been applied.
331 * Applying a marker could modify the underlying trail, usually
332 * by trimming the original segment of a #AdgMarker:size dependent
333 * length from the ends. The marker instance holds a copy of the
334 * original segment, generated by adg_marker_backup_segment(),
335 * to be used in recomputation, for example when the marker
336 * changes its size.
338 * When the subject segment is changed (either by changing
339 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
340 * is automatically restored.
342 * Returns: the original segment or %NULL on errors
344 const AdgSegment *
345 adg_marker_get_backup_segment(AdgMarker *marker)
347 AdgMarkerPrivate *data;
349 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
351 data = marker->data;
353 return data->backup_segment;
357 * adg_marker_backup_segment:
358 * @marker: an #AdgMarker
360 * <note><para>
361 * This function is only useful in marker implementations.
362 * </para></note>
364 * Duplicates the current subject segment for backup purpose: this
365 * segment can be accessed by adg_marker_get_backup_segment().
366 * Obviously, a current segment should exist (either the
367 * #AdgMarker:trail and #AdgMarker:n-segment properties must be
368 * properly defined) or this method will fail without further
369 * processing.
371 * When the subject segment is changed (either by changing
372 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
373 * is automatically restored.
375 void
376 adg_marker_backup_segment(AdgMarker *marker)
378 AdgMarkerPrivate *data;
380 g_return_if_fail(ADG_IS_MARKER(marker));
382 data = marker->data;
384 if (data->n_segment > 0) {
385 g_return_if_fail(data->trail != NULL);
387 g_free(data->backup_segment);
389 /* Backup the segment, if a segment to backup exists */
390 if (adg_trail_get_segment(data->trail,
391 &data->segment, data->n_segment))
392 data->backup_segment = adg_segment_deep_dup(&data->segment);
397 * adg_marker_get_segment:
398 * @marker: an #AdgMarker
400 * <note><para>
401 * This function is only useful in marker implementations.
402 * </para></note>
404 * Gets the segment where the marker will be applied. This segment
405 * is eventually a modified version of the backup segment, after
406 * having applied the marker.
408 * Returns: the segment or %NULL on errors
410 AdgSegment *
411 adg_marker_get_segment(AdgMarker *marker)
413 AdgMarkerPrivate *data;
415 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
417 data = marker->data;
419 return &data->segment;
423 * adg_marker_get_pos:
424 * @marker: an #AdgMarker
426 * Gets the current position of @marker. The returned value is a ratio
427 * position referred to the segment associated to @marker: %0 means the
428 * start point and %1 means the end point of the segment.
430 * Returns: the marker position
432 gdouble
433 adg_marker_get_pos(AdgMarker *marker)
435 AdgMarkerPrivate *data;
437 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
439 data = marker->data;
441 return data->pos;
445 * adg_marker_set_pos:
446 * @marker: an #AdgMarker
447 * @pos: the new pos
449 * Sets a new position on @marker. Check out adg_marker_get_pos() for
450 * details on what @pos represents.
452 void
453 adg_marker_set_pos(AdgMarker *marker, gdouble pos)
455 g_return_if_fail(ADG_IS_MARKER(marker));
457 if (set_pos(marker, pos))
458 g_object_notify((GObject *) marker, "pos");
462 * adg_marker_get_size:
463 * @marker: an #AdgMarker
465 * Gets the current size of @marker.
467 * Returns: the marker size, in global space
469 gdouble
470 adg_marker_get_size(AdgMarker *marker)
472 AdgMarkerPrivate *data;
474 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
476 data = marker->data;
478 return data->size;
482 * adg_marker_set_size:
483 * @marker: an #AdgMarker
484 * @size: the new size
486 * Sets a new size on @marker. The @size is an implementation-dependent
487 * property: it has meaning only when used by an #AdgMarker derived type.
489 void
490 adg_marker_set_size(AdgMarker *marker, gdouble size)
492 g_return_if_fail(ADG_IS_MARKER(marker));
494 if (set_size(marker, size))
495 g_object_notify((GObject *) marker, "size");
499 * adg_marker_model:
500 * @marker: an #AdgMarker
502 * <note><para>
503 * This function is only useful in marker implementations.
504 * </para></note>
506 * Gets the model of @marker. If the model is not found, it is
507 * automatically created by calling the create_model() virtual method.
509 * Returns: the current model owned by @marker or %NULL on errors
511 AdgModel *
512 adg_marker_model(AdgMarker *marker)
514 AdgMarkerPrivate *data;
516 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
518 data = marker->data;
520 if (data->model == NULL) {
521 /* Model not found: regenerate it */
522 AdgMarkerClass *marker_class = ADG_MARKER_GET_CLASS(marker);
524 adg_marker_set_model(marker, marker_class->create_model(marker));
527 return data->model;
531 * adg_marker_get_model:
532 * @marker: an #AdgMarker
534 * <note><para>
535 * This function is only useful in marker implementations.
536 * </para></note>
538 * Gets the current model of @marker. This is an accessor method:
539 * if you need to get the model for rendering, use adg_marker_model()
540 * instead.
542 * Returns: the cached model owned by @marker or %NULL on errors
544 AdgModel *
545 adg_marker_get_model(AdgMarker *marker)
547 AdgMarkerPrivate *data;
549 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
551 data = marker->data;
553 return data->model;
557 * adg_marker_set_model:
558 * @marker: an #AdgMarker
559 * @model: a new #AdgModel
561 * <note><para>
562 * This function is only useful in marker implementations.
563 * </para></note>
565 * Sets a new model for @marker. The reference to the old model (if an
566 * old model was present) is dropped while a new reference is added to
567 * @model.
569 void
570 adg_marker_set_model(AdgMarker *marker, AdgModel *model)
572 g_return_if_fail(ADG_IS_MARKER(marker));
574 if (set_model(marker, model))
575 g_object_notify((GObject *) marker, "model");
579 static void
580 get_local_matrix(AdgEntity *entity, AdgMatrix *matrix)
582 AdgMarkerPrivate *data;
583 CpmlPair pair;
584 CpmlVector vector;
585 AdgMatrix tmp;
587 data = ((AdgMarker *) entity)->data;
588 cpml_segment_pair_at(&data->segment, &pair, data->pos);
589 cpml_segment_vector_at(&data->segment, &vector, data->pos);
590 cpml_vector_set_length(&vector, data->size);
592 if (data->pos > 0.5) {
593 vector.x = -vector.x;
594 vector.y = -vector.y;
597 cairo_matrix_init (&tmp, vector.x, vector.y, -vector.y, vector.x, 0, 0);
599 cairo_matrix_init_translate(matrix, pair.x, pair.y);
600 cairo_matrix_multiply (matrix, &tmp, matrix);
603 static gboolean
604 invalidate(AdgEntity *entity)
606 AdgMarker *marker = (AdgMarker *) entity;
608 adg_marker_set_model(marker, NULL);
610 return TRUE;
614 static gboolean
615 set_trail(AdgMarker *marker, AdgTrail *trail)
617 AdgMarkerPrivate *data;
618 AdgEntity *entity;
620 data = marker->data;
622 if (trail == data->trail)
623 return FALSE;
625 entity = (AdgEntity *) marker;
627 if (data->trail != NULL) {
628 /* Restore the original segment in the old trail */
629 set_n_segment(marker, 0);
631 g_object_weak_unref((GObject *) data->trail,
632 (GWeakNotify) unset_trail, marker);
633 adg_model_remove_dependency((AdgModel *) data->trail, entity);
636 data->trail = trail;
637 data->n_segment = 0;
639 if (data->trail != NULL) {
640 g_object_weak_ref((GObject *) data->trail,
641 (GWeakNotify) unset_trail, marker);
642 adg_model_add_dependency((AdgModel *) data->trail, entity);
644 /* Set the first segment by default */
645 set_n_segment(marker, 1);
648 return TRUE;
651 static void
652 unset_trail(AdgMarker *marker)
654 AdgMarkerPrivate *data = marker->data;
656 if (data->trail != NULL) {
657 data->trail = NULL;
658 set_n_segment(marker, 0);
662 static gboolean
663 set_n_segment(AdgMarker *marker, guint n_segment)
665 AdgMarkerPrivate *data = marker->data;
667 if (n_segment == data->n_segment)
668 return FALSE;
670 if (data->backup_segment != NULL) {
671 /* Restore the original segment, if any */
672 if (data->trail != NULL)
673 adg_segment_deep_copy(&data->segment, data->backup_segment);
675 g_free(data->backup_segment);
676 data->backup_segment = NULL;
679 data->n_segment = n_segment;
681 return adg_trail_get_segment(data->trail, &data->segment, n_segment);
684 static gboolean
685 set_pos(AdgMarker *marker, gdouble pos)
687 AdgMarkerPrivate *data = marker->data;
689 if (pos == data->pos)
690 return FALSE;
692 data->pos = pos;
694 return TRUE;
697 static gboolean
698 set_size(AdgMarker *marker, gdouble size)
700 AdgMarkerPrivate *data = marker->data;
702 if (size == data->size)
703 return FALSE;
705 data->size = size;
707 return TRUE;
710 static gboolean
711 set_model(AdgMarker *marker, AdgModel *model)
713 AdgMarkerPrivate *data = marker->data;
715 if (model == data->model)
716 return FALSE;
718 if (data->model != NULL)
719 g_object_unref((GObject *) data->model);
721 data->model = model;
723 if (data->model != NULL)
724 g_object_ref((GObject *) data->model);
726 return TRUE;
729 static AdgModel *
730 create_model(AdgMarker *marker)
732 g_warning("%s: `create_model' method not implemented for type `%s'",
733 G_STRLOC, g_type_name(G_OBJECT_TYPE(marker)));
734 return NULL;