1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010 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.
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.
49 * All fields are privates and should not be used directly.
50 * Use its public methods instead.
54 #include "adg-internal.h"
55 #include "adg-marker.h"
56 #include "adg-marker-private.h"
59 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_marker_parent_class)
60 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_marker_parent_class)
73 static void dispose (GObject
*object
);
74 static void get_property (GObject
*object
,
78 static void set_property (GObject
*object
,
82 static void local_changed (AdgEntity
*entity
);
83 static void invalidate (AdgEntity
*entity
);
84 static gboolean
set_trail (AdgMarker
*marker
,
86 static void unset_trail (AdgMarker
*marker
);
87 static gboolean
set_n_segment (AdgMarker
*marker
,
89 static gboolean
set_segment (AdgMarker
*marker
,
92 static gboolean
set_pos (AdgMarker
*marker
,
94 static gboolean
set_size (AdgMarker
*marker
,
96 static gboolean
set_model (AdgMarker
*marker
,
98 static AdgModel
* create_model (AdgMarker
*marker
);
101 G_DEFINE_ABSTRACT_TYPE(AdgMarker
, adg_marker
, ADG_TYPE_ENTITY
);
105 adg_marker_class_init(AdgMarkerClass
*klass
)
107 GObjectClass
*gobject_class
;
108 AdgEntityClass
*entity_class
;
111 gobject_class
= (GObjectClass
*) klass
;
112 entity_class
= (AdgEntityClass
*) klass
;
114 g_type_class_add_private(klass
, sizeof(AdgMarkerPrivate
));
116 gobject_class
->dispose
= dispose
;
117 gobject_class
->set_property
= set_property
;
118 gobject_class
->get_property
= get_property
;
120 entity_class
->local_changed
= local_changed
;
121 entity_class
->invalidate
= invalidate
;
123 klass
->create_model
= create_model
;
125 param
= g_param_spec_object("trail",
127 P_("The subject AdgTrail for this marker"),
129 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
);
130 g_object_class_install_property(gobject_class
, PROP_TRAIL
, param
);
132 param
= g_param_spec_uint("n-segment",
134 P_("The segment on trail where this marker should be applied (where 0 means undefined segment, 1 the first segment and so on)"),
137 g_object_class_install_property(gobject_class
, PROP_N_SEGMENT
, param
);
139 param
= g_param_spec_double("pos",
141 P_("The position ratio inside the segment where to put the marker (0 means the start point while 1 means the end point)"),
143 G_PARAM_READWRITE
|G_PARAM_CONSTRUCT
);
144 g_object_class_install_property(gobject_class
, PROP_POS
, param
);
146 param
= g_param_spec_double("size",
148 P_("The size (in global space) of the marker"),
151 g_object_class_install_property(gobject_class
, PROP_SIZE
, param
);
153 param
= g_param_spec_object("model",
155 P_("A general purpose model usable by the marker implementations"),
158 g_object_class_install_property(gobject_class
, PROP_MODEL
, param
);
162 adg_marker_init(AdgMarker
*marker
)
164 AdgMarkerPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(marker
,
169 data
->backup_segment
= NULL
;
170 memset(&data
->segment
, 0, sizeof(data
->segment
));
179 dispose(GObject
*object
)
181 AdgMarker
*marker
= (AdgMarker
*) object
;
183 adg_marker_set_model(marker
, NULL
);
184 adg_marker_set_segment(marker
, NULL
, 0);
186 if (PARENT_OBJECT_CLASS
->dispose
)
187 PARENT_OBJECT_CLASS
->dispose(object
);
192 get_property(GObject
*object
,
193 guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
195 AdgMarkerPrivate
*data
= ((AdgMarker
*) object
)->data
;
199 g_value_set_object(value
, data
->trail
);
202 g_value_set_uint(value
, data
->n_segment
);
205 g_value_set_double(value
, data
->pos
);
208 g_value_set_double(value
, data
->size
);
211 g_value_set_object(value
, data
->model
);
214 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
220 set_property(GObject
*object
,
221 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
223 AdgMarker
*marker
= (AdgMarker
*) object
;
227 set_trail(marker
, g_value_get_object(value
));
230 set_n_segment(marker
, g_value_get_uint(value
));
233 set_pos(marker
, g_value_get_double(value
));
236 set_size(marker
, g_value_get_double(value
));
239 set_model(marker
, g_value_get_object(value
));
242 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
249 * adg_marker_set_trail:
250 * @marker: an #AdgMarker
251 * @trail: the new trail to use
253 * Sets the #AdgMarker:trail property to @trail. It is allowed to pass
254 * %NULL to unset the current trail.
256 * This method could fail unexpectedly if the segment index specified
257 * by the #AdgMarker:n-segment property is not present inside the new
258 * segment: if you want to set a new segment it is more convenient to
259 * change both properties (#AdgMarker:trail and #AdgMarker:n-segment)
260 * at once with adg_marker_set_segment().
263 adg_marker_set_trail(AdgMarker
*marker
, AdgTrail
*trail
)
265 g_return_if_fail(ADG_IS_MARKER(marker
));
267 if (set_trail(marker
, trail
))
268 g_object_notify((GObject
*) marker
, "trail");
272 * adg_marker_get_trail:
273 * @marker: an #AdgMarker
275 * Gets the trail where this marker should be applied.
277 * Returns: the trail owned by @marker or %NULL on errors
280 adg_marker_get_trail(AdgMarker
*marker
)
282 AdgMarkerPrivate
*data
;
284 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
292 * adg_marker_set_n_segment:
293 * @marker: an #AdgMarker
294 * @n_segment: the new segment index
296 * Sets the #AdgMarker:n-segment property to @n_segment. The trail
297 * is unchanged. If you want to set both properties at once (as
298 * usually requested to refer to a specific segment),
299 * adg_marker_set_segment() should be more convenient.
302 adg_marker_set_n_segment(AdgMarker
*marker
, guint n_segment
)
304 AdgMarkerPrivate
*data
;
306 g_return_if_fail(ADG_IS_MARKER(marker
));
310 if (set_n_segment(marker
, n_segment
))
311 g_object_notify((GObject
*) marker
, "n-segment");
315 * adg_marker_get_n_segment:
316 * @marker: an #AdgMarker
318 * Returns the segment of the associated trail where this marker
319 * will be applied, where %1 is the first segment.
321 * Returns: an index greather than %0 on success or %0 on errors
324 adg_marker_get_n_segment(AdgMarker
*marker
)
326 AdgMarkerPrivate
*data
;
328 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
332 return data
->n_segment
;
336 * adg_marker_set_segment:
337 * @marker: an #AdgMarker
338 * @trail: the #AdgTrail
339 * @n_segment: a segment index
341 * Sets a new segment where the marker should be applied at once. The weak
342 * reference to the old trail (if an old trail was present) is dropped
343 * while a new weak reference is added to @trail. If @trail is destroyed,
344 * the weak reference callback will automatically unset #AdgMarker:trail
345 * and will set #AdgMarker:n-segment to %0.
348 adg_marker_set_segment(AdgMarker
*marker
, AdgTrail
*trail
, guint n_segment
)
350 g_return_if_fail(ADG_IS_MARKER(marker
));
352 if (set_segment(marker
, trail
, n_segment
)) {
353 GObject
*object
= (GObject
*) marker
;
354 g_object_notify(object
, "trail");
355 g_object_notify(object
, "n-segment");
360 * adg_marker_get_segment:
361 * @marker: an #AdgMarker
364 * This function is only useful in marker implementations.
367 * Gets the segment where the marker will be applied. This segment
368 * is eventually a modified version of the backup segment, after
369 * having applied the marker.
371 * Returns: the segment or %NULL on errors
374 adg_marker_get_segment(AdgMarker
*marker
)
376 AdgMarkerPrivate
*data
;
378 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
382 return &data
->segment
;
386 * adg_marker_backup_segment:
387 * @marker: an #AdgMarker
390 * This function is only useful in marker implementations.
393 * Duplicates the current subject segment for backup purpose: this
394 * segment can be accessed by adg_marker_get_backup_segment().
395 * Obviously, a current segment should exist (either the
396 * #AdgMarker:trail and #AdgMarker:n-segment properties must be
397 * properly defined) or this method will fail without further
400 * When the subject segment is changed (either by changing
401 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
402 * is automatically restored.
405 adg_marker_backup_segment(AdgMarker
*marker
)
407 AdgMarkerPrivate
*data
;
409 g_return_if_fail(ADG_IS_MARKER(marker
));
413 if (data
->n_segment
> 0) {
414 g_return_if_fail(data
->trail
!= NULL
);
416 g_free(data
->backup_segment
);
418 /* Backup the segment, if a segment to backup exists */
419 if (adg_trail_put_segment(data
->trail
, data
->n_segment
,
421 data
->backup_segment
= adg_segment_deep_dup(&data
->segment
);
426 * adg_marker_get_backup_segment:
427 * @marker: an #AdgMarker
430 * This function is only useful in marker implementations.
433 * Gets the original segment where the marker has been applied.
434 * Applying a marker could modify the underlying trail, usually
435 * by trimming the original segment of a #AdgMarker:size dependent
436 * length from the ends. The marker instance holds a copy of the
437 * original segment, generated by adg_marker_backup_segment(),
438 * to be used in recomputation, for example when the marker
441 * When the subject segment is changed (either by changing
442 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
443 * is automatically restored.
445 * Returns: the original segment or %NULL on errors
448 adg_marker_get_backup_segment(AdgMarker
*marker
)
450 AdgMarkerPrivate
*data
;
452 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
456 return data
->backup_segment
;
460 * adg_marker_set_pos:
461 * @marker: an #AdgMarker
464 * Sets a new position on @marker. Check out adg_marker_get_pos() for
465 * details on what @pos represents.
468 adg_marker_set_pos(AdgMarker
*marker
, gdouble pos
)
470 g_return_if_fail(ADG_IS_MARKER(marker
));
472 if (set_pos(marker
, pos
))
473 g_object_notify((GObject
*) marker
, "pos");
477 * adg_marker_get_pos:
478 * @marker: an #AdgMarker
480 * Gets the current position of @marker. The returned value is a ratio
481 * position referred to the segment associated to @marker: %0 means the
482 * start point and %1 means the end point of the segment.
484 * Returns: the marker position
487 adg_marker_get_pos(AdgMarker
*marker
)
489 AdgMarkerPrivate
*data
;
491 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
499 * adg_marker_set_size:
500 * @marker: an #AdgMarker
501 * @size: the new size
503 * Sets a new size on @marker. The @size is an implementation-dependent
504 * property: it has meaning only when used by an #AdgMarker derived type.
507 adg_marker_set_size(AdgMarker
*marker
, gdouble size
)
509 g_return_if_fail(ADG_IS_MARKER(marker
));
511 if (set_size(marker
, size
))
512 g_object_notify((GObject
*) marker
, "size");
516 * adg_marker_get_size:
517 * @marker: an #AdgMarker
519 * Gets the current size of @marker.
521 * Returns: the marker size, in global space
524 adg_marker_get_size(AdgMarker
*marker
)
526 AdgMarkerPrivate
*data
;
528 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
536 * adg_marker_set_model:
537 * @marker: an #AdgMarker
538 * @model: a new #AdgModel
541 * This function is only useful in marker implementations.
544 * Sets a new model for @marker. The reference to the old model (if an
545 * old model was present) is dropped while a new reference is added to
549 adg_marker_set_model(AdgMarker
*marker
, AdgModel
*model
)
551 g_return_if_fail(ADG_IS_MARKER(marker
));
553 if (set_model(marker
, model
))
554 g_object_notify((GObject
*) marker
, "model");
558 * adg_marker_get_model:
559 * @marker: an #AdgMarker
562 * This function is only useful in marker implementations.
565 * Gets the current model of @marker. This is an accessor method:
566 * if you need to get the model for rendering, use adg_marker_model()
569 * Returns: the cached model owned by @marker or %NULL on errors
572 adg_marker_get_model(AdgMarker
*marker
)
574 AdgMarkerPrivate
*data
;
576 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
585 * @marker: an #AdgMarker
588 * This function is only useful in marker implementations.
591 * Gets the model of @marker. If the model is not found, it is
592 * automatically created by calling the create_model() virtual method.
594 * Returns: the current model owned by @marker or %NULL on errors
597 adg_marker_model(AdgMarker
*marker
)
599 AdgMarkerPrivate
*data
;
601 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
605 if (data
->model
== NULL
) {
606 /* Model not found: regenerate it */
607 AdgMarkerClass
*marker_class
= ADG_MARKER_GET_CLASS(marker
);
609 if (marker_class
->create_model
)
610 adg_marker_set_model(marker
, marker_class
->create_model(marker
));
618 local_changed(AdgEntity
*entity
)
620 AdgMarkerPrivate
*data
;
625 data
= ((AdgMarker
*) entity
)->data
;
626 if (data
->trail
== NULL
)
629 cpml_segment_put_pair_at(&data
->segment
, data
->pos
, &pair
);
630 cpml_segment_put_vector_at(&data
->segment
, data
->pos
, &vector
);
631 cpml_vector_set_length(&vector
, data
->size
);
633 if (data
->pos
> 0.5) {
634 vector
.x
= -vector
.x
;
635 vector
.y
= -vector
.y
;
638 cairo_matrix_init(&map
, vector
.x
, vector
.y
,
639 -vector
.y
, vector
.x
, pair
.x
, pair
.y
);
641 adg_entity_set_local_map(entity
, &map
);
643 if (PARENT_ENTITY_CLASS
->local_changed
)
644 PARENT_ENTITY_CLASS
->local_changed(entity
);
648 invalidate(AdgEntity
*entity
)
650 adg_marker_set_model((AdgMarker
*) entity
, NULL
);
655 set_trail(AdgMarker
*marker
, AdgTrail
*trail
)
657 AdgMarkerPrivate
*data
= marker
->data
;
658 return set_segment(marker
, trail
, data
->n_segment
);
662 unset_trail(AdgMarker
*marker
)
664 set_segment(marker
, NULL
, 0);
668 set_n_segment(AdgMarker
*marker
, guint n_segment
)
670 AdgMarkerPrivate
*data
= marker
->data
;
671 return set_segment(marker
, data
->trail
, n_segment
);
675 set_segment(AdgMarker
*marker
, AdgTrail
*trail
, guint n_segment
)
677 AdgMarkerPrivate
*data
;
679 AdgSegment segment
= { 0 };
681 g_return_val_if_fail(trail
== NULL
|| ADG_IS_TRAIL(trail
), FALSE
);
684 entity
= (AdgEntity
*) marker
;
686 /* Do not try to cache results! Although @trail and @n_segment
687 * could be the same, the internal CpmlSegment could change.
688 * This is the case when AdgLDim arranges the layout and changes
689 * the path data (to force outside arrows for example) reusing
690 * the same CpmlPath. In other words, do not do this:
692 * if (trail == data->trail && n_segment == data->n_segment)
695 * Incidentally, on a 64 bit platform this issue has been never
696 * exposed. Avoiding the cache will solve the issue 33:
698 * http://dev.entidi.com/p/adg/issues/33/
702 /* Validate the new segment, but only if n_segment is specified */
703 if (n_segment
> 0 && !adg_trail_put_segment(trail
, n_segment
, &segment
))
706 g_object_weak_ref((GObject
*) trail
,
707 (GWeakNotify
) unset_trail
, marker
);
708 adg_model_add_dependency((AdgModel
*) trail
, entity
);
711 if (data
->trail
!= NULL
) {
712 /* Disconnect the old trail */
713 if (data
->backup_segment
!= NULL
) {
714 /* Restore the original segment in the old trail */
715 adg_segment_deep_copy(&data
->segment
, data
->backup_segment
);
716 g_free(data
->backup_segment
);
717 data
->backup_segment
= NULL
;
720 g_object_weak_unref((GObject
*) data
->trail
,
721 (GWeakNotify
) unset_trail
, marker
);
722 adg_model_remove_dependency((AdgModel
*) data
->trail
, entity
);
725 cpml_segment_copy(&data
->segment
, &segment
);
727 data
->n_segment
= n_segment
;
733 set_pos(AdgMarker
*marker
, gdouble pos
)
735 AdgMarkerPrivate
*data
;
737 /* A better approach would be to use the GParamSpec of this property */
738 g_return_val_if_fail(pos
>= 0 && pos
<= 1, FALSE
);
742 if (pos
== data
->pos
)
751 set_size(AdgMarker
*marker
, gdouble size
)
753 AdgMarkerPrivate
*data
= marker
->data
;
755 /* A better approach would be to use the GParamSpec of this property */
756 g_return_val_if_fail(size
>= 0, FALSE
);
758 if (size
== data
->size
)
767 set_model(AdgMarker
*marker
, AdgModel
*model
)
769 AdgMarkerPrivate
*data
= marker
->data
;
771 g_return_val_if_fail(model
== NULL
|| ADG_IS_MODEL(model
), FALSE
);
773 if (model
== data
->model
)
776 if (data
->model
!= NULL
)
777 g_object_unref((GObject
*) data
->model
);
781 if (data
->model
!= NULL
) {
782 g_object_ref((GObject
*) data
->model
);
783 adg_entity_local_changed((AdgEntity
*) marker
);
790 create_model(AdgMarker
*marker
)
792 g_warning("%s: `create_model' method not implemented for type `%s'",
793 G_STRLOC
, g_type_name(G_OBJECT_TYPE(marker
)));