don't include dlfcn.h on win32
[dia.git] / lib / diagramdata.c
blob578cd500811321fa2a85edcc541c099d98078723
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include "intl.h"
24 #include "diagramdata.h"
25 #include "diarenderer.h"
26 #include "paper.h"
28 static const Rectangle invalid_extents = { -1.0,-1.0,-1.0,-1.0 };
29 static void set_parent_layer(gpointer layer, gpointer object);
31 DiagramData *
32 new_diagram_data (NewDiagramData *prefs)
34 DiagramData *data;
35 Layer *first_layer;
37 data = g_new (DiagramData, 1);
39 data->extents.left = 0.0;
40 data->extents.right = 10.0;
41 data->extents.top = 0.0;
42 data->extents.bottom = 10.0;
44 data->bg_color = color_white;
46 get_paper_info (&data->paper, -1, prefs);
48 data->grid.width_x = 1.0;
49 data->grid.width_y = 1.0;
50 data->grid.visible_x = 1;
51 data->grid.visible_y = 1;
53 data->guides.nhguides = 0;
54 data->guides.hguides = NULL;
55 data->guides.nvguides = 0;
56 data->guides.vguides = NULL;
58 first_layer = new_layer(g_strdup(_("Background")),data);
60 data->layers = g_ptr_array_new ();
61 g_ptr_array_add (data->layers, first_layer);
62 data->active_layer = first_layer;
64 data->selected_count = 0;
65 data->selected = NULL;
67 return data;
70 void
71 diagram_data_destroy(DiagramData *data)
73 int i;
75 g_free(data->paper.name);
77 for (i=0;i<data->layers->len;i++) {
78 layer_destroy(g_ptr_array_index(data->layers, i));
80 g_ptr_array_free(data->layers, TRUE);
81 data->active_layer = NULL;
83 g_list_free(data->selected);
84 data->selected = NULL; /* for safety */
85 data->selected_count = 0;
86 g_free(data);
89 Layer *
90 new_layer(gchar *name, DiagramData *parent)
92 Layer *layer;
94 layer = g_new(Layer, 1);
96 layer->name = name;
98 layer->parent_diagram = parent;
99 layer->visible = TRUE;
101 layer->objects = NULL;
103 layer->extents.left = 0.0;
104 layer->extents.right = 10.0;
105 layer->extents.top = 0.0;
106 layer->extents.bottom = 10.0;
108 return layer;
111 void
112 layer_destroy(Layer *layer)
114 g_free(layer->name);
115 destroy_object_list(layer->objects);
116 g_free(layer);
119 void
120 data_raise_layer(DiagramData *data, Layer *layer)
122 int i;
123 int layer_nr = -1;
124 Layer *tmp;
126 for (i=0;i<data->layers->len;i++) {
127 if (g_ptr_array_index(data->layers, i)==layer)
128 layer_nr = i;
131 g_assert(layer_nr>=0);
133 if (layer_nr < data->layers->len-1) {
134 tmp = g_ptr_array_index(data->layers, layer_nr+1);
135 g_ptr_array_index(data->layers, layer_nr+1) =
136 g_ptr_array_index(data->layers, layer_nr);
137 g_ptr_array_index(data->layers, layer_nr) = tmp;
141 void
142 data_lower_layer(DiagramData *data, Layer *layer)
144 int i;
145 int layer_nr = -1;
146 Layer *tmp;
148 for (i=0;i<data->layers->len;i++) {
149 if (g_ptr_array_index(data->layers, i)==layer)
150 layer_nr = i;
153 g_assert(layer_nr>=0);
155 if (layer_nr > 0) {
156 tmp = g_ptr_array_index(data->layers, layer_nr-1);
157 g_ptr_array_index(data->layers, layer_nr-1) =
158 g_ptr_array_index(data->layers, layer_nr);
159 g_ptr_array_index(data->layers, layer_nr) = tmp;
164 void
165 data_add_layer(DiagramData *data, Layer *layer)
167 g_ptr_array_add(data->layers, layer);
168 layer->parent_diagram = data;
169 layer_update_extents(layer);
170 data_update_extents(data);
172 void
173 data_add_layer_at(DiagramData *data, Layer *layer, int pos)
175 int len;
176 int i;
178 g_ptr_array_add(data->layers, layer);
179 len = data->layers->len;
181 if ( (pos>=0) && (pos < len)) {
182 for (i=len-1;i>pos;i--) {
183 g_ptr_array_index(data->layers, i) = g_ptr_array_index(data->layers, i-1);
185 g_ptr_array_index(data->layers, pos) = layer;
188 layer->parent_diagram = data;
189 layer_update_extents(layer);
190 data_update_extents(data);
193 void
194 data_set_active_layer(DiagramData *data, Layer *layer)
196 data->active_layer = layer;
199 void
200 data_delete_layer(DiagramData *data, Layer *layer)
202 if (data->layers->len<=1)
203 return;
205 if (data->active_layer == layer) {
206 data_remove_all_selected(data);
208 layer->parent_diagram = NULL;
209 g_ptr_array_remove(data->layers, layer);
211 if (data->active_layer == layer) {
212 data->active_layer = g_ptr_array_index(data->layers, 0);
216 void
217 data_select(DiagramData *data, Object *obj)
219 data->selected = g_list_prepend(data->selected, obj);
220 data->selected_count++;
223 void
224 data_unselect(DiagramData *data, Object *obj)
226 data->selected = g_list_remove(data->selected, obj);
227 data->selected_count--;
230 void
231 data_remove_all_selected(DiagramData *data)
233 g_list_free(data->selected); /* Remove previous selection */
234 data->selected_count = 0;
235 data->selected = NULL;
238 static gboolean
239 data_has_visible_layers(DiagramData *data)
241 guint i;
242 for (i = 0; i < data->layers->len; i++) {
243 Layer *layer = g_ptr_array_index(data->layers, i);
244 if (layer->visible) return TRUE;
246 return FALSE;
249 static void
250 data_get_layers_extents_union(DiagramData *data) {
251 guint i;
252 gboolean first = TRUE;
253 Rectangle new_extents;
255 for ( i = 0 ; i<data->layers->len; i++) {
256 Layer *layer = g_ptr_array_index(data->layers, i);
257 if (!layer->visible) continue;
259 layer_update_extents(layer);
261 if (first) {
262 new_extents = layer->extents;
263 first = rectangle_equals(&new_extents,&invalid_extents);
264 } else {
265 if (!rectangle_equals(&layer->extents,&invalid_extents)) {
266 rectangle_union(&new_extents, &layer->extents);
271 data->extents = new_extents;
274 static void
275 data_adapt_scaling_to_extents(DiagramData *data)
277 gdouble pwidth = data->paper.width * data->paper.scaling;
278 gdouble pheight = data->paper.height * data->paper.scaling;
280 gdouble xscale = data->paper.fitwidth * pwidth /
281 (data->extents.right - data->extents.left);
282 gdouble yscale = data->paper.fitheight * pheight /
283 (data->extents.bottom - data->extents.top);
285 data->paper.scaling = MIN(xscale, yscale);
286 data->paper.width = pwidth / data->paper.scaling;
287 data->paper.height = pheight / data->paper.scaling;
290 static gboolean
291 data_compute_extents(DiagramData *data)
293 Rectangle old_extents = data->extents;
295 if (!data_has_visible_layers(data)) {
296 if (data->layers->len > 0) {
297 Layer *layer = g_ptr_array_index(data->layers, 0);
298 layer_update_extents(layer);
300 data->extents = layer->extents;
301 } else {
302 data->extents = invalid_extents;
304 } else {
305 data_get_layers_extents_union(data);
308 if (rectangle_equals(&data->extents,&invalid_extents)) {
309 data->extents.left = 0.0;
310 data->extents.right = 10.0;
311 data->extents.top = 0.0;
312 data->extents.bottom = 10.0;
314 return (!rectangle_equals(&data->extents,&old_extents));
317 gboolean
318 data_update_extents(DiagramData *data)
321 gboolean changed;
323 changed = data_compute_extents(data);
324 if (changed && data->paper.fitto) data_adapt_scaling_to_extents(data);
326 return changed;
329 GList *
330 data_get_sorted_selected(DiagramData *data)
332 GList *list;
333 GList *sorted_list;
334 GList *found;
335 Object *obj;
337 if (data->selected_count == 0)
338 return NULL;
340 sorted_list = NULL;
341 list = g_list_last(data->active_layer->objects);
342 while (list != NULL) {
343 found = g_list_find(data->selected, list->data);
344 if (found) {
345 obj = (Object *)found->data;
346 sorted_list = g_list_prepend(sorted_list, obj);
348 list = g_list_previous(list);
351 return sorted_list;
354 GList *
355 data_get_sorted_selected_remove(DiagramData *data)
357 GList *list,*tmp;
358 GList *sorted_list;
359 GList *found;
360 Object *obj;
362 if (data->selected_count == 0)
363 return NULL;
365 sorted_list = NULL;
366 list = g_list_last(data->active_layer->objects);
367 while (list != NULL) {
368 found = g_list_find(data->selected, list->data);
369 if (found) {
370 obj = (Object *)found->data;
371 sorted_list = g_list_prepend(sorted_list, obj);
373 tmp = list;
374 list = g_list_previous(list);
375 data->active_layer->objects =
376 g_list_remove_link(data->active_layer->objects, tmp);
377 } else {
378 list = g_list_previous(list);
382 return sorted_list;
385 void
386 data_render(DiagramData *data, DiaRenderer *renderer, Rectangle *update,
387 ObjectRenderer obj_renderer /* Can be NULL */,
388 gpointer gdata)
390 Layer *layer;
391 int i;
392 int active_layer;
394 if (!renderer->is_interactive) (DIA_RENDERER_GET_CLASS(renderer)->begin_render)(renderer);
396 for (i=0; i<data->layers->len; i++) {
397 layer = (Layer *) g_ptr_array_index(data->layers, i);
398 active_layer = (layer == data->active_layer);
399 if (layer->visible)
400 layer_render(layer, renderer, update, obj_renderer, gdata, active_layer);
403 if (!renderer->is_interactive) (DIA_RENDERER_GET_CLASS(renderer)->end_render)(renderer);
406 static void
407 normal_render(Object *obj, DiaRenderer *renderer,
408 int active_layer,
409 gpointer data)
411 DIA_RENDERER_GET_CLASS(renderer)->draw_object(renderer, obj);
415 int render_bounding_boxes = FALSE;
417 /* If obj_renderer is NULL normal_render is used. */
418 void
419 layer_render(Layer *layer, DiaRenderer *renderer, Rectangle *update,
420 ObjectRenderer obj_renderer,
421 gpointer data,
422 int active_layer)
424 GList *list;
425 Object *obj;
427 if (obj_renderer == NULL)
428 obj_renderer = normal_render;
430 /* Draw all objects: */
431 list = layer->objects;
432 while (list!=NULL) {
433 obj = (Object *) list->data;
435 if (update==NULL || rectangle_intersects(update, &obj->bounding_box)) {
436 if ((render_bounding_boxes) && (renderer->is_interactive)) {
437 Point p1, p2;
438 Color col;
439 p1.x = obj->bounding_box.left;
440 p1.y = obj->bounding_box.top;
441 p2.x = obj->bounding_box.right;
442 p2.y = obj->bounding_box.bottom;
443 col.red = 1.0;
444 col.green = 0.0;
445 col.blue = 1.0;
447 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer,0.01);
448 DIA_RENDERER_GET_CLASS(renderer)->draw_rect(renderer, &p1, &p2, &col);
450 (*obj_renderer)(obj, renderer, active_layer, data);
453 list = g_list_next(list);
457 static void
458 set_parent_layer(gpointer element, gpointer user_data) {
459 ((Object*)element)->parent_layer = (Layer*)user_data;
463 layer_object_index(Layer *layer, Object *obj)
465 return (int)g_list_index(layer->objects, (gpointer) obj);
468 void
469 layer_add_object(Layer *layer, Object *obj)
471 layer->objects = g_list_append(layer->objects, (gpointer) obj);
472 set_parent_layer(obj, layer);
475 void
476 layer_add_object_at(Layer *layer, Object *obj, int pos)
478 layer->objects = g_list_insert(layer->objects, (gpointer) obj, pos);
479 set_parent_layer(obj, layer);
482 void
483 layer_add_objects(Layer *layer, GList *obj_list)
485 layer->objects = g_list_concat(layer->objects, obj_list);
486 g_list_foreach(obj_list, set_parent_layer, layer);
489 void
490 layer_add_objects_first(Layer *layer, GList *obj_list)
492 layer->objects = g_list_concat(obj_list, layer->objects);
493 g_list_foreach(obj_list, set_parent_layer, layer);
496 void
497 layer_remove_object(Layer *layer, Object *obj)
499 layer->objects = g_list_remove(layer->objects, obj);
500 set_parent_layer(obj, NULL);
503 void
504 layer_remove_objects(Layer *layer, GList *obj_list)
506 Object *obj;
507 while (obj_list != NULL) {
508 obj = (Object *) obj_list->data;
510 layer->objects = g_list_remove(layer->objects, obj);
512 obj_list = g_list_next(obj_list);
513 set_parent_layer(obj, NULL);
518 GList *
519 layer_find_objects_intersecting_rectangle(Layer *layer, Rectangle *rect)
521 GList *list;
522 GList *selected_list;
523 Object *obj;
525 selected_list = NULL;
526 list = layer->objects;
527 while (list != NULL) {
528 obj = (Object *)list->data;
530 if (rectangle_intersects(rect, &obj->bounding_box)) {
531 selected_list = g_list_prepend(selected_list, obj);
534 list = g_list_next(list);
537 return selected_list;
540 GList *
541 layer_find_objects_in_rectangle(Layer *layer, Rectangle *rect)
543 GList *list;
544 GList *selected_list;
545 Object *obj;
547 selected_list = NULL;
548 list = layer->objects;
549 while (list != NULL) {
550 obj = (Object *)list->data;
552 if (rectangle_in_rectangle(rect, &obj->bounding_box)) {
553 selected_list = g_list_prepend(selected_list, obj);
556 list = g_list_next(list);
559 return selected_list;
562 Object *
563 layer_find_closest_object(Layer *layer, Point *pos, real maxdist)
565 GList *l;
566 Object *closest;
567 Object *obj;
568 real dist;
570 closest = NULL;
572 l = layer->objects;
573 while(l!=NULL) {
574 obj = (Object *) l->data;
576 /* Check bounding box here too. Might give speedup. */
577 dist = obj->ops->distance_from(obj, pos);
579 if (dist<=maxdist) {
580 closest = obj;
583 l = g_list_next(l);
586 return closest;
589 real layer_find_closest_connectionpoint(Layer *layer,
590 ConnectionPoint **closest,
591 Point *pos,
592 Object *notthis)
594 GList *l;
595 Object *obj;
596 ConnectionPoint *cp;
597 real mindist, dist;
598 int i;
600 mindist = 1000000.0; /* Realy big value... */
602 *closest = NULL;
604 for (l = layer->objects; l!=NULL; l = g_list_next(l) ) {
605 obj = (Object *) l->data;
607 if (obj == notthis) continue;
608 for (i=0;i<obj->num_connections;i++) {
609 cp = obj->connections[i];
610 /* Note: Uses manhattan metric for speed... */
611 dist = distance_point_point_manhattan(pos, &cp->pos);
612 if (dist<mindist) {
613 mindist = dist;
614 *closest = cp;
620 return mindist;
623 int layer_update_extents(Layer *layer)
625 GList *l;
626 Object *obj;
627 Rectangle new_extents;
629 l = layer->objects;
630 if (l!=NULL) {
631 obj = (Object *) l->data;
632 new_extents = obj->bounding_box;
633 l = g_list_next(l);
635 while(l!=NULL) {
636 obj = (Object *) l->data;
637 rectangle_union(&new_extents, &obj->bounding_box);
638 l = g_list_next(l);
640 } else {
641 new_extents = invalid_extents;
644 if (rectangle_equals(&new_extents,&layer->extents)) return FALSE;
646 layer->extents = new_extents;
647 return TRUE;
650 void
651 layer_replace_object_with_list(Layer *layer, Object *remove_obj,
652 GList *insert_list)
654 GList *list;
656 list = g_list_find(layer->objects, remove_obj);
658 g_assert(list!=NULL);
659 set_parent_layer(remove_obj, NULL);
660 g_list_foreach(insert_list, set_parent_layer, layer);
662 if (list->prev == NULL) {
663 layer->objects = insert_list;
664 } else {
665 list->prev->next = insert_list;
666 insert_list->prev = list->prev;
668 if (list->next != NULL) {
669 GList *last;
670 last = g_list_last(insert_list);
671 last->next = list->next;
672 list->next->prev = last;
674 g_list_free_1(list);
677 void layer_set_object_list(Layer *layer, GList *list)
679 g_list_foreach(layer->objects, set_parent_layer, NULL);
680 g_list_free(layer->objects);
681 layer->objects = list;
682 g_list_foreach(layer->objects, set_parent_layer, layer);
685 DiagramData *
686 layer_get_parent_diagram(Layer *layer)
688 return layer->parent_diagram;