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.
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.
44 * All fields are privates and should not be used directly.
45 * Use its public methods instead.
49 #include "adg-marker.h"
50 #include "adg-marker-private.h"
53 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_marker_parent_class)
66 static void dispose (GObject
*object
);
67 static void get_property (GObject
*object
,
71 static void set_property (GObject
*object
,
75 static void get_local_matrix (AdgEntity
*entity
,
77 static gboolean
invalidate (AdgEntity
*entity
);
78 static gboolean
set_trail (AdgMarker
*marker
,
80 static void unset_trail (AdgMarker
*marker
);
81 static gboolean
set_n_segment (AdgMarker
*marker
,
83 static gboolean
set_pos (AdgMarker
*marker
,
85 static gboolean
set_size (AdgMarker
*marker
,
87 static gboolean
set_model (AdgMarker
*marker
,
89 static AdgModel
* create_model (AdgMarker
*marker
);
92 G_DEFINE_ABSTRACT_TYPE(AdgMarker
, adg_marker
, ADG_TYPE_ENTITY
);
96 adg_marker_class_init(AdgMarkerClass
*klass
)
98 GObjectClass
*gobject_class
;
99 AdgEntityClass
*entity_class
;
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",
118 P_("The subject AdgTrail for this marker"),
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",
125 P_("The segment on trail where this marker should be applied (where 0 means undefined segment, 1 the first segment and so on)"),
128 g_object_class_install_property(gobject_class
, PROP_N_SEGMENT
, param
);
130 param
= g_param_spec_double("pos",
132 P_("The position ratio inside the segment where to put the marker (0 means the start point while 1 means the end point)"),
135 g_object_class_install_property(gobject_class
, PROP_POS
, param
);
137 param
= g_param_spec_double("size",
139 P_("The size (in global space) of the marker"),
142 g_object_class_install_property(gobject_class
, PROP_SIZE
, param
);
144 param
= g_param_spec_object("model",
146 P_("A general purpose model usable by the marker implementations"),
149 g_object_class_install_property(gobject_class
, PROP_MODEL
, param
);
153 adg_marker_init(AdgMarker
*marker
)
155 AdgMarkerPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(marker
,
160 data
->backup_segment
= NULL
;
161 memset(&data
->segment
, 0, sizeof(data
->segment
));
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
);
183 get_property(GObject
*object
,
184 guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
186 AdgMarkerPrivate
*data
= ((AdgMarker
*) object
)->data
;
190 g_value_set_object(value
, data
->trail
);
193 g_value_set_uint(value
, data
->n_segment
);
196 g_value_set_double(value
, data
->pos
);
199 g_value_set_double(value
, data
->size
);
202 g_value_set_object(value
, data
->model
);
205 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
211 set_property(GObject
*object
,
212 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
214 AdgMarker
*marker
= (AdgMarker
*) object
;
218 set_trail(marker
, g_value_get_object(value
));
221 set_n_segment(marker
, g_value_get_uint(value
));
224 set_pos(marker
, g_value_get_double(value
));
227 set_size(marker
, g_value_get_double(value
));
230 set_model(marker
, g_value_get_object(value
));
233 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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
248 adg_marker_get_trail(AdgMarker
*marker
)
250 AdgMarkerPrivate
*data
;
252 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
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.
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
293 adg_marker_get_n_segment(AdgMarker
*marker
)
295 AdgMarkerPrivate
*data
;
297 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
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.
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
327 * This function is only useful in marker implementations.
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
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
345 adg_marker_get_backup_segment(AdgMarker
*marker
)
347 AdgMarkerPrivate
*data
;
349 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
353 return data
->backup_segment
;
357 * adg_marker_backup_segment:
358 * @marker: an #AdgMarker
361 * This function is only useful in marker implementations.
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
371 * When the subject segment is changed (either by changing
372 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
373 * is automatically restored.
376 adg_marker_backup_segment(AdgMarker
*marker
)
378 AdgMarkerPrivate
*data
;
380 g_return_if_fail(ADG_IS_MARKER(marker
));
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
401 * This function is only useful in marker implementations.
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
411 adg_marker_get_segment(AdgMarker
*marker
)
413 AdgMarkerPrivate
*data
;
415 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
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
433 adg_marker_get_pos(AdgMarker
*marker
)
435 AdgMarkerPrivate
*data
;
437 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
445 * adg_marker_set_pos:
446 * @marker: an #AdgMarker
449 * Sets a new position on @marker. Check out adg_marker_get_pos() for
450 * details on what @pos represents.
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
470 adg_marker_get_size(AdgMarker
*marker
)
472 AdgMarkerPrivate
*data
;
474 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
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.
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");
500 * @marker: an #AdgMarker
503 * This function is only useful in marker implementations.
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
512 adg_marker_model(AdgMarker
*marker
)
514 AdgMarkerPrivate
*data
;
516 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
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
));
531 * adg_marker_get_model:
532 * @marker: an #AdgMarker
535 * This function is only useful in marker implementations.
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()
542 * Returns: the cached model owned by @marker or %NULL on errors
545 adg_marker_get_model(AdgMarker
*marker
)
547 AdgMarkerPrivate
*data
;
549 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
557 * adg_marker_set_model:
558 * @marker: an #AdgMarker
559 * @model: a new #AdgModel
562 * This function is only useful in marker implementations.
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
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");
580 get_local_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
582 AdgMarkerPrivate
*data
;
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
);
604 invalidate(AdgEntity
*entity
)
606 AdgMarker
*marker
= (AdgMarker
*) entity
;
608 adg_marker_set_model(marker
, NULL
);
615 set_trail(AdgMarker
*marker
, AdgTrail
*trail
)
617 AdgMarkerPrivate
*data
;
622 if (trail
== data
->trail
)
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
);
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);
652 unset_trail(AdgMarker
*marker
)
654 AdgMarkerPrivate
*data
= marker
->data
;
656 if (data
->trail
!= NULL
) {
658 set_n_segment(marker
, 0);
663 set_n_segment(AdgMarker
*marker
, guint n_segment
)
665 AdgMarkerPrivate
*data
= marker
->data
;
667 if (n_segment
== data
->n_segment
)
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
);
685 set_pos(AdgMarker
*marker
, gdouble pos
)
687 AdgMarkerPrivate
*data
= marker
->data
;
689 if (pos
== data
->pos
)
698 set_size(AdgMarker
*marker
, gdouble size
)
700 AdgMarkerPrivate
*data
= marker
->data
;
702 if (size
== data
->size
)
711 set_model(AdgMarker
*marker
, AdgModel
*model
)
713 AdgMarkerPrivate
*data
= marker
->data
;
715 if (model
== data
->model
)
718 if (data
->model
!= NULL
)
719 g_object_unref((GObject
*) data
->model
);
723 if (data
->model
!= NULL
)
724 g_object_ref((GObject
*) data
->model
);
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
)));