2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / objects / chronogram / chronoref.c
blobb52746b9f4e22870b4b9c548864c5cfadd30f096
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 "pixmaps/chronoref.xpm"
50 #define DEFAULT_WIDTH 7.0
51 #define DEFAULT_HEIGHT 5.0
53 typedef struct _Chronoref {
54 Element element;
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;
64 DiaFont *font;
65 real font_size;
66 Color font_color;
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;
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);
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 */
113 0, /* version */
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,
172 {NULL}
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,
207 {NULL}
210 static void
211 chronoref_get_props(Chronoref *chronoref, GPtrArray *props)
213 object_get_props_from_offsets(&chronoref->element.object,
214 chronoref_offsets,props);
217 static void
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);
225 static real
226 chronoref_distance_from(Chronoref *chronoref, Point *point)
228 DiaObject *obj = &chronoref->element.object;
229 return distance_rectangle_point(&obj->bounding_box, point);
232 static void
233 chronoref_select(Chronoref *chronoref, Point *clicked_point,
234 DiaRenderer *interactive_renderer)
236 element_update_handles(&chronoref->element);
239 static ObjectChange*
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);
246 g_assert(to!=NULL);
248 element_move_handle(&chronoref->element, handle->id, to, cp,
249 reason, modifiers);
250 chronoref_update_data(chronoref);
252 return NULL;
255 static ObjectChange*
256 chronoref_move(Chronoref *chronoref, Point *to)
258 chronoref->element.corner = *to;
259 chronoref_update_data(chronoref);
261 return NULL;
264 static void
265 chronoref_draw(Chronoref *chronoref, DiaRenderer *renderer)
267 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
268 Element *elem;
269 Point lr_corner;
270 real t;
271 Point p1,p2,p3;
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;
294 p1.x <= lr_corner.x;
295 t += chronoref->time_lstep, p1.x += chronoref->mingrad) {
296 p2.x = p1.x;
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;
307 p1.x <= lr_corner.x;
308 t += chronoref->time_step, p1.x += chronoref->majgrad) {
309 char time[10];
310 p3.x = p2.x = p1.x;
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;
319 p2.x = lr_corner.x;
320 p1.y = p2.y = elem->corner.y;
322 renderer_ops->draw_line(renderer,&p1,&p2,&chronoref->color);
325 static void
326 chronoref_update_data(Chronoref *chronoref)
328 Element *elem = &chronoref->element;
329 DiaObject *obj = &elem->object;
330 real time_span,t;
331 Point p1,p2;
332 Point ur_corner;
333 int shouldbe,i;
334 real labelwidth;
335 char biglabel[10];
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. */
342 t = 1;
343 i = 0;
345 while (t > chronoref->time_step) {
346 t /= 10;
347 i++;
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;
360 time_span = .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);
414 static DiaObject *
415 chronoref_create(Point *startpoint,
416 void *user_data,
417 Handle **handle1,
418 Handle **handle2)
420 Chronoref *chronoref;
421 Element *elem;
422 DiaObject *obj;
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;
434 elem->width = 20.0;
435 elem->height = 1.0;
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);
452 *handle1 = NULL;
453 *handle2 = obj->handles[7];
454 return &chronoref->element.object;
457 static void
458 chronoref_destroy(Chronoref *chronoref)
460 dia_font_unref(chronoref->font);
461 connpointline_destroy(chronoref->scale);
462 element_destroy(&chronoref->element);
465 static DiaObject *
466 chronoref_load(ObjectNode obj_node, int version, const char *filename)
468 return object_load_using_properties(&chronoref_type,
469 obj_node,version,filename);