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 "pixmaps/chronoref.xpm"
50 #define DEFAULT_WIDTH 7.0
51 #define DEFAULT_HEIGHT 5.0
53 typedef struct _Chronoref
{
68 ConnPointLine
*scale
; /* not saved ; num_connections derived from
69 start_time, end_time, time_step. */
70 real majgrad_height
,mingrad_height
;
71 real firstmaj
,firstmin
; /* in time units */
72 real firstmaj_x
,firstmin_x
,majgrad
,mingrad
; /* in dia graphic units */
76 static real
chronoref_distance_from(Chronoref
*chronoref
, Point
*point
);
77 static void chronoref_select(Chronoref
*chronoref
, Point
*clicked_point
,
78 DiaRenderer
*interactive_renderer
);
79 static ObjectChange
* chronoref_move_handle(Chronoref
*chronoref
, Handle
*handle
,
80 Point
*to
, ConnectionPoint
*cp
,
81 HandleMoveReason reason
,
82 ModifierKeys modifiers
);
83 static ObjectChange
* chronoref_move(Chronoref
*chronoref
, Point
*to
);
84 static void chronoref_draw(Chronoref
*chronoref
, DiaRenderer
*renderer
);
85 static void chronoref_update_data(Chronoref
*chronoref
);
86 static DiaObject
*chronoref_create(Point
*startpoint
,
90 static void chronoref_destroy(Chronoref
*chronoref
);
91 static DiaObject
*chronoref_load(ObjectNode obj_node
, int version
,
92 const char *filename
);
93 static PropDescription
*chronoref_describe_props(Chronoref
*chronoref
);
94 static void chronoref_get_props(Chronoref
*chronoref
,
96 static void chronoref_set_props(Chronoref
*chronoref
,
101 static ObjectTypeOps chronoref_type_ops
=
103 (CreateFunc
) chronoref_create
,
104 (LoadFunc
) chronoref_load
/*using properties*/,
105 (SaveFunc
) object_save_using_properties
,
106 (GetDefaultsFunc
) NULL
,
107 (ApplyDefaultsFunc
) NULL
110 DiaObjectType chronoref_type
=
112 "chronogram - reference", /* name */
114 (char **) chronoref_xpm
, /* pixmap */
116 &chronoref_type_ops
/* ops */
120 static ObjectOps chronoref_ops
= {
121 (DestroyFunc
) chronoref_destroy
,
122 (DrawFunc
) chronoref_draw
,
123 (DistanceFunc
) chronoref_distance_from
,
124 (SelectFunc
) chronoref_select
,
125 (CopyFunc
) object_copy_using_properties
,
126 (MoveFunc
) chronoref_move
,
127 (MoveHandleFunc
) chronoref_move_handle
,
128 (GetPropertiesFunc
) object_create_props_dialog
,
129 (ApplyPropertiesFunc
) object_apply_props_from_dialog
,
130 (ObjectMenuFunc
) NULL
,
131 (DescribePropsFunc
) chronoref_describe_props
,
132 (GetPropsFunc
) chronoref_get_props
,
133 (SetPropsFunc
) chronoref_set_props
136 static PropNumData time_range
= { -32767.0, 32768.0, 0.1};
137 static PropNumData step_range
= { 0.0, 1000.0, 0.1};
139 static PropDescription chronoref_props
[] = {
140 ELEMENT_COMMON_PROPERTIES
,
141 PROP_STD_MULTICOL_BEGIN
,
143 PROP_MULTICOL_COLUMN("time"),
144 PROP_FRAME_BEGIN("time",0,N_("Time data")),
145 { "start_time",PROP_TYPE_REAL
,PROP_FLAG_VISIBLE
,
146 N_("Start time"),NULL
,&time_range
},
147 { "end_time",PROP_TYPE_REAL
,PROP_FLAG_VISIBLE
,
148 N_("End time"),NULL
,&time_range
},
149 { "time_step",PROP_TYPE_REAL
,PROP_FLAG_VISIBLE
,
150 N_("Major time step"),NULL
,&step_range
},
151 { "time_lstep",PROP_TYPE_REAL
,PROP_FLAG_VISIBLE
,
152 N_("Minor time step"),NULL
,&step_range
},
153 PROP_FRAME_END("time",0),
155 PROP_MULTICOL_COLUMN("aspect"),
156 PROP_FRAME_BEGIN("aspect",0,N_("Aspect")),
157 { "color", PROP_TYPE_COLOUR
, PROP_FLAG_VISIBLE
,
158 N_("Line color"),NULL
},
159 { "main_lwidth", PROP_TYPE_REAL
, PROP_FLAG_VISIBLE
,
160 N_("Line width"),NULL
, &prop_std_line_width_data
},
161 { "light_lwidth", PROP_TYPE_REAL
, PROP_FLAG_VISIBLE
,
162 N_("Minor step line width"),NULL
, &prop_std_line_width_data
},
164 { "font", PROP_TYPE_FONT
, PROP_FLAG_VISIBLE
, N_("Font"), NULL
, NULL
},
165 { "font_size", PROP_TYPE_REAL
, PROP_FLAG_VISIBLE
,
166 N_("Font size"), NULL
, &prop_std_text_height_data
},
167 { "font_color", PROP_TYPE_COLOUR
, PROP_FLAG_VISIBLE
,
168 N_("Text color"), NULL
, NULL
},
169 PROP_FRAME_END("aspect",0),
171 PROP_STD_MULTICOL_END
,
175 static PropDescription
*
176 chronoref_describe_props(Chronoref
*chronoref
)
178 if (chronoref_props
[0].quark
== 0) {
179 prop_desc_list_calculate_quarks(chronoref_props
);
181 return chronoref_props
;
184 static PropOffset chronoref_offsets
[] = {
185 ELEMENT_COMMON_PROPERTIES_OFFSETS
,
186 PROP_OFFSET_STD_MULTICOL_BEGIN
,
188 PROP_OFFSET_MULTICOL_COLUMN("time"),
189 PROP_OFFSET_FRAME_BEGIN("time"),
190 { "start_time",PROP_TYPE_REAL
, offsetof(Chronoref
,start_time
)},
191 { "end_time",PROP_TYPE_REAL
, offsetof(Chronoref
,end_time
)},
192 { "time_step",PROP_TYPE_REAL
, offsetof(Chronoref
,time_step
)},
193 { "time_lstep",PROP_TYPE_REAL
, offsetof(Chronoref
,time_lstep
)},
194 PROP_OFFSET_FRAME_END("time"),
196 PROP_OFFSET_MULTICOL_COLUMN("aspect"),
197 PROP_OFFSET_FRAME_BEGIN("aspect"),
198 { "color", PROP_TYPE_COLOUR
, offsetof(Chronoref
,color
)},
199 { "main_lwidth", PROP_TYPE_REAL
, offsetof(Chronoref
,main_lwidth
)},
200 { "light_lwidth", PROP_TYPE_REAL
, offsetof(Chronoref
,light_lwidth
)},
201 { "font", PROP_TYPE_FONT
, offsetof(Chronoref
,font
)},
202 { "font_size", PROP_TYPE_REAL
, offsetof(Chronoref
,font_size
)},
203 { "font_color", PROP_TYPE_COLOUR
, offsetof(Chronoref
,font_color
)},
204 PROP_OFFSET_FRAME_END("aspect"),
206 PROP_OFFSET_STD_MULTICOL_END
,
211 chronoref_get_props(Chronoref
*chronoref
, GPtrArray
*props
)
213 object_get_props_from_offsets(&chronoref
->element
.object
,
214 chronoref_offsets
,props
);
218 chronoref_set_props(Chronoref
*chronoref
, GPtrArray
*props
)
220 object_set_props_from_offsets(&chronoref
->element
.object
,
221 chronoref_offsets
,props
);
222 chronoref_update_data(chronoref
);
226 chronoref_distance_from(Chronoref
*chronoref
, Point
*point
)
228 DiaObject
*obj
= &chronoref
->element
.object
;
229 return distance_rectangle_point(&obj
->bounding_box
, point
);
233 chronoref_select(Chronoref
*chronoref
, Point
*clicked_point
,
234 DiaRenderer
*interactive_renderer
)
236 element_update_handles(&chronoref
->element
);
240 chronoref_move_handle(Chronoref
*chronoref
, Handle
*handle
,
241 Point
*to
, ConnectionPoint
*cp
,
242 HandleMoveReason reason
, ModifierKeys modifiers
)
244 g_assert(chronoref
!=NULL
);
245 g_assert(handle
!=NULL
);
248 element_move_handle(&chronoref
->element
, handle
->id
, to
, cp
,
250 chronoref_update_data(chronoref
);
256 chronoref_move(Chronoref
*chronoref
, Point
*to
)
258 chronoref
->element
.corner
= *to
;
259 chronoref_update_data(chronoref
);
265 chronoref_draw(Chronoref
*chronoref
, DiaRenderer
*renderer
)
267 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
273 assert(renderer
!= NULL
);
275 elem
= &chronoref
->element
;
277 renderer_ops
->set_linestyle(renderer
, LINESTYLE_SOLID
);
278 renderer_ops
->set_linejoin(renderer
, LINEJOIN_MITER
);
281 lr_corner
.x
= elem
->corner
.x
+ elem
->width
;
282 lr_corner
.y
= elem
->corner
.y
+ elem
->height
;
284 p1
.y
= p2
.y
= elem
->corner
.y
;
286 renderer_ops
->set_font(renderer
, chronoref
->font
, chronoref
->font_size
);
287 p3
.y
= p2
.y
+ chronoref
->majgrad_height
+
288 dia_font_ascent("1",chronoref
->font
, chronoref
->font_size
);
290 renderer_ops
->set_linewidth(renderer
, chronoref
->light_lwidth
);
291 if (chronoref
->time_lstep
> 0.0) {
292 p2
.y
= p1
.y
+ chronoref
->mingrad_height
;
293 for (t
= chronoref
->firstmaj
, p1
.x
= chronoref
->firstmin_x
;
295 t
+= chronoref
->time_lstep
, p1
.x
+= chronoref
->mingrad
) {
298 renderer_ops
->draw_line(renderer
,&p1
,&p2
,&chronoref
->color
);
302 renderer_ops
->set_linewidth(renderer
, chronoref
->main_lwidth
);
303 if (chronoref
->time_step
> 0.0) {
304 p2
.y
= p1
.y
+ chronoref
->majgrad_height
;
306 for (t
= chronoref
->firstmaj
, p1
.x
= chronoref
->firstmaj_x
;
308 t
+= chronoref
->time_step
, p1
.x
+= chronoref
->majgrad
) {
312 renderer_ops
->draw_line(renderer
,&p1
,&p2
,&chronoref
->color
);
313 g_snprintf(time
,sizeof(time
),chronoref
->spec
,t
);
314 renderer_ops
->draw_string(renderer
,time
,&p3
,ALIGN_CENTER
,
315 &chronoref
->font_color
);
318 p1
.x
= elem
->corner
.x
;
320 p1
.y
= p2
.y
= elem
->corner
.y
;
322 renderer_ops
->draw_line(renderer
,&p1
,&p2
,&chronoref
->color
);
326 chronoref_update_data(Chronoref
*chronoref
)
328 Element
*elem
= &chronoref
->element
;
329 DiaObject
*obj
= &elem
->object
;
336 ElementBBExtras
*extra
= &elem
->extra_spacing
;
338 chronoref
->majgrad_height
= elem
->height
;
339 chronoref
->mingrad_height
= elem
->height
/ 3.0;
341 /* build i = -log_{10}(time_step), then make a %.if format out of it. */
345 while (t
> chronoref
->time_step
) {
349 g_snprintf(chronoref
->spec
,sizeof(chronoref
->spec
),"%%.%df",i
);
350 g_snprintf(biglabel
,sizeof(biglabel
),chronoref
->spec
,
351 MIN(-ABS(chronoref
->start_time
),-ABS(chronoref
->end_time
)));
353 labelwidth
= dia_font_string_width(biglabel
,chronoref
->font
,
354 chronoref
->font_size
);
356 /* Now, update the drawing helper counters */
357 time_span
= chronoref
->end_time
- chronoref
->start_time
;
358 if (time_span
== 0) {
359 chronoref
->end_time
= chronoref
->start_time
+ .1;
361 } else if (time_span
< 0) {
362 chronoref
->start_time
= chronoref
->end_time
;
363 time_span
= -time_span
;
364 chronoref
->end_time
= chronoref
->start_time
+ time_span
;
367 chronoref
->firstmaj
= chronoref
->time_step
*
368 ceil(chronoref
->start_time
/ chronoref
->time_step
);
369 if (chronoref
->firstmaj
< chronoref
->start_time
)
370 chronoref
->firstmaj
+= chronoref
->time_step
;
371 chronoref
->firstmin
= chronoref
->time_lstep
*
372 ceil(chronoref
->start_time
/ chronoref
->time_lstep
);
373 if (chronoref
->firstmin
< chronoref
->start_time
)
374 chronoref
->firstmin
+= chronoref
->time_lstep
;
376 chronoref
->firstmaj_x
= elem
->corner
.x
+
377 elem
->width
*((chronoref
->firstmaj
-chronoref
->start_time
)/time_span
);
378 chronoref
->firstmin_x
= elem
->corner
.x
+
379 elem
->width
*((chronoref
->firstmin
-chronoref
->start_time
)/time_span
);
380 chronoref
->majgrad
= (chronoref
->time_step
* elem
->width
) / time_span
;
381 chronoref
->mingrad
= (chronoref
->time_lstep
* elem
->width
) / time_span
;
383 extra
->border_trans
= chronoref
->main_lwidth
/2;
384 element_update_boundingbox(elem
);
386 /* fix boundingbox for special extras: */
387 obj
->bounding_box
.left
-= (chronoref
->font_size
+ labelwidth
)/2;
388 obj
->bounding_box
.bottom
+= chronoref
->font_size
;
389 obj
->bounding_box
.right
+= (chronoref
->font_size
+ labelwidth
)/2;
391 obj
->position
= elem
->corner
;
393 element_update_handles(elem
);
395 /* Update connections: */
396 ur_corner
.x
= elem
->corner
.x
+ elem
->width
;
397 ur_corner
.y
= elem
->corner
.y
;
399 shouldbe
= (int)(ceil((chronoref
->end_time
-chronoref
->firstmin
)/
400 chronoref
->time_lstep
));
401 if (shouldbe
== 0) shouldbe
++;
402 if (shouldbe
< 0) shouldbe
= 0;
403 shouldbe
++; /* off by one.. */
405 connpointline_adjust_count(chronoref
->scale
,shouldbe
,&ur_corner
);
406 connpointline_update(chronoref
->scale
);
408 point_copy(&p1
,&elem
->corner
); point_copy(&p2
,&ur_corner
);
409 p1
.x
-= chronoref
->mingrad
;
410 p2
.x
+= chronoref
->mingrad
;
411 connpointline_putonaline(chronoref
->scale
,&p1
,&p2
);
415 chronoref_create(Point
*startpoint
,
420 Chronoref
*chronoref
;
424 chronoref
= g_new0(Chronoref
,1);
425 elem
= &(chronoref
->element
);
427 obj
= &(chronoref
->element
.object
);
428 obj
->type
= &chronoref_type
;
429 obj
->ops
= &chronoref_ops
;
431 chronoref
->scale
= connpointline_create(obj
,0);
433 elem
->corner
= *startpoint
;
437 element_init(elem
, 8, 0);
439 chronoref
->font
= dia_font_new_from_style (DIA_FONT_SANS
,1.0);
440 chronoref
->font_size
= 1.0;
441 chronoref
->font_color
= color_black
;
442 chronoref
->start_time
= 0.0;
443 chronoref
->end_time
= 20.0;
444 chronoref
->time_step
= 5.0;
445 chronoref
->time_lstep
= 1.0;
446 chronoref
->color
= color_black
;
447 chronoref
->main_lwidth
= .1;
448 chronoref
->light_lwidth
= .05;
450 chronoref_update_data(chronoref
);
453 *handle2
= obj
->handles
[7];
454 return &chronoref
->element
.object
;
458 chronoref_destroy(Chronoref
*chronoref
)
460 dia_font_unref(chronoref
->font
);
461 connpointline_destroy(chronoref
->scale
);
462 element_destroy(&chronoref
->element
);
466 chronoref_load(ObjectNode obj_node
, int version
, const char *filename
)
468 return object_load_using_properties(&chronoref_type
,
469 obj_node
,version
,filename
);