[AdgMarker] Completed by implementing missing stuff
[adg.git] / adg / adg-marker.c
blob68eedfd37653d83209eaaea3539731bf57ece90c
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.
27 **/
29 /**
30 * AdgMarker:
32 * All fields are privates and should not be used directly.
33 * Use its public methods instead.
34 **/
37 #include "adg-marker.h"
38 #include "adg-marker-private.h"
39 #include "adg-intl.h"
41 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_marker_parent_class)
44 enum {
45 PROP_0,
46 PROP_PATH,
47 PROP_N_SEGMENT,
48 PROP_POS,
49 PROP_SIZE,
50 PROP_MODEL
54 static void dispose (GObject *object);
55 static void get_property (GObject *object,
56 guint prop_id,
57 GValue *value,
58 GParamSpec *pspec);
59 static void set_property (GObject *object,
60 guint prop_id,
61 const GValue *value,
62 GParamSpec *pspec);
63 static gboolean invalidate (AdgEntity *entity);
64 static gboolean set_path (AdgMarker *marker,
65 AdgPath *path);
66 static void unset_path (AdgMarker *marker);
67 static gboolean set_n_segment (AdgMarker *marker,
68 gint n_segment);
69 static gboolean set_pos (AdgMarker *marker,
70 gdouble pos);
71 static gboolean set_size (AdgMarker *marker,
72 gdouble size);
73 static gboolean set_model (AdgMarker *marker,
74 AdgModel *model);
75 static AdgModel * create_model (AdgMarker *marker);
78 G_DEFINE_ABSTRACT_TYPE(AdgMarker, adg_marker, ADG_TYPE_ENTITY);
81 static void
82 adg_marker_class_init(AdgMarkerClass *klass)
84 GObjectClass *gobject_class;
85 AdgEntityClass *entity_class;
86 GParamSpec *param;
88 gobject_class = (GObjectClass *) klass;
89 entity_class = (AdgEntityClass *) klass;
91 g_type_class_add_private(klass, sizeof(AdgMarkerPrivate));
93 gobject_class->dispose = dispose;
94 gobject_class->set_property = set_property;
95 gobject_class->get_property = get_property;
97 entity_class->invalidate = invalidate;
99 klass->create_model = create_model;
101 param = g_param_spec_object("path",
102 P_("Path"),
103 P_("The subject path for this marker"),
104 ADG_TYPE_PATH,
105 G_PARAM_CONSTRUCT|G_PARAM_READWRITE);
106 g_object_class_install_property(gobject_class, PROP_PATH, param);
108 param = g_param_spec_uint("n-segment",
109 P_("Segment Index"),
110 P_("The segment of path where this marker should be applied (where 0 means undefined segment, 1 the first segment and so on)"),
111 0, G_MAXUINT, 0,
112 G_PARAM_CONSTRUCT|G_PARAM_READWRITE);
113 g_object_class_install_property(gobject_class, PROP_N_SEGMENT, param);
115 param = g_param_spec_double("pos",
116 P_("Position"),
117 P_("The position ratio inside the segment where to put the marker (0 means the start point while 1 means the end point)"),
118 0, 1, 0,
119 G_PARAM_CONSTRUCT|G_PARAM_READWRITE);
120 g_object_class_install_property(gobject_class, PROP_POS, param);
122 param = g_param_spec_double("size",
123 P_("Marker Size"),
124 P_("The size (in global space) of the marker"),
125 0, G_MAXDOUBLE, 12,
126 G_PARAM_READWRITE);
127 g_object_class_install_property(gobject_class, PROP_SIZE, param);
129 param = g_param_spec_object("model",
130 P_("Model"),
131 P_("A general purpose model usable by the marker implementations"),
132 ADG_TYPE_MODEL,
133 G_PARAM_READWRITE);
134 g_object_class_install_property(gobject_class, PROP_MODEL, param);
137 static void
138 adg_marker_init(AdgMarker *marker)
140 AdgMarkerPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(marker,
141 ADG_TYPE_MARKER,
142 AdgMarkerPrivate);
143 data->path = NULL;
144 data->n_segment = 0;
145 data->backup_segment = NULL;
146 memset(&data->segment, 0, sizeof(data->segment));
147 data->pos = 0;
148 data->size = 10;
149 data->model = NULL;
151 marker->data = data;
154 static void
155 dispose(GObject *object)
157 AdgMarker *marker = (AdgMarker *) object;
159 adg_marker_set_model(marker, NULL);
160 adg_marker_set_path(marker, NULL);
162 if (PARENT_OBJECT_CLASS->dispose != NULL)
163 PARENT_OBJECT_CLASS->dispose(object);
167 static void
168 get_property(GObject *object,
169 guint prop_id, GValue *value, GParamSpec *pspec)
171 AdgMarkerPrivate *data = ((AdgMarker *) object)->data;
173 switch (prop_id) {
174 case PROP_PATH:
175 g_value_set_object(value, data->path);
176 break;
177 case PROP_N_SEGMENT:
178 g_value_set_uint(value, data->n_segment);
179 break;
180 case PROP_POS:
181 g_value_set_double(value, data->pos);
182 break;
183 case PROP_SIZE:
184 g_value_set_double(value, data->size);
185 break;
186 case PROP_MODEL:
187 g_value_set_object(value, data->model);
188 break;
189 default:
190 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
191 break;
195 static void
196 set_property(GObject *object,
197 guint prop_id, const GValue *value, GParamSpec *pspec)
199 AdgMarker *marker = (AdgMarker *) object;
201 switch (prop_id) {
202 case PROP_PATH:
203 set_path(marker, g_value_get_object(value));
204 break;
205 case PROP_N_SEGMENT:
206 set_n_segment(marker, g_value_get_uint(value));
207 break;
208 case PROP_POS:
209 set_pos(marker, g_value_get_double(value));
210 break;
211 case PROP_SIZE:
212 set_size(marker, g_value_get_double(value));
213 break;
214 case PROP_MODEL:
215 set_model(marker, g_value_get_object(value));
216 break;
217 default:
218 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
219 break;
225 * adg_marker_get_path:
226 * @marker: an #AdgMarker
228 * Gets the path where this marker should be applied.
230 * Returns: the path owned by @marker or %NULL on errors
232 AdgPath *
233 adg_marker_get_path(AdgMarker *marker)
235 AdgMarkerPrivate *data;
237 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
239 data = marker->data;
241 return data->path;
245 * adg_marker_set_path:
246 * @marker: an #AdgMarker
247 * @path: a new #AdgPath
249 * Sets a new path where the marker should be applied. The weak reference
250 * to the old path (if an old path was present) is dropped while a new
251 * weak reference is added to @path. If @path is destroyed, the weak
252 * reference callback will automatically unset #AdgMarker:path and will
253 * set #AdgMarker:n-segment to %0.
255 * After setting a new path, the #AdgMarker:n-segment property is
256 * reset to %1. This means the first segment of the path is always
257 * selected by default.
259 void
260 adg_marker_set_path(AdgMarker *marker, AdgPath *path)
262 g_return_if_fail(ADG_IS_MARKER(marker));
264 if (set_path(marker, path))
265 g_object_notify((GObject *) marker, "path");
269 * adg_marker_get_n_segment:
270 * @marker: an #AdgMarker
272 * Returns the segment of the associated path where this marker
273 * should be applied, where %1 is the first segment.
275 * Returns: an index greather than %0 on success or %0 on errors
277 gint
278 adg_marker_get_n_segment(AdgMarker *marker)
280 AdgMarkerPrivate *data;
282 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
284 data = marker->data;
286 return data->n_segment;
290 * adg_marker_set_n_segment:
291 * @marker: an #AdgMarker
292 * @n_segment: a new segment index
294 * Sets a new segment to use. @n_segment is expected to be greather than
295 * %0 and to not exceed the number of segments in the underlying path.
296 * By convention, %1 is the first segment.
298 void
299 adg_marker_set_n_segment(AdgMarker *marker, gint n_segment)
301 g_return_if_fail(ADG_IS_MARKER(marker));
303 if (set_n_segment(marker, n_segment))
304 g_object_notify((GObject *) marker, "n-segment");
308 * adg_marker_get_backup_segment:
309 * @marker: an #AdgMarker
311 * <note><para>
312 * This function is only useful in marker implementations.
313 * </para></note>
315 * Gets the original segment where the marker has been applied.
316 * Applying a marker could modify the underlying path, usually
317 * by trimming the original segment of a #AdgMarker:size dependent
318 * length from the end. The marker instance holds a copy of the
319 * original segment, got using adg_segment_deep_dup(), to be used
320 * in recomputation (when the marker changes size, for instance).
322 * When the subject segment is changed (either by changing
323 * #AdgMarker:path or #AdgMarker:n-segment) the original segment
324 * is restored.
326 * Returns: the original segment or %NULL on errors
328 const AdgSegment *
329 adg_marker_get_backup_segment(AdgMarker *marker)
331 AdgMarkerPrivate *data;
333 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
335 data = marker->data;
337 return data->backup_segment;
341 * adg_marker_get_segment:
342 * @marker: an #AdgMarker
344 * <note><para>
345 * This function is only useful in marker implementations.
346 * </para></note>
348 * Gets the segment where the marker will be applied. This segment
349 * is eventually a modified version of the backup segment, after
350 * having applied the marker.
352 * Returns: the segment or %NULL on errors
354 AdgSegment *
355 adg_marker_get_segment(AdgMarker *marker)
357 AdgMarkerPrivate *data;
359 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
361 data = marker->data;
363 return &data->segment;
367 * adg_marker_get_pos:
368 * @marker: an #AdgMarker
370 * Gets the current position of @marker. The returned value is a ratio
371 * position referred to the segment associated to @marker: %0 means the
372 * start point and %1 means the end point of the segment.
374 * Returns: the marker position
376 gdouble
377 adg_marker_get_pos(AdgMarker *marker)
379 AdgMarkerPrivate *data;
381 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
383 data = marker->data;
385 return data->pos;
389 * adg_marker_set_pos:
390 * @marker: an #AdgMarker
391 * @pos: the new pos
393 * Sets a new position on @marker. Check out adg_marker_get_pos() for
394 * details on what @pos represents.
396 void
397 adg_marker_set_pos(AdgMarker *marker, gdouble pos)
399 g_return_if_fail(ADG_IS_MARKER(marker));
401 if (set_pos(marker, pos))
402 g_object_notify((GObject *) marker, "pos");
406 * adg_marker_get_size:
407 * @marker: an #AdgMarker
409 * Gets the current size of @marker.
411 * Returns: the marker size, in global space
413 gdouble
414 adg_marker_get_size(AdgMarker *marker)
416 AdgMarkerPrivate *data;
418 g_return_val_if_fail(ADG_IS_MARKER(marker), 0);
420 data = marker->data;
422 return data->size;
426 * adg_marker_set_size:
427 * @marker: an #AdgMarker
428 * @size: the new size
430 * Sets a new size on @marker. The @size is an implementation-dependent
431 * property: it has meaning only when used by an #AdgMarker derived type.
433 void
434 adg_marker_set_size(AdgMarker *marker, gdouble size)
436 g_return_if_fail(ADG_IS_MARKER(marker));
438 if (set_size(marker, size))
439 g_object_notify((GObject *) marker, "size");
443 * adg_marker_model:
444 * @marker: an #AdgMarker
446 * <note><para>
447 * This function is only useful in marker implementations.
448 * </para></note>
450 * Gets the model of @marker. If the model is not found, it is
451 * automatically created by calling the create_model() virtual method.
453 * Returns: the current model owned by @marker or %NULL on errors
455 AdgModel *
456 adg_marker_model(AdgMarker *marker)
458 AdgMarkerPrivate *data;
460 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
462 data = marker->data;
464 if (data->model == NULL) {
465 /* Model not found: regenerate it */
466 AdgMarkerClass *marker_class = ADG_MARKER_GET_CLASS(marker);
468 adg_marker_set_model(marker, marker_class->create_model(marker));
471 return data->model;
475 * adg_marker_get_model:
476 * @marker: an #AdgMarker
478 * <note><para>
479 * This function is only useful in marker implementations.
480 * </para></note>
482 * Gets the current model of @marker. This is an accessor method:
483 * if you need to get the model for rendering, use adg_marker_model()
484 * instead.
486 * Returns: the cached model owned by @marker or %NULL on errors
488 AdgModel *
489 adg_marker_get_model(AdgMarker *marker)
491 AdgMarkerPrivate *data;
493 g_return_val_if_fail(ADG_IS_MARKER(marker), NULL);
495 data = marker->data;
497 return data->model;
501 * adg_marker_set_model:
502 * @marker: an #AdgMarker
503 * @model: a new #AdgModel
505 * <note><para>
506 * This function is only useful in marker implementations.
507 * </para></note>
509 * Sets a new model for @marker. The reference to the old model (if an
510 * old model was present) is dropped while a new reference is added to
511 * @model.
513 void
514 adg_marker_set_model(AdgMarker *marker, AdgModel *model)
516 g_return_if_fail(ADG_IS_MARKER(marker));
518 if (set_model(marker, model))
519 g_object_notify((GObject *) marker, "model");
523 static gboolean
524 invalidate(AdgEntity *entity)
526 AdgMarker *marker = (AdgMarker *) entity;
528 adg_marker_set_model(marker, NULL);
530 return TRUE;
534 static gboolean
535 set_path(AdgMarker *marker, AdgPath *path)
537 AdgMarkerPrivate *data;
538 AdgEntity *entity;
540 data = marker->data;
542 if (path == data->path)
543 return FALSE;
545 entity = (AdgEntity *) marker;
547 if (data->path != NULL) {
548 /* Restore the original segment in the old path */
549 set_n_segment(marker, 0);
551 g_object_weak_unref((GObject *) data->path,
552 (GWeakNotify) unset_path, marker);
553 adg_model_remove_dependency((AdgModel *) data->path, entity);
556 data->path = path;
558 if (data->path != NULL) {
559 g_object_weak_ref((GObject *) data->path,
560 (GWeakNotify) unset_path, marker);
561 adg_model_add_dependency((AdgModel *) data->path, entity);
563 /* Set the first segment by default */
564 set_n_segment(marker, 1);
567 return TRUE;
570 static void
571 unset_path(AdgMarker *marker)
573 AdgMarkerPrivate *data = marker->data;
575 if (data->path != NULL) {
576 data->path = NULL;
577 set_n_segment(marker, 0);
581 static gboolean
582 set_n_segment(AdgMarker *marker, gint n_segment)
584 AdgMarkerPrivate *data = marker->data;
586 if (n_segment == data->n_segment)
587 return FALSE;
589 if (data->n_segment > 0) {
590 g_assert(data->backup_segment != NULL);
592 /* Restore the original segment */
593 adg_segment_deep_copy(&data->segment, data->backup_segment);
596 if (data->backup_segment != NULL) {
597 g_free(data->backup_segment);
598 data->backup_segment = NULL;
601 if (n_segment > 0) {
602 data->n_segment = 0;
603 g_return_val_if_fail(data->path != NULL, FALSE);
605 if (!adg_path_get_segment(data->path,
606 &data->segment, data->n_segment))
607 return FALSE;
609 /* Backup the segment */
610 data->backup_segment = adg_segment_deep_dup(&data->segment);
613 data->n_segment = n_segment;
615 return TRUE;
618 static gboolean
619 set_pos(AdgMarker *marker, gdouble pos)
621 AdgMarkerPrivate *data = marker->data;
623 if (pos == data->pos)
624 return FALSE;
626 data->pos = pos;
628 return TRUE;
631 static gboolean
632 set_size(AdgMarker *marker, gdouble size)
634 AdgMarkerPrivate *data = marker->data;
636 if (size == data->size)
637 return FALSE;
639 data->size = size;
641 return TRUE;
644 static gboolean
645 set_model(AdgMarker *marker, AdgModel *model)
647 AdgMarkerPrivate *data = marker->data;
649 if (model == data->model)
650 return FALSE;
652 if (data->model != NULL)
653 g_object_unref((GObject *) data->model);
655 data->model = model;
657 if (data->model != NULL)
658 g_object_ref((GObject *) data->model);
660 return TRUE;
663 static AdgModel *
664 create_model(AdgMarker *marker)
666 g_warning("%s: `create_model' method not implemented for type `%s'",
667 G_STRLOC, g_type_name(G_OBJECT_TYPE(marker)));
668 return NULL;