Fix of bug #350246: Broken snap-to-grid inside objects.
[dia.git] / objects / Misc / analog_clock.c
blob4367e7b664b93404fe504993542807ada8a34bfe
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * Analog clock object
5 * Copyright (C) 2002 Cyrille Chepelov
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <assert.h>
27 #include <math.h>
28 #include <string.h>
29 #include <glib.h>
30 #include <time.h>
32 #include "intl.h"
33 #include "object.h"
34 #include "element.h"
35 #include "connectionpoint.h"
36 #include "diarenderer.h"
37 #include "attributes.h"
38 #include "widgets.h"
39 #include "message.h"
40 #include "color.h"
41 #include "properties.h"
42 #include "dynamic_obj.h"
44 #include "pixmaps/analog_clock.xpm"
46 typedef struct _Chronoline {
47 Element element;
49 ConnectionPoint hours[12];
50 ConnectionPoint hour_tip, min_tip, sec_tip;
51 ConnectionPoint center_cp;
53 Color border_color;
54 real border_line_width;
55 Color inner_color;
56 gboolean show_background;
57 Color arrow_color;
58 real arrow_line_width;
59 Color sec_arrow_color;
60 real sec_arrow_line_width;
61 gboolean show_ticks;
63 Point centre; /* computed */
64 real radius; /* computed */
65 } Analog_Clock;
68 static real analog_clock_distance_from(Analog_Clock *analog_clock,
69 Point *point);
71 static void analog_clock_select(Analog_Clock *analog_clock,
72 Point *clicked_point,
73 DiaRenderer *interactive_renderer);
74 static ObjectChange* analog_clock_move_handle(Analog_Clock *analog_clock,
75 Handle *handle, Point *to,
76 ConnectionPoint *cp, HandleMoveReason reason,
77 ModifierKeys modifiers);
78 static ObjectChange* analog_clock_move(Analog_Clock *analog_clock, Point *to);
79 static void analog_clock_draw(Analog_Clock *analog_clock, DiaRenderer *renderer);
80 static void analog_clock_update_data(Analog_Clock *analog_clock);
81 static DiaObject *analog_clock_create(Point *startpoint,
82 void *user_data,
83 Handle **handle1,
84 Handle **handle2);
85 static void analog_clock_destroy(Analog_Clock *analog_clock);
86 static DiaObject *analog_clock_load(ObjectNode obj_node, int version,
87 const char *filename);
88 static PropDescription *analog_clock_describe_props(
89 Analog_Clock *analog_clock);
90 static void analog_clock_get_props(Analog_Clock *analog_clock,
91 GPtrArray *props);
92 static void analog_clock_set_props(Analog_Clock *analog_clock,
93 GPtrArray *props);
95 static ObjectTypeOps analog_clock_type_ops =
97 (CreateFunc) analog_clock_create,
98 (LoadFunc) analog_clock_load/*using properties*/,
99 (SaveFunc) object_save_using_properties,
100 (GetDefaultsFunc) NULL,
101 (ApplyDefaultsFunc) NULL
104 DiaObjectType analog_clock_type =
106 "Misc - Analog Clock", /* name */
107 0, /* version */
108 (char **) analog_clock_xpm, /* pixmap */
110 &analog_clock_type_ops /* ops */
113 static ObjectOps analog_clock_ops = {
114 (DestroyFunc) analog_clock_destroy,
115 (DrawFunc) analog_clock_draw,
116 (DistanceFunc) analog_clock_distance_from,
117 (SelectFunc) analog_clock_select,
118 (CopyFunc) object_copy_using_properties,
119 (MoveFunc) analog_clock_move,
120 (MoveHandleFunc) analog_clock_move_handle,
121 (GetPropertiesFunc) object_create_props_dialog,
122 (ApplyPropertiesFunc) object_apply_props_from_dialog,
123 (ObjectMenuFunc) NULL,
124 (DescribePropsFunc) analog_clock_describe_props,
125 (GetPropsFunc) analog_clock_get_props,
126 (SetPropsFunc) analog_clock_set_props
129 static PropDescription analog_clock_props[] = {
130 ELEMENT_COMMON_PROPERTIES,
131 PROP_STD_LINE_WIDTH,
132 PROP_STD_LINE_COLOUR,
133 PROP_STD_FILL_COLOUR,
134 PROP_STD_SHOW_BACKGROUND,
136 { "arrow_colour", PROP_TYPE_COLOUR, PROP_FLAG_VISIBLE,
137 N_("Arrow color"), NULL, NULL },
138 { "arrow_line_width", PROP_TYPE_REAL, PROP_FLAG_VISIBLE,
139 N_("Arrow line width"), NULL,NULL },
140 { "sec_arrow_colour", PROP_TYPE_COLOUR, PROP_FLAG_VISIBLE,
141 N_("Seconds arrow color"), NULL, NULL },
142 { "sec_arrow_line_width", PROP_TYPE_REAL, PROP_FLAG_VISIBLE,
143 N_("Seconds arrow line width"), NULL,NULL },
144 { "show_ticks", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
145 N_("Show hours"), NULL, NULL },
147 PROP_STD_NOTEBOOK_END,
148 {NULL}
151 static PropDescription *
152 analog_clock_describe_props(Analog_Clock *analog_clock)
154 if (analog_clock_props[0].quark == 0) {
155 prop_desc_list_calculate_quarks(analog_clock_props);
157 return analog_clock_props;
160 static PropOffset analog_clock_offsets[] = {
161 ELEMENT_COMMON_PROPERTIES_OFFSETS,
162 { "line_width", PROP_TYPE_REAL, offsetof(Analog_Clock, border_line_width) },
163 { "line_colour", PROP_TYPE_COLOUR, offsetof(Analog_Clock, border_color) },
164 { "fill_colour", PROP_TYPE_COLOUR, offsetof(Analog_Clock,inner_color) },
165 { "show_background", PROP_TYPE_BOOL,offsetof(Analog_Clock,show_background) },
166 { "arrow_colour", PROP_TYPE_COLOUR, offsetof(Analog_Clock, arrow_color) },
167 { "arrow_line_width", PROP_TYPE_REAL, offsetof(Analog_Clock,
168 arrow_line_width) },
169 { "sec_arrow_colour", PROP_TYPE_COLOUR,
170 offsetof(Analog_Clock, sec_arrow_color) },
171 { "sec_arrow_line_width", PROP_TYPE_REAL,
172 offsetof(Analog_Clock, sec_arrow_line_width) },
174 { "show_ticks", PROP_TYPE_BOOL,offsetof(Analog_Clock,show_ticks) },
176 {NULL}
179 static void
180 analog_clock_get_props(Analog_Clock *analog_clock, GPtrArray *props)
182 object_get_props_from_offsets(&analog_clock->element.object,
183 analog_clock_offsets,props);
186 static void
187 analog_clock_set_props(Analog_Clock *analog_clock, GPtrArray *props)
189 object_set_props_from_offsets(&analog_clock->element.object,
190 analog_clock_offsets,props);
191 analog_clock_update_data(analog_clock);
194 static real
195 analog_clock_distance_from(Analog_Clock *analog_clock, Point *point)
197 DiaObject *obj = &analog_clock->element.object;
198 return distance_rectangle_point(&obj->bounding_box, point);
201 static void
202 analog_clock_select(Analog_Clock *analog_clock, Point *clicked_point,
203 DiaRenderer *interactive_renderer)
205 element_update_handles(&analog_clock->element);
208 static ObjectChange*
209 analog_clock_move_handle(Analog_Clock *analog_clock, Handle *handle,
210 Point *to, ConnectionPoint *cp,
211 HandleMoveReason reason, ModifierKeys modifiers)
213 g_assert(analog_clock!=NULL);
214 g_assert(handle!=NULL);
215 g_assert(to!=NULL);
217 element_move_handle(&analog_clock->element, handle->id, to, cp,
218 reason, modifiers);
219 analog_clock_update_data(analog_clock);
221 return NULL;
224 static ObjectChange*
225 analog_clock_move(Analog_Clock *analog_clock, Point *to)
227 analog_clock->element.corner = *to;
228 analog_clock_update_data(analog_clock);
230 return NULL;
233 static void make_angle(const Point *centre, real degrees, real radius,
234 Point *pt)
236 real radians = ((90 - degrees) * M_PI) / 180.0;
237 pt->x = centre->x + radius * cos( radians );
238 pt->y = centre->y - radius * sin( radians );
241 static void make_hours(const Point *centre, unsigned hours, real radius,
242 Point *pt)
244 while (hours > 11) hours -= 12;
246 make_angle(centre,((real)hours) * 360.0 / 12.0,radius,pt);
249 static void make_minutes(const Point *centre, unsigned minutes,
250 real radius, Point *pt)
252 make_angle(centre,((real)minutes) * 360.0 / 60.0,radius,pt);
255 static void
256 analog_clock_update_arrow_tips(Analog_Clock *analog_clock)
258 time_t now;
259 struct tm *local;
261 now = time(NULL);
262 local = localtime(&now);
263 analog_clock->hour_tip.directions = DIR_ALL;
264 analog_clock->min_tip.directions = DIR_ALL;
265 analog_clock->sec_tip.directions = DIR_ALL;
266 if (local) {
267 make_hours(&analog_clock->centre,local->tm_hour,
268 0.50 * analog_clock->radius, &analog_clock->hour_tip.pos);
269 make_minutes(&analog_clock->centre,local->tm_min,
270 0.80 * analog_clock->radius, &analog_clock->min_tip.pos);
271 make_minutes(&analog_clock->centre,local->tm_sec,
272 0.85 * analog_clock->radius, &analog_clock->sec_tip.pos);
273 } else {
274 /* Highly unlikely */
275 point_copy(&analog_clock->hour_tip.pos,&analog_clock->centre);
276 point_copy(&analog_clock->min_tip.pos,&analog_clock->centre);
277 point_copy(&analog_clock->sec_tip.pos,&analog_clock->centre);
282 static void
283 analog_clock_update_data(Analog_Clock *analog_clock)
285 Element *elem = &analog_clock->element;
286 DiaObject *obj = &elem->object;
287 int i;
288 ElementBBExtras *extra = &elem->extra_spacing;
290 extra->border_trans = analog_clock->border_line_width / 2;
291 element_update_boundingbox(elem);
293 obj->position = elem->corner;
295 element_update_handles(elem);
297 analog_clock->centre.x = obj->position.x + elem->width/2;
298 analog_clock->centre.y = obj->position.y + elem->height/2;
300 analog_clock->radius = MIN(elem->width/2,elem->height/2);
302 /* Update connections: */
303 for (i = 0; i < 12; ++i)
305 make_hours(&analog_clock->centre, i+1, analog_clock->radius,
306 &analog_clock->hours[i].pos);
307 analog_clock->hours[i].directions = DIR_ALL;
309 analog_clock->center_cp.pos.x = elem->corner.x + elem->width/2;
310 analog_clock->center_cp.pos.y = elem->corner.y + elem->height/2;
312 analog_clock_update_arrow_tips(analog_clock);
315 static void
316 analog_clock_draw(Analog_Clock *analog_clock, DiaRenderer *renderer)
318 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
319 Element *elem;
321 g_assert(analog_clock != NULL);
322 g_assert(renderer != NULL);
324 elem = &analog_clock->element;
326 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
327 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
328 renderer_ops->set_linewidth(renderer, analog_clock->border_line_width);
330 if (analog_clock->show_background)
331 renderer_ops->fill_ellipse(renderer,&analog_clock->centre,
332 2*analog_clock->radius,2*analog_clock->radius,
333 &analog_clock->inner_color);
334 renderer_ops->draw_ellipse(renderer,&analog_clock->centre,
335 2*analog_clock->radius,2*analog_clock->radius,
336 &analog_clock->border_color);
337 if (analog_clock->show_ticks)
339 Point out, in;
340 unsigned i;
342 for (i = 0; i < 12; ++i) {
343 real ticklen;
344 switch(i) {
345 case 0:
346 ticklen = 3.5 * analog_clock->border_line_width; break;
347 case 3: case 6: case 9:
348 ticklen = 3 * analog_clock->border_line_width; break;
349 default:
350 ticklen = 2 * analog_clock->border_line_width; break;
352 make_hours(&analog_clock->centre, i,
353 analog_clock->radius, &out);
354 make_hours(&analog_clock->centre, i,
355 analog_clock->radius-ticklen, &in);
356 renderer_ops->draw_line(renderer,&out,&in,&analog_clock->border_color);
360 analog_clock_update_arrow_tips(analog_clock);
362 renderer_ops->set_linewidth(renderer, analog_clock->arrow_line_width);
363 renderer_ops->draw_line(renderer,
364 &analog_clock->hour_tip.pos, &analog_clock->centre,
365 &analog_clock->arrow_color);
366 renderer_ops->draw_line(renderer,
367 &analog_clock->min_tip.pos, &analog_clock->centre,
368 &analog_clock->arrow_color);
370 renderer_ops->set_linewidth(renderer, analog_clock->sec_arrow_line_width);
371 renderer_ops->draw_line(renderer,
372 &analog_clock->sec_tip.pos, &analog_clock->centre,
373 &analog_clock->sec_arrow_color);
374 renderer_ops->fill_ellipse(renderer,&analog_clock->centre,
375 analog_clock->arrow_line_width*2.25,
376 analog_clock->arrow_line_width*2.25,
377 &analog_clock->sec_arrow_color);
382 static DiaObject *
383 analog_clock_create(Point *startpoint,
384 void *user_data,
385 Handle **handle1,
386 Handle **handle2)
388 Analog_Clock *analog_clock;
389 Element *elem;
390 DiaObject *obj;
391 unsigned i;
393 analog_clock = g_new0(Analog_Clock,1);
394 elem = &(analog_clock->element);
396 obj = &(analog_clock->element.object);
397 obj->type = &analog_clock_type;
398 obj->ops = &analog_clock_ops;
400 elem->corner = *startpoint;
401 elem->width = 4.0;
402 elem->height = 4.0;
404 element_init(elem, 8, 16);
406 analog_clock->border_color = attributes_get_foreground();
407 analog_clock->border_line_width = attributes_get_default_linewidth();
408 analog_clock->inner_color = attributes_get_background();
409 analog_clock->show_background = TRUE;
410 analog_clock->arrow_color.red = 0.0;
411 analog_clock->arrow_color.green = 0.0;
412 analog_clock->arrow_color.blue = 0.5;
413 analog_clock->arrow_line_width = attributes_get_default_linewidth();
414 analog_clock->sec_arrow_color.red = 1.0;
415 analog_clock->sec_arrow_color.green = 0.0;
416 analog_clock->sec_arrow_color.blue = 0.0;
417 analog_clock->sec_arrow_line_width = attributes_get_default_linewidth()/3;
418 analog_clock->show_ticks = TRUE;
420 for (i = 0; i < 12; ++i)
422 obj->connections[i] = &analog_clock->hours[i];
423 analog_clock->hours[i].object = obj;
424 analog_clock->hours[i].connected = NULL;
426 obj->connections[12] = &analog_clock->hour_tip;
427 analog_clock->hour_tip.object = obj;
428 analog_clock->hour_tip.connected = NULL;
429 obj->connections[13] = &analog_clock->min_tip;
430 analog_clock->min_tip.object = obj;
431 analog_clock->min_tip.connected = NULL;
432 obj->connections[14] = &analog_clock->sec_tip;
433 analog_clock->sec_tip.object = obj;
434 analog_clock->sec_tip.connected = NULL;
435 obj->connections[15] = &analog_clock->center_cp;
436 analog_clock->center_cp.object = obj;
437 analog_clock->center_cp.connected = NULL;
438 analog_clock->center_cp.flags = CP_FLAGS_MAIN;
440 analog_clock->hours[0].directions = DIR_NORTH;
441 analog_clock->hours[1].directions = DIR_NORTH|DIR_EAST;
442 analog_clock->hours[2].directions = DIR_NORTH|DIR_EAST;
443 analog_clock->hours[3].directions = DIR_EAST;
444 analog_clock->hours[4].directions = DIR_EAST|DIR_SOUTH;
445 analog_clock->hours[5].directions = DIR_EAST|DIR_SOUTH;
446 analog_clock->hours[6].directions = DIR_SOUTH;
447 analog_clock->hours[7].directions = DIR_SOUTH|DIR_WEST;
448 analog_clock->hours[8].directions = DIR_SOUTH|DIR_WEST;
449 analog_clock->hours[9].directions = DIR_WEST;
450 analog_clock->hours[10].directions = DIR_WEST|DIR_NORTH;
451 analog_clock->hours[11].directions = DIR_WEST|DIR_NORTH;
452 analog_clock->center_cp.directions = DIR_ALL;
454 analog_clock_update_data(analog_clock);
456 *handle1 = NULL;
457 *handle2 = obj->handles[7];
459 /* We are an animated object -- special case ! */
460 dynobj_list_add_object(&analog_clock->element.object,1000);
462 return &analog_clock->element.object;
465 static void
466 analog_clock_destroy(Analog_Clock *analog_clock)
468 /* We are an animated object -- special case ! */
469 dynobj_list_remove_object(&analog_clock->element.object);
470 element_destroy(&analog_clock->element);
473 static DiaObject *
474 analog_clock_load(ObjectNode obj_node, int version, const char *filename)
476 return object_load_using_properties(&analog_clock_type,
477 obj_node,version,filename);