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.
24 #include "diagramdata.h"
25 #include "diarenderer.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
;
40 diagram_data_get_type (void)
42 static GType object_type
= 0;
46 static const GTypeInfo object_info
=
48 sizeof (DiagramDataClass
),
50 (GBaseFinalizeFunc
) NULL
,
51 (GClassInitFunc
) diagram_data_class_init
,
52 NULL
, /* class_finalize */
53 NULL
, /* class_data */
56 (GInstanceInitFunc
)diagram_data_init
/* init */
59 object_type
= g_type_register_static (G_TYPE_OBJECT
,
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
);
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 */
97 diagram_data_finalize(GObject
*object
)
99 DiagramData
*data
= DIA_DIAGRAM_DATA(object
);
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;
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
;
127 new_layer(gchar
*name
, DiagramData
*parent
)
131 layer
= g_new(Layer
, 1);
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;
150 layer_destroy(Layer
*layer
)
153 destroy_object_list(layer
->objects
);
158 data_raise_layer(DiagramData
*data
, Layer
*layer
)
164 for (i
=0;i
<data
->layers
->len
;i
++) {
165 if (g_ptr_array_index(data
->layers
, i
)==layer
)
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
;
180 data_lower_layer(DiagramData
*data
, Layer
*layer
)
186 for (i
=0;i
<data
->layers
->len
;i
++) {
187 if (g_ptr_array_index(data
->layers
, i
)==layer
)
191 g_assert(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
;
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
);
211 data_add_layer_at(DiagramData
*data
, Layer
*layer
, int pos
)
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
);
232 data_set_active_layer(DiagramData
*data
, Layer
*layer
)
234 data
->active_layer
= layer
;
238 data_delete_layer(DiagramData
*data
, Layer
*layer
)
240 if (data
->layers
->len
<=1)
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);
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
++;
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.
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;
284 data_has_visible_layers(DiagramData
*data
)
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
;
295 data_get_layers_extents_union(DiagramData
*data
) {
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
);
307 new_extents
= layer
->extents
;
308 first
= rectangle_equals(&new_extents
,&invalid_extents
);
310 if (!rectangle_equals(&layer
->extents
,&invalid_extents
)) {
311 rectangle_union(&new_extents
, &layer
->extents
);
316 data
->extents
= new_extents
;
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
;
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
;
347 data
->extents
= invalid_extents
;
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
));
363 data_update_extents(DiagramData
*data
)
368 changed
= data_compute_extents(data
);
369 if (changed
&& data
->paper
.fitto
) data_adapt_scaling_to_extents(data
);
375 data_get_sorted_selected(DiagramData
*data
)
382 g_assert (g_list_length (data
->selected
) == data
->selected_count_private
);
383 if (data
->selected_count_private
== 0)
387 list
= g_list_last(data
->active_layer
->objects
);
388 while (list
!= NULL
) {
389 found
= g_list_find(data
->selected
, list
->data
);
391 obj
= (DiaObject
*)found
->data
;
392 sorted_list
= g_list_prepend(sorted_list
, obj
);
394 list
= g_list_previous(list
);
400 /** Remove the currently selected objects from the list of objects.
401 * The selected objects are returned in a newly created GList.
404 data_get_sorted_selected_remove(DiagramData
*data
)
411 g_assert (g_list_length (data
->selected
) == data
->selected_count_private
);
412 if (data
->selected_count_private
== 0)
416 list
= g_list_last(data
->active_layer
->objects
);
417 while (list
!= NULL
) {
418 found
= g_list_find(data
->selected
, list
->data
);
420 obj
= (DiaObject
*)found
->data
;
421 sorted_list
= g_list_prepend(sorted_list
, obj
);
424 list
= g_list_previous(list
);
425 data
->active_layer
->objects
=
426 g_list_remove_link(data
->active_layer
->objects
, tmp
);
428 list
= g_list_previous(list
);
436 data_render(DiagramData
*data
, DiaRenderer
*renderer
, Rectangle
*update
,
437 ObjectRenderer obj_renderer
/* Can be NULL */,
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
);
450 layer_render(layer
, renderer
, update
, obj_renderer
, gdata
, active_layer
);
453 if (!renderer
->is_interactive
) (DIA_RENDERER_GET_CLASS(renderer
)->end_render
)(renderer
);
457 normal_render(DiaObject
*obj
, DiaRenderer
*renderer
,
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. */
469 layer_render(Layer
*layer
, DiaRenderer
*renderer
, Rectangle
*update
,
470 ObjectRenderer obj_renderer
,
477 if (obj_renderer
== NULL
)
478 obj_renderer
= normal_render
;
480 /* Draw all objects: */
481 list
= layer
->objects
;
483 obj
= (DiaObject
*) list
->data
;
485 if (update
==NULL
|| rectangle_intersects(update
, &obj
->bounding_box
)) {
486 if ((render_bounding_boxes
) && (renderer
->is_interactive
)) {
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
;
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
);
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
);
522 layer_add_object(Layer
*layer
, DiaObject
*obj
)
524 layer
->objects
= g_list_append(layer
->objects
, (gpointer
) obj
);
525 set_parent_layer(obj
, layer
);
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
);
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
);
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
);
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
);
558 layer_remove_objects(Layer
*layer
, GList
*obj_list
)
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
);
574 layer_find_objects_intersecting_rectangle(Layer
*layer
, Rectangle
*rect
)
577 GList
*selected_list
;
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
;
596 layer_find_objects_in_rectangle(Layer
*layer
, Rectangle
*rect
)
599 GList
*selected_list
;
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.
622 layer_find_closest_object_except(Layer
*layer
, Point
*pos
,
623 real maxdist
, GList
*avoid
)
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
) {
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
,
672 mindist
= 1000000.0; /* Realy big value... */
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
);
696 layer_update_extents(Layer
*layer
)
700 Rectangle new_extents
;
704 obj
= (DiaObject
*) l
->data
;
705 new_extents
= obj
->bounding_box
;
709 obj
= (DiaObject
*) l
->data
;
710 rectangle_union(&new_extents
, &obj
->bounding_box
);
714 new_extents
= invalid_extents
;
717 if (rectangle_equals(&new_extents
,&layer
->extents
)) return FALSE
;
719 layer
->extents
= new_extents
;
724 layer_replace_object_with_list(Layer
*layer
, DiaObject
*remove_obj
,
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
;
739 list
->prev
->next
= insert_list
;
740 insert_list
->prev
= list
->prev
;
742 if (list
->next
!= NULL
) {
744 last
= g_list_last(insert_list
);
745 last
->next
= list
->next
;
746 list
->next
->prev
= last
;
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
);
767 layer_get_parent_diagram(Layer
*layer
)
769 return layer
->parent_diagram
;