1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 *
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.
9 *
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.
14 *
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.
19 *
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.
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <assert.h>
30 #include <math.h>
31 #include <string.h>
32 #include <glib.h>
33
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"
46
47 #include "chronogram.h"
48 #include "pixmaps/chronoref.xpm"
49
50 #define DEFAULT_WIDTH 7.0
51 #define DEFAULT_HEIGHT 5.0
52
53 typedef struct _Chronoref {
54 Element element;
55
56 real main_lwidth;
57 real light_lwidth;
58 Color color;
59 real start_time;
60 real end_time;
61 real time_step;
62 real time_lstep;
63
64 DiaFont *font;
65 real font_size;
66 Color font_color;
67
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 */
73 char spec[10];
74 } Chronoref;
75
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,
87 void *user_data,
88 Handle **handle1,
89 Handle **handle2);
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,
95 GPtrArray *props);
96 static void chronoref_set_props(Chronoref *chronoref,
97 GPtrArray *props);
98
99
100
101 static ObjectTypeOps chronoref_type_ops =
102 {
103 (CreateFunc) chronoref_create,
104 (LoadFunc) chronoref_load/*using properties*/,
105 (SaveFunc) object_save_using_properties,
106 (GetDefaultsFunc) NULL,
107 (ApplyDefaultsFunc) NULL
108 };
109
110 DiaObjectType chronoref_type =
111 {
112 "chronogram - reference", /* name */
113 0, /* version */
114 (char **) chronoref_xpm, /* pixmap */
115
116 &chronoref_type_ops /* ops */
117 };
118
119
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 (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
130 (ObjectMenuFunc) NULL,
131 (DescribePropsFunc) chronoref_describe_props,
132 (GetPropsFunc) chronoref_get_props,
133 (SetPropsFunc) chronoref_set_props,
134 (TextEditFunc) 0,
135 (ApplyPropertiesListFunc) object_apply_props,
136 };
137
138 static PropNumData time_range = { -32767.0, 32768.0, 0.1};
139 static PropNumData step_range = { 0.0, 1000.0, 0.1};
140
141 static PropDescription chronoref_props[] = {
142 ELEMENT_COMMON_PROPERTIES,
143 PROP_MULTICOL_BEGIN("chronoref"),
144
145 PROP_MULTICOL_COLUMN("time"),
146 PROP_FRAME_BEGIN("time",0,N_("Time data")),
147 { "start_time",PROP_TYPE_REAL,PROP_FLAG_VISIBLE,
148 N_("Start time"),NULL,&time_range},
149 { "end_time",PROP_TYPE_REAL,PROP_FLAG_VISIBLE,
150 N_("End time"),NULL,&time_range},
151 { "time_step",PROP_TYPE_REAL,PROP_FLAG_VISIBLE,
152 N_("Major time step"),NULL,&step_range},
153 { "time_lstep",PROP_TYPE_REAL,PROP_FLAG_VISIBLE,
154 N_("Minor time step"),NULL,&step_range},
155 PROP_FRAME_END("time",0),
156
157 PROP_MULTICOL_COLUMN("aspect"),
158 PROP_FRAME_BEGIN("aspect",0,N_("Aspect")),
159 { "color", PROP_TYPE_COLOUR, PROP_FLAG_VISIBLE,
160 N_("Line color"),NULL},
161 { "main_lwidth", PROP_TYPE_LENGTH, PROP_FLAG_VISIBLE,
162 N_("Line width"),NULL, &prop_std_line_width_data},
163 { "light_lwidth", PROP_TYPE_LENGTH, PROP_FLAG_VISIBLE,
164 N_("Minor step line width"),NULL, &prop_std_line_width_data},
165
166 { "font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE, N_("Font"), NULL, NULL },
167 { "font_size", PROP_TYPE_FONTSIZE, PROP_FLAG_VISIBLE,
168 N_("Font size"), NULL, &prop_std_text_height_data },
169 { "font_color", PROP_TYPE_COLOUR, PROP_FLAG_VISIBLE,
170 N_("Text color"), NULL, NULL },
171 PROP_FRAME_END("aspect",0),
172
173 PROP_MULTICOL_END("chronoref"),
174 {NULL}
175 };
176
177 static PropDescription *
chronoref_describe_props(Chronoref * chronoref)178 chronoref_describe_props(Chronoref *chronoref)
179 {
180 if (chronoref_props[0].quark == 0) {
181 prop_desc_list_calculate_quarks(chronoref_props);
182 }
183 return chronoref_props;
184 }
185
186 static PropOffset chronoref_offsets[] = {
187 ELEMENT_COMMON_PROPERTIES_OFFSETS,
188 PROP_OFFSET_MULTICOL_BEGIN("chronref"),
189
190 PROP_OFFSET_MULTICOL_COLUMN("time"),
191 PROP_OFFSET_FRAME_BEGIN("time"),
192 { "start_time",PROP_TYPE_REAL, offsetof(Chronoref,start_time)},
193 { "end_time",PROP_TYPE_REAL, offsetof(Chronoref,end_time)},
194 { "time_step",PROP_TYPE_REAL, offsetof(Chronoref,time_step)},
195 { "time_lstep",PROP_TYPE_REAL, offsetof(Chronoref,time_lstep)},
196 PROP_OFFSET_FRAME_END("time"),
197
198 PROP_OFFSET_MULTICOL_COLUMN("aspect"),
199 PROP_OFFSET_FRAME_BEGIN("aspect"),
200 { "color", PROP_TYPE_COLOUR, offsetof(Chronoref,color)},
201 { "main_lwidth", PROP_TYPE_LENGTH, offsetof(Chronoref,main_lwidth)},
202 { "light_lwidth", PROP_TYPE_LENGTH, offsetof(Chronoref,light_lwidth)},
203 { "font", PROP_TYPE_FONT, offsetof(Chronoref,font)},
204 { "font_size", PROP_TYPE_FONTSIZE, offsetof(Chronoref,font_size)},
205 { "font_color", PROP_TYPE_COLOUR, offsetof(Chronoref,font_color)},
206 PROP_OFFSET_FRAME_END("aspect"),
207
208 PROP_OFFSET_MULTICOL_END("chronref"),
209 {NULL}
210 };
211
212 static void
chronoref_get_props(Chronoref * chronoref,GPtrArray * props)213 chronoref_get_props(Chronoref *chronoref, GPtrArray *props)
214 {
215 object_get_props_from_offsets(&chronoref->element.object,
216 chronoref_offsets,props);
217 }
218
219 static void
chronoref_set_props(Chronoref * chronoref,GPtrArray * props)220 chronoref_set_props(Chronoref *chronoref, GPtrArray *props)
221 {
222 object_set_props_from_offsets(&chronoref->element.object,
223 chronoref_offsets,props);
224 chronoref_update_data(chronoref);
225 }
226
227 static real
chronoref_distance_from(Chronoref * chronoref,Point * point)228 chronoref_distance_from(Chronoref *chronoref, Point *point)
229 {
230 DiaObject *obj = &chronoref->element.object;
231 return distance_rectangle_point(&obj->bounding_box, point);
232 }
233
234 static void
chronoref_select(Chronoref * chronoref,Point * clicked_point,DiaRenderer * interactive_renderer)235 chronoref_select(Chronoref *chronoref, Point *clicked_point,
236 DiaRenderer *interactive_renderer)
237 {
238 element_update_handles(&chronoref->element);
239 }
240
241 static ObjectChange*
chronoref_move_handle(Chronoref * chronoref,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)242 chronoref_move_handle(Chronoref *chronoref, Handle *handle,
243 Point *to, ConnectionPoint *cp,
244 HandleMoveReason reason, ModifierKeys modifiers)
245 {
246 g_assert(chronoref!=NULL);
247 g_assert(handle!=NULL);
248 g_assert(to!=NULL);
249
250 element_move_handle(&chronoref->element, handle->id, to, cp,
251 reason, modifiers);
252 chronoref_update_data(chronoref);
253
254 return NULL;
255 }
256
257 static ObjectChange*
chronoref_move(Chronoref * chronoref,Point * to)258 chronoref_move(Chronoref *chronoref, Point *to)
259 {
260 chronoref->element.corner = *to;
261 chronoref_update_data(chronoref);
262
263 return NULL;
264 }
265
266 static void
chronoref_draw(Chronoref * chronoref,DiaRenderer * renderer)267 chronoref_draw(Chronoref *chronoref, DiaRenderer *renderer)
268 {
269 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
270 Element *elem;
271 Point lr_corner;
272 real t;
273 Point p1,p2,p3;
274
275 assert(renderer != NULL);
276
277 elem = &chronoref->element;
278
279 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
280 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
281
282
283 lr_corner.x = elem->corner.x + elem->width;
284 lr_corner.y = elem->corner.y + elem->height;
285
286 p1.y = p2.y = elem->corner.y;
287
288 renderer_ops->set_font(renderer, chronoref->font, chronoref->font_size);
289 p3.y = p2.y + chronoref->majgrad_height +
290 dia_font_ascent("1",chronoref->font, chronoref->font_size);
291
292 renderer_ops->set_linewidth(renderer, chronoref->light_lwidth);
293 if (chronoref->time_lstep > 0.0) {
294 p2.y = p1.y + chronoref->mingrad_height;
295 for (t = chronoref->firstmaj, p1.x = chronoref->firstmin_x;
296 p1.x <= lr_corner.x;
297 t += chronoref->time_lstep, p1.x += chronoref->mingrad) {
298 p2.x = p1.x;
299
300 renderer_ops->draw_line(renderer,&p1,&p2,&chronoref->color);
301 }
302 }
303
304 renderer_ops->set_linewidth(renderer, chronoref->main_lwidth);
305 if (chronoref->time_step > 0.0) {
306 p2.y = p1.y + chronoref->majgrad_height;
307
308 for (t = chronoref->firstmaj, p1.x = chronoref->firstmaj_x;
309 p1.x <= lr_corner.x;
310 t += chronoref->time_step, p1.x += chronoref->majgrad) {
311 char time[10];
312 p3.x = p2.x = p1.x;
313
314 renderer_ops->draw_line(renderer,&p1,&p2,&chronoref->color);
315 g_snprintf(time,sizeof(time),chronoref->spec,t);
316 renderer_ops->draw_string(renderer,time,&p3,ALIGN_CENTER,
317 &chronoref->font_color);
318 }
319 }
320 p1.x = elem->corner.x;
321 p2.x = lr_corner.x;
322 p1.y = p2.y = elem->corner.y;
323
324 renderer_ops->draw_line(renderer,&p1,&p2,&chronoref->color);
325 }
326
327 static void
chronoref_update_data(Chronoref * chronoref)328 chronoref_update_data(Chronoref *chronoref)
329 {
330 Element *elem = &chronoref->element;
331 DiaObject *obj = &elem->object;
332 real time_span,t;
333 Point p1,p2;
334 Point ur_corner;
335 int shouldbe,i;
336 real labelwidth;
337 char biglabel[10];
338 ElementBBExtras *extra = &elem->extra_spacing;
339
340 chronoref->majgrad_height = elem->height;
341 chronoref->mingrad_height = elem->height / 3.0;
342
343 /* build i = -log_{10}(time_step), then make a %.if format out of it. */
344 t = 1;
345 i = 0;
346
347 while (t > chronoref->time_step) {
348 t /= 10;
349 i++;
350 }
351 g_snprintf(chronoref->spec,sizeof(chronoref->spec),"%%.%df",i);
352 g_snprintf(biglabel,sizeof(biglabel),chronoref->spec,
353 MIN(-ABS(chronoref->start_time),-ABS(chronoref->end_time)));
354
355 labelwidth = dia_font_string_width(biglabel,chronoref->font,
356 chronoref->font_size);
357
358 /* Now, update the drawing helper counters */
359 time_span = chronoref->end_time - chronoref->start_time;
360 if (time_span == 0) {
361 chronoref->end_time = chronoref->start_time + .1;
362 time_span = .1;
363 } else if (time_span < 0) {
364 chronoref->start_time = chronoref->end_time;
365 time_span = -time_span;
366 chronoref->end_time = chronoref->start_time + time_span;
367 }
368
369 chronoref->firstmaj = chronoref->time_step *
370 ceil(chronoref->start_time / chronoref->time_step);
371 if (chronoref->firstmaj < chronoref->start_time)
372 chronoref->firstmaj += chronoref->time_step;
373 chronoref->firstmin = chronoref->time_lstep *
374 ceil(chronoref->start_time / chronoref->time_lstep);
375 if (chronoref->firstmin < chronoref->start_time)
376 chronoref->firstmin += chronoref->time_lstep;
377
378 chronoref->firstmaj_x = elem->corner.x +
379 elem->width*((chronoref->firstmaj-chronoref->start_time)/time_span);
380 chronoref->firstmin_x = elem->corner.x +
381 elem->width*((chronoref->firstmin-chronoref->start_time)/time_span);
382 chronoref->majgrad = (chronoref->time_step * elem->width) / time_span;
383 chronoref->mingrad = (chronoref->time_lstep * elem->width) / time_span;
384
385 extra->border_trans = chronoref->main_lwidth/2;
386 element_update_boundingbox(elem);
387
388 /* fix boundingbox for special extras: */
389 obj->bounding_box.left -= (chronoref->font_size + labelwidth)/2;
390 obj->bounding_box.bottom += chronoref->font_size;
391 obj->bounding_box.right += (chronoref->font_size + labelwidth)/2;
392
393 obj->position = elem->corner;
394
395 element_update_handles(elem);
396
397 /* Update connections: */
398 ur_corner.x = elem->corner.x + elem->width;
399 ur_corner.y = elem->corner.y;
400
401 shouldbe = (int)(ceil((chronoref->end_time-chronoref->firstmin)/
402 chronoref->time_lstep));
403 if (shouldbe == 0) shouldbe++;
404 if (shouldbe < 0) shouldbe = 0;
405 shouldbe++; /* off by one.. */
406
407 connpointline_adjust_count(chronoref->scale,shouldbe,&ur_corner);
408 connpointline_update(chronoref->scale);
409
410 point_copy(&p1,&elem->corner); point_copy(&p2,&ur_corner);
411 p1.x -= chronoref->mingrad;
412 p2.x += chronoref->mingrad;
413 connpointline_putonaline(chronoref->scale,&p1,&p2);
414 }
415
416 static DiaObject *
chronoref_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)417 chronoref_create(Point *startpoint,
418 void *user_data,
419 Handle **handle1,
420 Handle **handle2)
421 {
422 Chronoref *chronoref;
423 Element *elem;
424 DiaObject *obj;
425
426 chronoref = g_new0(Chronoref,1);
427 elem = &(chronoref->element);
428
429 obj = &(chronoref->element.object);
430 obj->type = &chronoref_type;
431 obj->ops = &chronoref_ops;
432
433 chronoref->scale = connpointline_create(obj,0);
434
435 elem->corner = *startpoint;
436 elem->width = 20.0;
437 elem->height = 1.0;
438
439 element_init(elem, 8, 0);
440
441 chronoref->font = dia_font_new_from_style (DIA_FONT_SANS,1.0);
442 chronoref->font_size = 1.0;
443 chronoref->font_color = color_black;
444 chronoref->start_time = 0.0;
445 chronoref->end_time = 20.0;
446 chronoref->time_step = 5.0;
447 chronoref->time_lstep = 1.0;
448 chronoref->color = color_black;
449 chronoref->main_lwidth = .1;
450 chronoref->light_lwidth = .05;
451
452 chronoref_update_data(chronoref);
453
454 *handle1 = NULL;
455 *handle2 = obj->handles[7];
456 return &chronoref->element.object;
457 }
458
459 static void
chronoref_destroy(Chronoref * chronoref)460 chronoref_destroy(Chronoref *chronoref)
461 {
462 dia_font_unref(chronoref->font);
463 connpointline_destroy(chronoref->scale);
464 element_destroy(&chronoref->element);
465 }
466
467 static DiaObject *
chronoref_load(ObjectNode obj_node,int version,const char * filename)468 chronoref_load(ObjectNode obj_node, int version, const char *filename)
469 {
470 return object_load_using_properties(&chronoref_type,
471 obj_node,version,filename);
472 }
473