1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * Chronogram objects support
5 * Copyright (C) 2000, 2001 Cyrille Chepelov
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.
37 #include "connectionpoint.h"
38 #include "diarenderer.h"
39 #include "attributes.h"
43 #include "connpoint_line.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
{
73 /* computed values : */
74 ConnPointLine
*snap
; /* not saved ; num_connections derived from
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
,
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
,
105 static void chronoline_set_props(Chronoline
*chronoline
,
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 */
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
,
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
,
232 chronoline_get_props(Chronoline
*chronoline
, GPtrArray
*props
)
234 object_get_props_from_offsets(&chronoline
->element
.object
,
235 chronoline_offsets
,props
);
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
);
247 chronoline_distance_from(Chronoline
*chronoline
, Point
*point
)
249 DiaObject
*obj
= &chronoline
->element
.object
;
250 return distance_rectangle_point(&obj
->bounding_box
, point
);
254 chronoline_select(Chronoline
*chronoline
, Point
*clicked_point
,
255 DiaRenderer
*interactive_renderer
)
257 element_update_handles(&chronoline
->element
);
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
);
269 element_move_handle(&chronoline
->element
, handle
->id
, to
, cp
,
271 chronoline_update_data(chronoline
);
277 chronoline_move(Chronoline
*chronoline
, Point
*to
)
279 chronoline
->element
.corner
= *to
;
280 chronoline_update_data(chronoline
);
286 cld_onebit(Chronoline
*chronoline
,
287 DiaRenderer
*renderer
,
288 real x1
,CLEventType s1
,
289 real x2
,CLEventType s2
,
292 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
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
;
305 if ((s1
== CLE_UNKNOWN
) || (s2
== CLE_UNKNOWN
)) {
306 renderer_ops
->fill_polygon(renderer
,pts
,sizeof(pts
)/sizeof(pts
[0]),
307 &chronoline
->datagray
);
309 renderer_ops
->fill_polygon(renderer
,pts
,sizeof(pts
)/sizeof(pts
[0]),
313 renderer_ops
->draw_line(renderer
,&pts
[1],&pts
[2],
314 &chronoline
->data_color
);
320 cld_multibit(Chronoline
*chronoline
,
321 DiaRenderer
*renderer
,
322 real x1
,CLEventType s1
,
323 real x2
,CLEventType s2
,
326 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
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
;
336 pts
[0].y
= pts
[1].y
= ymid
;
342 pts
[2].y
= pts
[3].y
= ymid
;
349 if ((s1
== CLE_UNKNOWN
) || (s2
== CLE_UNKNOWN
)) {
350 renderer_ops
->fill_polygon(renderer
,pts
,sizeof(pts
)/sizeof(pts
[0]),
351 &chronoline
->datagray
);
353 renderer_ops
->fill_polygon(renderer
,pts
,sizeof(pts
)/sizeof(pts
[0]),
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
);
367 chronoline_draw_really(Chronoline
*chronoline
, DiaRenderer
*renderer
,
370 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
371 Element
*elem
= &chronoline
->element
;
374 gboolean finished
= FALSE
;
375 CLEventType state
= CLE_UNKNOWN
;
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
);
390 evt
= (CLEvent
*)lst
->data
;
392 if (evt
->time
>= start_time
) {
393 if (evt
->time
<= end_time
) {
397 if (chronoline
->multibit
)
398 cld_multibit(chronoline
,renderer
,oldx
,state
,newx
,evt
->type
,fill
);
400 cld_onebit(chronoline
,renderer
,oldx
,state
,newx
,evt
->type
,fill
);
403 newx
= elem
->corner
.x
+ elem
->width
;
405 if (chronoline
->multibit
)
406 cld_multibit(chronoline
,renderer
,oldx
,state
,newx
,evt
->type
,fill
);
408 cld_onebit(chronoline
,renderer
,oldx
,state
,newx
,evt
->type
,fill
);
414 lst
= g_slist_next(lst
);
417 newx
= chronoline
->element
.corner
.x
+chronoline
->element
.width
;
418 if (chronoline
->multibit
)
419 cld_multibit(chronoline
,renderer
,oldx
,state
,newx
,state
,fill
);
421 cld_onebit(chronoline
,renderer
,oldx
,state
,newx
,state
,fill
);
426 chronoline_draw(Chronoline
*chronoline
, DiaRenderer
*renderer
)
428 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
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
;
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
,
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
);
482 chronoline_update_data(Chronoline
*chronoline
)
484 Element
*elem
= &chronoline
->element
;
485 DiaObject
*obj
= &elem
->object
;
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
,
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;
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
);
545 lst
= chronoline
->evtlist
;
548 evt
= (CLEvent
*)lst
->data
;
550 if ((evt
->time
>= chronoline
->start_time
) &&
551 (evt
->time
<= chronoline
->end_time
))
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
;
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
;
573 g_assert(i
< chronoline
->snap
->num_connections
);
575 if (chronoline
->multibit
) {
576 cp
->pos
.y
= .5 * (chronoline
->y_down
+ chronoline
->y_up
);
577 cp
->directions
= DIR_ALL
;
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
);
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
);
595 chronoline_create(Point
*startpoint
,
600 Chronoline
*chronoline
;
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
;
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
);
641 *handle2
= obj
->handles
[7];
642 return &chronoline
->element
.object
;
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
);
657 chronoline_load(ObjectNode obj_node
, int version
, const char *filename
)
659 return object_load_using_properties(&chronoline_type
,
660 obj_node
,version
,filename
);