UML class fix bigtime.
[dia.git] / lib / diagramdata.c
blobbd9f330a720282dca2f33efcb248c4321b0409f1
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"
27 #include "persistence.h"
29 #include "dynamic_obj.h"
31 static const Rectangle invalid_extents = { -1.0,-1.0,-1.0,-1.0 };
32 static void set_parent_layer(gpointer layer, gpointer object);
34 static void diagram_data_class_init (DiagramDataClass *klass);
35 static void diagram_data_init (DiagramData *object);
37 static gpointer parent_class = NULL;
39 GType
40 diagram_data_get_type (void)
42 static GType object_type = 0;
44 if (!object_type)
46 static const GTypeInfo object_info =
48 sizeof (DiagramDataClass),
49 (GBaseInitFunc) NULL,
50 (GBaseFinalizeFunc) NULL,
51 (GClassInitFunc) diagram_data_class_init,
52 NULL, /* class_finalize */
53 NULL, /* class_data */
54 sizeof (DiagramData),
55 0, /* n_preallocs */
56 (GInstanceInitFunc)diagram_data_init /* init */
59 object_type = g_type_register_static (G_TYPE_OBJECT,
60 "DiagramData",
61 &object_info, 0);
64 return object_type;
67 static void
68 diagram_data_init (DiagramData *data)
70 Color* color = persistence_register_color ("new_diagram_bgcolour", &color_white);
71 gboolean compress = persistence_register_boolean ("compress_save", TRUE);
72 Layer *first_layer;
74 data->extents.left = 0.0;
75 data->extents.right = 10.0;
76 data->extents.top = 0.0;
77 data->extents.bottom = 10.0;
79 data->bg_color = *color;
81 get_paper_info (&data->paper, -1, NULL);
83 first_layer = new_layer(g_strdup(_("Background")),data);
85 data->layers = g_ptr_array_new ();
86 g_ptr_array_add (data->layers, first_layer);
87 data->active_layer = first_layer;
89 data->selected_count_private = 0;
90 data->selected = NULL;
92 data->is_compressed = compress; /* Overridden by doc */
96 static void
97 diagram_data_finalize(GObject *object)
99 DiagramData *data = DIA_DIAGRAM_DATA(object);
101 int i;
103 g_free(data->paper.name);
105 for (i=0;i<data->layers->len;i++) {
106 layer_destroy(g_ptr_array_index(data->layers, i));
108 g_ptr_array_free(data->layers, TRUE);
109 data->active_layer = NULL;
111 g_list_free(data->selected);
112 data->selected = NULL; /* for safety */
113 data->selected_count_private = 0;
116 static void
117 diagram_data_class_init (DiagramDataClass *klass)
119 GObjectClass *object_class = G_OBJECT_CLASS (klass);
121 parent_class = g_type_class_peek_parent (klass);
123 object_class->finalize = diagram_data_finalize;
126 Layer *
127 new_layer(gchar *name, DiagramData *parent)
129 Layer *layer;
131 layer = g_new(Layer, 1);
133 layer->name = name;
135 layer->parent_diagram = parent;
136 layer->visible = TRUE;
137 layer->connectable = TRUE;
139 layer->objects = NULL;
141 layer->extents.left = 0.0;
142 layer->extents.right = 10.0;
143 layer->extents.top = 0.0;
144 layer->extents.bottom = 10.0;
146 return layer;
149 void
150 layer_destroy(Layer *layer)
152 g_free(layer->name);
153 destroy_object_list(layer->objects);
154 g_free(layer);
157 void
158 data_raise_layer(DiagramData *data, Layer *layer)
160 int i;
161 int layer_nr = -1;
162 Layer *tmp;
164 for (i=0;i<data->layers->len;i++) {
165 if (g_ptr_array_index(data->layers, i)==layer)
166 layer_nr = i;
169 g_assert(layer_nr>=0);
171 if (layer_nr < data->layers->len-1) {
172 tmp = g_ptr_array_index(data->layers, layer_nr+1);
173 g_ptr_array_index(data->layers, layer_nr+1) =
174 g_ptr_array_index(data->layers, layer_nr);
175 g_ptr_array_index(data->layers, layer_nr) = tmp;
179 void
180 data_lower_layer(DiagramData *data, Layer *layer)
182 int i;
183 int layer_nr = -1;
184 Layer *tmp;
186 for (i=0;i<data->layers->len;i++) {
187 if (g_ptr_array_index(data->layers, i)==layer)
188 layer_nr = i;
191 g_assert(layer_nr>=0);
193 if (layer_nr > 0) {
194 tmp = g_ptr_array_index(data->layers, layer_nr-1);
195 g_ptr_array_index(data->layers, layer_nr-1) =
196 g_ptr_array_index(data->layers, layer_nr);
197 g_ptr_array_index(data->layers, layer_nr) = tmp;
202 void
203 data_add_layer(DiagramData *data, Layer *layer)
205 g_ptr_array_add(data->layers, layer);
206 layer->parent_diagram = data;
207 layer_update_extents(layer);
208 data_update_extents(data);
210 void
211 data_add_layer_at(DiagramData *data, Layer *layer, int pos)
213 int len;
214 int i;
216 g_ptr_array_add(data->layers, layer);
217 len = data->layers->len;
219 if ( (pos>=0) && (pos < len)) {
220 for (i=len-1;i>pos;i--) {
221 g_ptr_array_index(data->layers, i) = g_ptr_array_index(data->layers, i-1);
223 g_ptr_array_index(data->layers, pos) = layer;
226 layer->parent_diagram = data;
227 layer_update_extents(layer);
228 data_update_extents(data);
231 void
232 data_set_active_layer(DiagramData *data, Layer *layer)
234 data->active_layer = layer;
237 void
238 data_delete_layer(DiagramData *data, Layer *layer)
240 if (data->layers->len<=1)
241 return;
243 if (data->active_layer == layer) {
244 data_remove_all_selected(data);
246 layer->parent_diagram = NULL;
247 g_ptr_array_remove(data->layers, layer);
249 if (data->active_layer == layer) {
250 data->active_layer = g_ptr_array_index(data->layers, 0);
254 void
255 data_select(DiagramData *data, DiaObject *obj)
257 if (g_list_find (data->selected, obj))
258 return; /* should this be an error?`*/
259 data->selected = g_list_prepend(data->selected, obj);
260 data->selected_count_private++;
263 void
264 data_unselect(DiagramData *data, DiaObject *obj)
266 if (!g_list_find (data->selected, obj))
267 return; /* should this be an error?`*/
268 data->selected = g_list_remove(data->selected, obj);
269 data->selected_count_private--;
272 /** Clears the list of selected objects.
273 * Does *not* remove these objects from the object list.
275 void
276 data_remove_all_selected(DiagramData *data)
278 g_list_free(data->selected); /* Remove previous selection */
279 data->selected = NULL;
280 data->selected_count_private = 0;
283 static gboolean
284 data_has_visible_layers(DiagramData *data)
286 guint i;
287 for (i = 0; i < data->layers->len; i++) {
288 Layer *layer = g_ptr_array_index(data->layers, i);
289 if (layer->visible) return TRUE;
291 return FALSE;
294 static void
295 data_get_layers_extents_union(DiagramData *data) {
296 guint i;
297 gboolean first = TRUE;
298 Rectangle new_extents;
300 for ( i = 0 ; i<data->layers->len; i++) {
301 Layer *layer = g_ptr_array_index(data->layers, i);
302 if (!layer->visible) continue;
304 layer_update_extents(layer);
306 if (first) {
307 new_extents = layer->extents;
308 first = rectangle_equals(&new_extents,&invalid_extents);
309 } else {
310 if (!rectangle_equals(&layer->extents,&invalid_extents)) {
311 rectangle_union(&new_extents, &layer->extents);
316 data->extents = new_extents;
319 static void
320 data_adapt_scaling_to_extents(DiagramData *data)
322 gdouble pwidth = data->paper.width * data->paper.scaling;
323 gdouble pheight = data->paper.height * data->paper.scaling;
325 gdouble xscale = data->paper.fitwidth * pwidth /
326 (data->extents.right - data->extents.left);
327 gdouble yscale = data->paper.fitheight * pheight /
328 (data->extents.bottom - data->extents.top);
330 data->paper.scaling = MIN(xscale, yscale);
331 data->paper.width = pwidth / data->paper.scaling;
332 data->paper.height = pheight / data->paper.scaling;
335 static gboolean
336 data_compute_extents(DiagramData *data)
338 Rectangle old_extents = data->extents;
340 if (!data_has_visible_layers(data)) {
341 if (data->layers->len > 0) {
342 Layer *layer = g_ptr_array_index(data->layers, 0);
343 layer_update_extents(layer);
345 data->extents = layer->extents;
346 } else {
347 data->extents = invalid_extents;
349 } else {
350 data_get_layers_extents_union(data);
353 if (rectangle_equals(&data->extents,&invalid_extents)) {
354 data->extents.left = 0.0;
355 data->extents.right = 10.0;
356 data->extents.top = 0.0;
357 data->extents.bottom = 10.0;
359 return (!rectangle_equals(&data->extents,&old_extents));
362 gboolean
363 data_update_extents(DiagramData *data)
366 gboolean changed;
368 changed = data_compute_extents(data);
369 if (changed && data->paper.fitto) data_adapt_scaling_to_extents(data);
371 return changed;
374 GList *
375 data_get_sorted_selected(DiagramData *data)
377 GList *list;
378 GList *sorted_list;
379 GList *found;
380 DiaObject *obj;
382 g_assert (g_list_length (data->selected) == data->selected_count_private);
383 if (data->selected_count_private == 0)
384 return NULL;
386 sorted_list = NULL;
387 list = g_list_last(data->active_layer->objects);
388 while (list != NULL) {
389 found = g_list_find(data->selected, list->data);
390 if (found) {
391 obj = (DiaObject *)found->data;
392 sorted_list = g_list_prepend(sorted_list, obj);
394 list = g_list_previous(list);
397 return sorted_list;
400 /** Remove the currently selected objects from the list of objects.
401 * The selected objects are returned in a newly created GList.
403 GList *
404 data_get_sorted_selected_remove(DiagramData *data)
406 GList *list,*tmp;
407 GList *sorted_list;
408 GList *found;
409 DiaObject *obj;
411 g_assert (g_list_length (data->selected) == data->selected_count_private);
412 if (data->selected_count_private == 0)
413 return NULL;
415 sorted_list = NULL;
416 list = g_list_last(data->active_layer->objects);
417 while (list != NULL) {
418 found = g_list_find(data->selected, list->data);
419 if (found) {
420 obj = (DiaObject *)found->data;
421 sorted_list = g_list_prepend(sorted_list, obj);
423 tmp = list;
424 list = g_list_previous(list);
425 data->active_layer->objects =
426 g_list_remove_link(data->active_layer->objects, tmp);
427 } else {
428 list = g_list_previous(list);
432 return sorted_list;
435 void
436 data_render(DiagramData *data, DiaRenderer *renderer, Rectangle *update,
437 ObjectRenderer obj_renderer /* Can be NULL */,
438 gpointer gdata)
440 Layer *layer;
441 int i;
442 int active_layer;
444 if (!renderer->is_interactive) (DIA_RENDERER_GET_CLASS(renderer)->begin_render)(renderer);
446 for (i=0; i<data->layers->len; i++) {
447 layer = (Layer *) g_ptr_array_index(data->layers, i);
448 active_layer = (layer == data->active_layer);
449 if (layer->visible)
450 layer_render(layer, renderer, update, obj_renderer, gdata, active_layer);
453 if (!renderer->is_interactive) (DIA_RENDERER_GET_CLASS(renderer)->end_render)(renderer);
456 static void
457 normal_render(DiaObject *obj, DiaRenderer *renderer,
458 int active_layer,
459 gpointer data)
461 DIA_RENDERER_GET_CLASS(renderer)->draw_object(renderer, obj);
465 int render_bounding_boxes = FALSE;
467 /* If obj_renderer is NULL normal_render is used. */
468 void
469 layer_render(Layer *layer, DiaRenderer *renderer, Rectangle *update,
470 ObjectRenderer obj_renderer,
471 gpointer data,
472 int active_layer)
474 GList *list;
475 DiaObject *obj;
477 if (obj_renderer == NULL)
478 obj_renderer = normal_render;
480 /* Draw all objects: */
481 list = layer->objects;
482 while (list!=NULL) {
483 obj = (DiaObject *) list->data;
485 if (update==NULL || rectangle_intersects(update, &obj->bounding_box)) {
486 if ((render_bounding_boxes) && (renderer->is_interactive)) {
487 Point p1, p2;
488 Color col;
489 p1.x = obj->bounding_box.left;
490 p1.y = obj->bounding_box.top;
491 p2.x = obj->bounding_box.right;
492 p2.y = obj->bounding_box.bottom;
493 col.red = 1.0;
494 col.green = 0.0;
495 col.blue = 1.0;
497 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer,0.01);
498 DIA_RENDERER_GET_CLASS(renderer)->draw_rect(renderer, &p1, &p2, &col);
500 (*obj_renderer)(obj, renderer, active_layer, data);
503 list = g_list_next(list);
507 static void
508 set_parent_layer(gpointer element, gpointer user_data)
510 ((DiaObject*)element)->parent_layer = (Layer*)user_data;
511 /* FIXME: even group members need a parent_layer and what about parent objects ???
512 * Now I know again why I always try to avoid back-pointers )-; --hb */
516 layer_object_index(Layer *layer, DiaObject *obj)
518 return (int)g_list_index(layer->objects, (gpointer) obj);
521 void
522 layer_add_object(Layer *layer, DiaObject *obj)
524 layer->objects = g_list_append(layer->objects, (gpointer) obj);
525 set_parent_layer(obj, layer);
528 void
529 layer_add_object_at(Layer *layer, DiaObject *obj, int pos)
531 layer->objects = g_list_insert(layer->objects, (gpointer) obj, pos);
532 set_parent_layer(obj, layer);
535 void
536 layer_add_objects(Layer *layer, GList *obj_list)
538 layer->objects = g_list_concat(layer->objects, obj_list);
539 g_list_foreach(obj_list, set_parent_layer, layer);
542 void
543 layer_add_objects_first(Layer *layer, GList *obj_list)
545 layer->objects = g_list_concat(obj_list, layer->objects);
546 g_list_foreach(obj_list, set_parent_layer, layer);
549 void
550 layer_remove_object(Layer *layer, DiaObject *obj)
552 layer->objects = g_list_remove(layer->objects, obj);
553 dynobj_list_remove_object(obj);
554 set_parent_layer(obj, NULL);
557 void
558 layer_remove_objects(Layer *layer, GList *obj_list)
560 DiaObject *obj;
561 while (obj_list != NULL) {
562 obj = (DiaObject *) obj_list->data;
564 layer->objects = g_list_remove(layer->objects, obj);
566 obj_list = g_list_next(obj_list);
567 dynobj_list_remove_object(obj);
568 set_parent_layer(obj, NULL);
573 GList *
574 layer_find_objects_intersecting_rectangle(Layer *layer, Rectangle *rect)
576 GList *list;
577 GList *selected_list;
578 DiaObject *obj;
580 selected_list = NULL;
581 list = layer->objects;
582 while (list != NULL) {
583 obj = (DiaObject *)list->data;
585 if (rectangle_intersects(rect, &obj->bounding_box)) {
586 selected_list = g_list_prepend(selected_list, obj);
589 list = g_list_next(list);
592 return selected_list;
595 GList *
596 layer_find_objects_in_rectangle(Layer *layer, Rectangle *rect)
598 GList *list;
599 GList *selected_list;
600 DiaObject *obj;
602 selected_list = NULL;
603 list = layer->objects;
604 while (list != NULL) {
605 obj = (DiaObject *)list->data;
607 if (rectangle_in_rectangle(rect, &obj->bounding_box)) {
608 selected_list = g_list_prepend(selected_list, obj);
611 list = g_list_next(list);
614 return selected_list;
617 /** Find the object closest to the given point in the layer,
618 * no further away than maxdist, and not included in avoid.
619 * Stops it if finds an object that includes the point.
621 DiaObject *
622 layer_find_closest_object_except(Layer *layer, Point *pos,
623 real maxdist, GList *avoid)
625 GList *l;
626 DiaObject *closest;
627 DiaObject *obj;
628 real dist;
629 GList *avoid_tmp;
631 closest = NULL;
633 for (l = layer->objects; l!=NULL; l = g_list_next(l)) {
634 obj = (DiaObject *) l->data;
636 /* Check bounding box here too. Might give speedup. */
637 dist = obj->ops->distance_from(obj, pos);
639 if (maxdist-dist > 0.00000001) {
640 for (avoid_tmp = avoid; avoid_tmp != NULL; avoid_tmp = avoid_tmp->next) {
641 if (avoid_tmp->data == obj) {
642 goto NEXTOBJECT;
645 closest = obj;
647 NEXTOBJECT:
651 return closest;
654 DiaObject *
655 layer_find_closest_object(Layer *layer, Point *pos, real maxdist)
657 return layer_find_closest_object_except(layer, pos, maxdist, NULL);
661 real layer_find_closest_connectionpoint(Layer *layer,
662 ConnectionPoint **closest,
663 Point *pos,
664 DiaObject *notthis)
666 GList *l;
667 DiaObject *obj;
668 ConnectionPoint *cp;
669 real mindist, dist;
670 int i;
672 mindist = 1000000.0; /* Realy big value... */
674 *closest = NULL;
676 for (l = layer->objects; l!=NULL; l = g_list_next(l) ) {
677 obj = (DiaObject *) l->data;
679 if (obj == notthis) continue;
680 for (i=0;i<obj->num_connections;i++) {
681 cp = obj->connections[i];
682 /* Note: Uses manhattan metric for speed... */
683 dist = distance_point_point_manhattan(pos, &cp->pos);
684 if (dist<mindist) {
685 mindist = dist;
686 *closest = cp;
692 return mindist;
696 layer_update_extents(Layer *layer)
698 GList *l;
699 DiaObject *obj;
700 Rectangle new_extents;
702 l = layer->objects;
703 if (l!=NULL) {
704 obj = (DiaObject *) l->data;
705 new_extents = obj->bounding_box;
706 l = g_list_next(l);
708 while(l!=NULL) {
709 obj = (DiaObject *) l->data;
710 rectangle_union(&new_extents, &obj->bounding_box);
711 l = g_list_next(l);
713 } else {
714 new_extents = invalid_extents;
717 if (rectangle_equals(&new_extents,&layer->extents)) return FALSE;
719 layer->extents = new_extents;
720 return TRUE;
723 void
724 layer_replace_object_with_list(Layer *layer, DiaObject *remove_obj,
725 GList *insert_list)
727 GList *list;
729 list = g_list_find(layer->objects, remove_obj);
731 g_assert(list!=NULL);
732 set_parent_layer(remove_obj, NULL);
733 dynobj_list_remove_object(remove_obj);
734 g_list_foreach(insert_list, set_parent_layer, layer);
736 if (list->prev == NULL) {
737 layer->objects = insert_list;
738 } else {
739 list->prev->next = insert_list;
740 insert_list->prev = list->prev;
742 if (list->next != NULL) {
743 GList *last;
744 last = g_list_last(insert_list);
745 last->next = list->next;
746 list->next->prev = last;
748 g_list_free_1(list);
751 static void
752 layer_remove_dynobj(gpointer obj, gpointer userdata)
754 dynobj_list_remove_object((DiaObject*)obj);
757 void layer_set_object_list(Layer *layer, GList *list)
759 g_list_foreach(layer->objects, set_parent_layer, NULL);
760 g_list_foreach(layer->objects, layer_remove_dynobj, NULL);
761 g_list_free(layer->objects);
762 layer->objects = list;
763 g_list_foreach(layer->objects, set_parent_layer, layer);
766 DiagramData *
767 layer_get_parent_diagram(Layer *layer)
769 return layer->parent_diagram;