vdx version .6.1
[dia.git] / objects / chronogram / chronoline.c
blob96fb948d4d0025eab346c31e1ecc18bff6fa3d14
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * Chronogram objects support
5 * Copyright (C) 2000, 2001 Cyrille Chepelov
6 *
7 * Ultimately forked from Flowchart toolbox -- objects for drawing flowcharts.
8 * Copyright (C) 1999 James Henstridge.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
29 #include <assert.h>
30 #include <math.h>
31 #include <string.h>
32 #include <glib.h>
34 #include "intl.h"
35 #include "object.h"
36 #include "element.h"
37 #include "connectionpoint.h"
38 #include "diarenderer.h"
39 #include "attributes.h"
40 #include "text.h"
41 #include "widgets.h"
42 #include "message.h"
43 #include "connpoint_line.h"
44 #include "color.h"
45 #include "properties.h"
47 #include "chronogram.h"
48 #include "chronoline_event.h"
50 #include "pixmaps/chronoline.xpm"
52 #define DEFAULT_WIDTH 7.0
53 #define DEFAULT_HEIGHT 5.0
55 typedef struct _Chronoline {
56 Element element;
58 real main_lwidth;
59 Color color;
60 real start_time;
61 real end_time;
62 real data_lwidth;
63 Color data_color;
64 char *events;
65 char *name;
66 real rise_time;
67 real fall_time;
68 gboolean multibit;
69 DiaFont *font;
70 real font_size;
71 Color font_color;
73 /* computed values : */
74 ConnPointLine *snap; /* not saved ; num_connections derived from
75 the event string. */
76 CLEventList *evtlist;
78 int checksum;
79 real labelwidth;
80 real y_down,y_up;
81 Color gray, datagray;
82 } Chronoline;
85 static real chronoline_distance_from(Chronoline *chronoline, Point *point);
86 static void chronoline_select(Chronoline *chronoline, Point *clicked_point,
87 DiaRenderer *interactive_renderer);
88 static ObjectChange* chronoline_move_handle(Chronoline *chronoline, Handle *handle,
89 Point *to, ConnectionPoint *cp,
90 HandleMoveReason reason,
91 ModifierKeys modifiers);
92 static ObjectChange* chronoline_move(Chronoline *chronoline, Point *to);
93 static void chronoline_draw(Chronoline *chronoline, DiaRenderer *renderer);
94 static void chronoline_update_data(Chronoline *chronoline);
95 static DiaObject *chronoline_create(Point *startpoint,
96 void *user_data,
97 Handle **handle1,
98 Handle **handle2);
99 static void chronoline_destroy(Chronoline *chronoline);
100 static DiaObject *chronoline_load(ObjectNode obj_node, int version,
101 const char *filename);
102 static PropDescription *chronoline_describe_props(Chronoline *chronoline);
103 static void chronoline_get_props(Chronoline *chronoline,
104 GPtrArray *props);
105 static void chronoline_set_props(Chronoline *chronoline,
106 GPtrArray *props);
108 static ObjectTypeOps chronoline_type_ops =
110 (CreateFunc) chronoline_create,
111 (LoadFunc) chronoline_load/*using properties*/,
112 (SaveFunc) object_save_using_properties,
113 (GetDefaultsFunc) NULL,
114 (ApplyDefaultsFunc) NULL
117 DiaObjectType chronoline_type =
119 "chronogram - line", /* name */
120 0, /* version */
121 (char **) chronoline_xpm, /* pixmap */
123 &chronoline_type_ops /* ops */
126 static ObjectOps chronoline_ops = {
127 (DestroyFunc) chronoline_destroy,
128 (DrawFunc) chronoline_draw,
129 (DistanceFunc) chronoline_distance_from,
130 (SelectFunc) chronoline_select,
131 (CopyFunc) object_copy_using_properties,
132 (MoveFunc) chronoline_move,
133 (MoveHandleFunc) chronoline_move_handle,
134 (GetPropertiesFunc) object_create_props_dialog,
135 (ApplyPropertiesFunc) object_apply_props_from_dialog,
136 (ObjectMenuFunc) NULL,
137 (DescribePropsFunc) chronoline_describe_props,
138 (GetPropsFunc) chronoline_get_props,
139 (SetPropsFunc) chronoline_set_props
142 static PropNumData time_range = { -32767.0, 32768.0, 0.1};
143 static PropNumData edge_range = { 0.0, 1000.0, 0.1};
145 static PropDescription chronoline_props[] = {
146 ELEMENT_COMMON_PROPERTIES,
147 PROP_STD_NOTEBOOK_BEGIN,
148 PROP_NOTEBOOK_PAGE("data",PROP_FLAG_DONT_MERGE,N_("Data")),
149 { "name", PROP_TYPE_STRING,PROP_FLAG_VISIBLE|PROP_FLAG_DONT_MERGE,
150 N_("Data name"),NULL },
151 { "events", PROP_TYPE_MULTISTRING,PROP_FLAG_VISIBLE|PROP_FLAG_DONT_MERGE,
152 N_("Events"), NULL,GINT_TO_POINTER(5) },
153 { "help", PROP_TYPE_STATIC,
154 PROP_FLAG_VISIBLE|PROP_FLAG_DONT_SAVE|PROP_FLAG_DONT_MERGE,
155 N_("Event specification"),N_(
156 "@ time set the pointer at an absolute time.\n"
157 "( duration sets the signal up, then wait 'duration'.\n"
158 ") duration sets the signal down, then wait 'duration'.\n"
159 "u duration sets the signal to \"unknown\" state, then wait 'duration'.\n"
160 "example : @ 1.0 (2.0)1.0(2.0)\n" )},
162 PROP_NOTEBOOK_PAGE("parameters",0,N_("Parameters")),
163 { "start_time",PROP_TYPE_REAL,PROP_FLAG_VISIBLE,
164 N_("Start time"),NULL,&time_range},
165 { "end_time",PROP_TYPE_REAL,PROP_FLAG_VISIBLE,
166 N_("End time"),NULL,&time_range},
167 { "rise_time",PROP_TYPE_REAL,PROP_FLAG_VISIBLE,
168 N_("Rise time"),NULL,&edge_range},
169 { "fall_time",PROP_TYPE_REAL,PROP_FLAG_VISIBLE,
170 N_("Fall time"),NULL,&edge_range},
171 { "multibit",PROP_TYPE_BOOL,PROP_FLAG_VISIBLE, N_("Multi-bit data"),NULL},
173 PROP_NOTEBOOK_PAGE("aspect",0,N_("Aspect")),
174 { "data_color", PROP_TYPE_COLOUR, PROP_FLAG_VISIBLE,
175 N_("Data color"),NULL},
176 { "data_lwidth", PROP_TYPE_REAL, PROP_FLAG_VISIBLE,
177 N_("Data line width"),NULL, &prop_std_line_width_data},
178 { "color", PROP_TYPE_COLOUR, PROP_FLAG_VISIBLE,
179 N_("Line color"),NULL},
180 { "main_lwidth", PROP_TYPE_REAL, PROP_FLAG_VISIBLE,
181 N_("Line width"),NULL, &prop_std_line_width_data},
183 { "font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE, N_("Font"), NULL, NULL },
184 { "font_size", PROP_TYPE_REAL, PROP_FLAG_VISIBLE,
185 N_("Font size"), NULL, &prop_std_text_height_data },
186 { "font_color", PROP_TYPE_COLOUR, PROP_FLAG_VISIBLE,
187 N_("Text color"), NULL, NULL },
189 PROP_STD_NOTEBOOK_END,
190 {NULL}
193 static PropDescription *
194 chronoline_describe_props(Chronoline *chronoline)
196 if (chronoline_props[0].quark == 0) {
197 prop_desc_list_calculate_quarks(chronoline_props);
199 return chronoline_props;
202 static PropOffset chronoline_offsets[] = {
203 ELEMENT_COMMON_PROPERTIES_OFFSETS,
204 PROP_OFFSET_STD_NOTEBOOK_BEGIN,
206 PROP_OFFSET_NOTEBOOK_PAGE("data"),
207 { "name", PROP_TYPE_STRING, offsetof(Chronoline,name)},
208 { "events", PROP_TYPE_MULTISTRING, offsetof(Chronoline,events)},
209 { "help", PROP_TYPE_STATIC, 0},
211 PROP_OFFSET_NOTEBOOK_PAGE("parameters"),
212 { "start_time",PROP_TYPE_REAL, offsetof(Chronoline,start_time)},
213 { "end_time",PROP_TYPE_REAL, offsetof(Chronoline,end_time)},
214 { "rise_time",PROP_TYPE_REAL, offsetof(Chronoline,rise_time)},
215 { "fall_time",PROP_TYPE_REAL, offsetof(Chronoline,fall_time)},
216 { "multibit",PROP_TYPE_BOOL, offsetof(Chronoline,multibit)},
218 PROP_OFFSET_NOTEBOOK_PAGE("aspect"),
219 { "data_color", PROP_TYPE_COLOUR, offsetof(Chronoline,data_color)},
220 { "data_lwidth", PROP_TYPE_REAL, offsetof(Chronoline,data_lwidth)},
221 { "color", PROP_TYPE_COLOUR, offsetof(Chronoline,color)},
222 { "main_lwidth", PROP_TYPE_REAL, offsetof(Chronoline,main_lwidth)},
223 { "font", PROP_TYPE_FONT, offsetof(Chronoline,font)},
224 { "font_size", PROP_TYPE_REAL, offsetof(Chronoline,font_size)},
225 { "font_color", PROP_TYPE_COLOUR, offsetof(Chronoline,font_color)},
227 PROP_OFFSET_STD_NOTEBOOK_END,
228 {NULL}
231 static void
232 chronoline_get_props(Chronoline *chronoline, GPtrArray *props)
234 object_get_props_from_offsets(&chronoline->element.object,
235 chronoline_offsets,props);
238 static void
239 chronoline_set_props(Chronoline *chronoline, GPtrArray *props)
241 object_set_props_from_offsets(&chronoline->element.object,
242 chronoline_offsets,props);
243 chronoline_update_data(chronoline);
246 static real
247 chronoline_distance_from(Chronoline *chronoline, Point *point)
249 DiaObject *obj = &chronoline->element.object;
250 return distance_rectangle_point(&obj->bounding_box, point);
253 static void
254 chronoline_select(Chronoline *chronoline, Point *clicked_point,
255 DiaRenderer *interactive_renderer)
257 element_update_handles(&chronoline->element);
260 static ObjectChange*
261 chronoline_move_handle(Chronoline *chronoline, Handle *handle,
262 Point *to, ConnectionPoint *cp,
263 HandleMoveReason reason, ModifierKeys modifiers)
265 g_assert(chronoline!=NULL);
266 g_assert(handle!=NULL);
267 g_assert(to!=NULL);
269 element_move_handle(&chronoline->element, handle->id, to, cp,
270 reason, modifiers);
271 chronoline_update_data(chronoline);
273 return NULL;
276 static ObjectChange*
277 chronoline_move(Chronoline *chronoline, Point *to)
279 chronoline->element.corner = *to;
280 chronoline_update_data(chronoline);
282 return NULL;
285 static void
286 cld_onebit(Chronoline *chronoline,
287 DiaRenderer *renderer,
288 real x1,CLEventType s1,
289 real x2,CLEventType s2,
290 gboolean fill)
292 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
293 Point pts[4];
294 real y_down = chronoline->y_down;
295 real y_up = chronoline->y_up;
297 pts[0].x = pts[1].x = x1;
298 pts[2].x = pts[3].x = x2;
300 pts[0].y = pts[3].y = chronoline->y_down;
301 pts[1].y = (s1 == CLE_OFF)?y_down:y_up;
302 pts[2].y = (s2 == CLE_OFF)?y_down:y_up;
304 if (fill) {
305 if ((s1 == CLE_UNKNOWN) || (s2 == CLE_UNKNOWN)) {
306 renderer_ops->fill_polygon(renderer,pts,sizeof(pts)/sizeof(pts[0]),
307 &chronoline->datagray);
308 } else {
309 renderer_ops->fill_polygon(renderer,pts,sizeof(pts)/sizeof(pts[0]),
310 &color_white);
312 } else {
313 renderer_ops->draw_line(renderer,&pts[1],&pts[2],
314 &chronoline->data_color);
319 static void
320 cld_multibit(Chronoline *chronoline,
321 DiaRenderer *renderer,
322 real x1,CLEventType s1,
323 real x2,CLEventType s2,
324 gboolean fill)
326 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
327 Point pts[4];
328 real ymid = .5 * (chronoline->y_down + chronoline->y_up);
329 real y_down = chronoline->y_down;
330 real y_up = chronoline->y_up;
332 pts[0].x = pts[1].x = x1;
333 pts[2].x = pts[3].x = x2;
335 if (s1 == CLE_OFF) {
336 pts[0].y = pts[1].y = ymid;
337 } else {
338 pts[0].y = y_down;
339 pts[1].y = y_up;
341 if (s2 == CLE_OFF) {
342 pts[2].y = pts[3].y = ymid;
343 } else {
344 pts[3].y = y_down;
345 pts[2].y = y_up;
348 if (fill) {
349 if ((s1 == CLE_UNKNOWN) || (s2 == CLE_UNKNOWN)) {
350 renderer_ops->fill_polygon(renderer,pts,sizeof(pts)/sizeof(pts[0]),
351 &chronoline->datagray);
352 } else {
353 renderer_ops->fill_polygon(renderer,pts,sizeof(pts)/sizeof(pts[0]),
354 &color_white);
356 } else {
357 renderer_ops->draw_line(renderer,&pts[1],&pts[2],
358 &chronoline->data_color);
359 renderer_ops->draw_line(renderer,&pts[0],&pts[3],
360 &chronoline->data_color);
366 inline static void
367 chronoline_draw_really(Chronoline *chronoline, DiaRenderer *renderer,
368 gboolean fill)
370 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
371 Element *elem = &chronoline->element;
372 real oldx,newx;
374 gboolean finished = FALSE;
375 CLEventType state = CLE_UNKNOWN;
377 CLEventList *lst;
378 CLEvent *evt;
379 real start_time = chronoline->start_time;
380 real end_time = chronoline->end_time;
382 oldx = elem->corner.x;
384 lst = chronoline->evtlist;
385 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
386 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
387 renderer_ops->set_linewidth(renderer,chronoline->data_lwidth);
389 while (lst) {
390 evt = (CLEvent *)lst->data;
391 g_assert(evt);
392 if (evt->time >= start_time) {
393 if (evt->time <= end_time) {
394 /* regular point */
395 newx = evt->x;
397 if (chronoline->multibit)
398 cld_multibit(chronoline,renderer,oldx,state,newx,evt->type,fill);
399 else
400 cld_onebit(chronoline,renderer,oldx,state,newx,evt->type,fill);
401 oldx = newx;
402 } else {
403 newx = elem->corner.x + elem->width;
404 if (!finished) {
405 if (chronoline->multibit)
406 cld_multibit(chronoline,renderer,oldx,state,newx,evt->type,fill);
407 else
408 cld_onebit(chronoline,renderer,oldx,state,newx,evt->type,fill);
409 finished = TRUE;
413 state = evt->type;
414 lst = g_slist_next(lst);
416 if (!finished) {
417 newx = chronoline->element.corner.x+chronoline->element.width;
418 if (chronoline->multibit)
419 cld_multibit(chronoline,renderer,oldx,state,newx,state,fill);
420 else
421 cld_onebit(chronoline,renderer,oldx,state,newx,state,fill);
425 static void
426 chronoline_draw(Chronoline *chronoline, DiaRenderer *renderer)
428 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
429 Element *elem;
430 Point lr_corner;
431 Point p1,p2,p3;
433 g_assert(chronoline != NULL);
434 g_assert(renderer != NULL);
436 elem = &chronoline->element;
438 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
439 renderer_ops->set_linestyle(renderer, LINESTYLE_DOTTED);
440 renderer_ops->set_linewidth(renderer, chronoline->main_lwidth);
441 p1.x = elem->corner.x + elem->width;
442 p1.y = elem->corner.y;
443 renderer_ops->draw_line(renderer,&elem->corner,&p1,&chronoline->gray);
445 chronoline_draw_really(chronoline,renderer,TRUE);
446 chronoline_draw_really(chronoline,renderer,FALSE);
448 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
450 lr_corner.x = elem->corner.x + elem->width;
451 lr_corner.y = elem->corner.y + elem->height;
453 p1.x = elem->corner.x;
454 p2.x = lr_corner.x;
455 p1.y = p2.y = chronoline->y_down;
457 renderer_ops->set_linewidth(renderer, chronoline->main_lwidth);
458 renderer_ops->draw_line(renderer,&p1,&p2,&chronoline->color);
459 p2.x = p1.x = elem->corner.x;
460 p1.y = chronoline->y_down;
461 p2.y = chronoline->y_up;
462 renderer_ops->draw_line(renderer,&p1,&p2,&chronoline->color);
464 renderer_ops->set_font(renderer, chronoline->font,
465 chronoline->font_size);
466 p3.y = lr_corner.y - chronoline->font_size
467 + dia_font_ascent(chronoline->name,
468 chronoline->font,chronoline->font_size);
469 p3.x = p1.x - chronoline->main_lwidth;
470 renderer_ops->draw_string(renderer,chronoline->name,&p3,ALIGN_RIGHT,
471 &chronoline->color);
474 inline static void grayify(Color *col,Color *src)
476 col->red = .5 * (src->red + color_white.red);
477 col->green = .5 * (src->green + color_white.green);
478 col->blue = .5 * (src->blue + color_white.blue);
481 static void
482 chronoline_update_data(Chronoline *chronoline)
484 Element *elem = &chronoline->element;
485 DiaObject *obj = &elem->object;
486 real time_span;
487 Point ur_corner;
488 int shouldbe,i;
489 real realheight;
490 CLEventList *lst;
491 CLEvent *evt;
492 GSList *conn_elem;
493 ElementBBExtras *extra = &elem->extra_spacing;
495 grayify(&chronoline->datagray,&chronoline->data_color);
496 grayify(&chronoline->gray,&chronoline->color);
498 chronoline->labelwidth = dia_font_string_width(chronoline->name,
499 chronoline->font,
500 chronoline->font_size);
502 chronoline->y_up = elem->corner.y;
503 chronoline->y_down = elem->corner.y + elem->height;
505 /* Now, update the drawing helper counters */
506 time_span = chronoline->end_time - chronoline->start_time;
507 if (time_span == 0) {
508 chronoline->end_time = chronoline->start_time + .1;
509 time_span = .1;
510 } else if (time_span < 0) {
511 chronoline->start_time = chronoline->end_time;
512 time_span = -time_span;
513 chronoline->end_time = chronoline->start_time + time_span;
516 extra->border_trans = chronoline->main_lwidth / 2;
517 element_update_boundingbox(elem);
519 /* fix boundingbox for special extras: */
520 realheight = obj->bounding_box.bottom - obj->bounding_box.top;
521 realheight = MAX(realheight,chronoline->font_size);
523 obj->bounding_box.left -= chronoline->labelwidth;
524 obj->bounding_box.bottom = obj->bounding_box.top + realheight +
525 chronoline->main_lwidth;
527 obj->position = elem->corner;
529 element_update_handles(elem);
531 /* Update connections: */
532 ur_corner.x = elem->corner.x + elem->width;
533 ur_corner.y = elem->corner.y;
535 /* Update the events : count those which fit in [start_time,end_time].
538 reparse_clevent(chronoline->events,
539 &chronoline->evtlist,
540 &chronoline->checksum,
541 chronoline->rise_time,
542 chronoline->fall_time,
543 chronoline->end_time);
544 shouldbe = 0;
545 lst = chronoline->evtlist;
547 while (lst) {
548 evt = (CLEvent *)lst->data;
550 if ((evt->time >= chronoline->start_time) &&
551 (evt->time <= chronoline->end_time))
552 shouldbe++;
553 lst = g_slist_next(lst);
556 connpointline_adjust_count(chronoline->snap,shouldbe,&ur_corner);
557 connpointline_update(chronoline->snap);
558 /* connpointline_putonaline(chronoline->snap,&elem->corner,&ur_corner); */
560 /* Now fix the actual connection point positions : */
561 lst = chronoline->evtlist;
562 conn_elem = chronoline->snap->connections;
563 i = 0;
564 while (lst && lst->data && conn_elem && conn_elem->data) {
565 ConnectionPoint *cp = (ConnectionPoint *)(conn_elem->data);
566 evt = (CLEvent *)lst->data;
568 if ((evt->time >= chronoline->start_time) &&
569 (evt->time <= chronoline->end_time)) {
570 evt->x = elem->corner.x +
571 elem->width*(evt->time-chronoline->start_time)/time_span;
572 g_assert(cp);
573 g_assert(i < chronoline->snap->num_connections);
574 cp->pos.x = evt->x;
575 if (chronoline->multibit) {
576 cp->pos.y = .5 * (chronoline->y_down + chronoline->y_up);
577 cp->directions = DIR_ALL;
578 } else {
579 cp->pos.y = (evt->type==CLE_OFF?
580 chronoline->y_down:chronoline->y_up);
581 cp->directions = (evt->type==CLE_OFF?DIR_SOUTH:DIR_NORTH);
583 i++;
584 conn_elem = g_slist_next(conn_elem);
585 } else if (evt->time >= chronoline->start_time) {
586 evt->x = elem->corner.x;
587 } else if (evt->time <= chronoline->end_time) {
588 evt->x = elem->corner.x + elem->width;
590 lst = g_slist_next(lst);
594 static DiaObject *
595 chronoline_create(Point *startpoint,
596 void *user_data,
597 Handle **handle1,
598 Handle **handle2)
600 Chronoline *chronoline;
601 Element *elem;
602 DiaObject *obj;
604 chronoline = g_new0(Chronoline,1);
605 elem = &(chronoline->element);
607 obj = &(chronoline->element.object);
608 obj->type = &chronoline_type;
609 obj->ops = &chronoline_ops;
611 chronoline->snap = connpointline_create(obj,0);
613 elem->corner = *startpoint;
614 elem->width = 20.0;
615 elem->height = 3.0;
617 element_init(elem, 8, 0);
619 chronoline->name = g_strdup("");
620 chronoline->events = g_strdup("");
622 chronoline->font = dia_font_new_from_style (DIA_FONT_SANS,1.0);
623 chronoline->font_size = 1.0;
624 chronoline->font_color = color_black;
625 chronoline->start_time = 0.0;
626 chronoline->end_time = 20.0;
627 chronoline->rise_time = .3;
628 chronoline->fall_time = .3;
629 chronoline->color = color_black;
630 chronoline->main_lwidth = .1;
631 chronoline->data_lwidth = .1;
632 chronoline->data_color.red = 1.0;
633 chronoline->data_color.green = 0.0;
634 chronoline->data_color.blue = 0.0;
635 chronoline->multibit = FALSE;
637 chronoline->evtlist = NULL;
638 chronoline_update_data(chronoline);
640 *handle1 = NULL;
641 *handle2 = obj->handles[7];
642 return &chronoline->element.object;
645 static void
646 chronoline_destroy(Chronoline *chronoline)
648 g_free(chronoline->name);
649 g_free(chronoline->events);
650 dia_font_unref(chronoline->font);
651 connpointline_destroy(chronoline->snap);
652 destroy_clevent_list(chronoline->evtlist);
653 element_destroy(&chronoline->element);
656 static DiaObject *
657 chronoline_load(ObjectNode obj_node, int version, const char *filename)
659 return object_load_using_properties(&chronoline_type,
660 obj_node,version,filename);