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_segment (AdgMarker
*marker
,
81 static void unset_trail (AdgMarker
*marker
);
82 static gboolean
set_n_segment (AdgMarker
*marker
,
84 static gboolean
set_pos (AdgMarker
*marker
,
86 static gboolean
set_size (AdgMarker
*marker
,
88 static gboolean
set_model (AdgMarker
*marker
,
90 static AdgModel
* create_model (AdgMarker
*marker
);
93 G_DEFINE_ABSTRACT_TYPE(AdgMarker
, adg_marker
, ADG_TYPE_ENTITY
);
97 adg_marker_class_init(AdgMarkerClass
*klass
)
99 GObjectClass
*gobject_class
;
100 AdgEntityClass
*entity_class
;
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",
119 P_("The subject AdgTrail for this marker"),
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",
126 P_("The segment on trail where this marker should be applied (where 0 means undefined segment, 1 the first segment and so on)"),
129 g_object_class_install_property(gobject_class
, PROP_N_SEGMENT
, param
);
131 param
= g_param_spec_double("pos",
133 P_("The position ratio inside the segment where to put the marker (0 means the start point while 1 means the end point)"),
136 g_object_class_install_property(gobject_class
, PROP_POS
, param
);
138 param
= g_param_spec_double("size",
140 P_("The size (in global space) of the marker"),
143 g_object_class_install_property(gobject_class
, PROP_SIZE
, param
);
145 param
= g_param_spec_object("model",
147 P_("A general purpose model usable by the marker implementations"),
150 g_object_class_install_property(gobject_class
, PROP_MODEL
, param
);
154 adg_marker_init(AdgMarker
*marker
)
156 AdgMarkerPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(marker
,
161 data
->backup_segment
= NULL
;
162 memset(&data
->segment
, 0, sizeof(data
->segment
));
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
);
184 get_property(GObject
*object
,
185 guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
187 AdgMarkerPrivate
*data
= ((AdgMarker
*) object
)->data
;
191 g_value_set_object(value
, data
->trail
);
194 g_value_set_uint(value
, data
->n_segment
);
197 g_value_set_double(value
, data
->pos
);
200 g_value_set_double(value
, data
->size
);
203 g_value_set_object(value
, data
->model
);
206 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
212 set_property(GObject
*object
,
213 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
215 AdgMarker
*marker
= (AdgMarker
*) object
;
219 set_segment(marker
, g_value_get_object(value
), 1);
222 set_n_segment(marker
, g_value_get_uint(value
));
225 set_pos(marker
, g_value_get_double(value
));
228 set_size(marker
, g_value_get_double(value
));
231 set_model(marker
, g_value_get_object(value
));
234 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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
249 adg_marker_get_trail(AdgMarker
*marker
)
251 AdgMarkerPrivate
*data
;
253 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
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
270 adg_marker_get_n_segment(AdgMarker
*marker
)
272 AdgMarkerPrivate
*data
;
274 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
278 return data
->n_segment
;
282 * adg_marker_get_segment:
283 * @marker: an #AdgMarker
286 * This function is only useful in marker implementations.
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
296 adg_marker_get_segment(AdgMarker
*marker
)
298 AdgMarkerPrivate
*data
;
300 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
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.
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
333 * This function is only useful in marker implementations.
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
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
351 adg_marker_get_backup_segment(AdgMarker
*marker
)
353 AdgMarkerPrivate
*data
;
355 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
359 return data
->backup_segment
;
363 * adg_marker_backup_segment:
364 * @marker: an #AdgMarker
367 * This function is only useful in marker implementations.
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
377 * When the subject segment is changed (either by changing
378 * #AdgMarker:trail or #AdgMarker:n-segment) the original segment
379 * is automatically restored.
382 adg_marker_backup_segment(AdgMarker
*marker
)
384 AdgMarkerPrivate
*data
;
386 g_return_if_fail(ADG_IS_MARKER(marker
));
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
413 adg_marker_get_pos(AdgMarker
*marker
)
415 AdgMarkerPrivate
*data
;
417 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
425 * adg_marker_set_pos:
426 * @marker: an #AdgMarker
429 * Sets a new position on @marker. Check out adg_marker_get_pos() for
430 * details on what @pos represents.
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
450 adg_marker_get_size(AdgMarker
*marker
)
452 AdgMarkerPrivate
*data
;
454 g_return_val_if_fail(ADG_IS_MARKER(marker
), 0);
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.
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");
480 * @marker: an #AdgMarker
483 * This function is only useful in marker implementations.
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
492 adg_marker_model(AdgMarker
*marker
)
494 AdgMarkerPrivate
*data
;
496 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
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
));
511 * adg_marker_get_model:
512 * @marker: an #AdgMarker
515 * This function is only useful in marker implementations.
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()
522 * Returns: the cached model owned by @marker or %NULL on errors
525 adg_marker_get_model(AdgMarker
*marker
)
527 AdgMarkerPrivate
*data
;
529 g_return_val_if_fail(ADG_IS_MARKER(marker
), NULL
);
537 * adg_marker_set_model:
538 * @marker: an #AdgMarker
539 * @model: a new #AdgModel
542 * This function is only useful in marker implementations.
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
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");
560 get_local_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
562 AdgMarkerPrivate
*data
;
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
);
584 invalidate(AdgEntity
*entity
)
586 AdgMarker
*marker
= (AdgMarker
*) entity
;
588 adg_marker_set_model(marker
, NULL
);
595 set_segment(AdgMarker
*marker
, AdgTrail
*trail
, guint n_segment
)
597 AdgMarkerPrivate
*data
;
601 entity
= (AdgEntity
*) marker
;
603 if (trail
== data
->trail
&& n_segment
== data
->n_segment
)
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
);
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
);
630 unset_trail(AdgMarker
*marker
)
632 AdgMarkerPrivate
*data
= marker
->data
;
634 if (data
->trail
!= NULL
) {
636 set_n_segment(marker
, 0);
641 set_n_segment(AdgMarker
*marker
, guint n_segment
)
643 AdgMarkerPrivate
*data
= marker
->data
;
645 if (n_segment
== data
->n_segment
)
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
);
663 set_pos(AdgMarker
*marker
, gdouble pos
)
665 AdgMarkerPrivate
*data
= marker
->data
;
667 if (pos
== data
->pos
)
676 set_size(AdgMarker
*marker
, gdouble size
)
678 AdgMarkerPrivate
*data
= marker
->data
;
680 if (size
== data
->size
)
689 set_model(AdgMarker
*marker
, AdgModel
*model
)
691 AdgMarkerPrivate
*data
= marker
->data
;
693 if (model
== data
->model
)
696 if (data
->model
!= NULL
)
697 g_object_unref((GObject
*) data
->model
);
701 if (data
->model
!= NULL
)
702 g_object_ref((GObject
*) data
->model
);
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
)));