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"
28 static const Rectangle invalid_extents
= { -1.0,-1.0,-1.0,-1.0 };
29 static void set_parent_layer(gpointer layer
, gpointer object
);
32 new_diagram_data (NewDiagramData
*prefs
)
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
;
71 diagram_data_destroy(DiagramData
*data
)
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;
90 new_layer(gchar
*name
, DiagramData
*parent
)
94 layer
= g_new(Layer
, 1);
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;
112 layer_destroy(Layer
*layer
)
115 destroy_object_list(layer
->objects
);
120 data_raise_layer(DiagramData
*data
, Layer
*layer
)
126 for (i
=0;i
<data
->layers
->len
;i
++) {
127 if (g_ptr_array_index(data
->layers
, i
)==layer
)
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
;
142 data_lower_layer(DiagramData
*data
, Layer
*layer
)
148 for (i
=0;i
<data
->layers
->len
;i
++) {
149 if (g_ptr_array_index(data
->layers
, i
)==layer
)
153 g_assert(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
;
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
);
173 data_add_layer_at(DiagramData
*data
, Layer
*layer
, int pos
)
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
);
194 data_set_active_layer(DiagramData
*data
, Layer
*layer
)
196 data
->active_layer
= layer
;
200 data_delete_layer(DiagramData
*data
, Layer
*layer
)
202 if (data
->layers
->len
<=1)
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);
217 data_select(DiagramData
*data
, Object
*obj
)
219 data
->selected
= g_list_prepend(data
->selected
, obj
);
220 data
->selected_count
++;
224 data_unselect(DiagramData
*data
, Object
*obj
)
226 data
->selected
= g_list_remove(data
->selected
, obj
);
227 data
->selected_count
--;
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
;
239 data_has_visible_layers(DiagramData
*data
)
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
;
250 data_get_layers_extents_union(DiagramData
*data
) {
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
);
262 new_extents
= layer
->extents
;
263 first
= rectangle_equals(&new_extents
,&invalid_extents
);
265 if (!rectangle_equals(&layer
->extents
,&invalid_extents
)) {
266 rectangle_union(&new_extents
, &layer
->extents
);
271 data
->extents
= new_extents
;
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
;
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
;
302 data
->extents
= invalid_extents
;
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
));
318 data_update_extents(DiagramData
*data
)
323 changed
= data_compute_extents(data
);
324 if (changed
&& data
->paper
.fitto
) data_adapt_scaling_to_extents(data
);
330 data_get_sorted_selected(DiagramData
*data
)
337 if (data
->selected_count
== 0)
341 list
= g_list_last(data
->active_layer
->objects
);
342 while (list
!= NULL
) {
343 found
= g_list_find(data
->selected
, list
->data
);
345 obj
= (Object
*)found
->data
;
346 sorted_list
= g_list_prepend(sorted_list
, obj
);
348 list
= g_list_previous(list
);
355 data_get_sorted_selected_remove(DiagramData
*data
)
362 if (data
->selected_count
== 0)
366 list
= g_list_last(data
->active_layer
->objects
);
367 while (list
!= NULL
) {
368 found
= g_list_find(data
->selected
, list
->data
);
370 obj
= (Object
*)found
->data
;
371 sorted_list
= g_list_prepend(sorted_list
, obj
);
374 list
= g_list_previous(list
);
375 data
->active_layer
->objects
=
376 g_list_remove_link(data
->active_layer
->objects
, tmp
);
378 list
= g_list_previous(list
);
386 data_render(DiagramData
*data
, DiaRenderer
*renderer
, Rectangle
*update
,
387 ObjectRenderer obj_renderer
/* Can be NULL */,
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
);
400 layer_render(layer
, renderer
, update
, obj_renderer
, gdata
, active_layer
);
403 if (!renderer
->is_interactive
) (DIA_RENDERER_GET_CLASS(renderer
)->end_render
)(renderer
);
407 normal_render(Object
*obj
, DiaRenderer
*renderer
,
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. */
419 layer_render(Layer
*layer
, DiaRenderer
*renderer
, Rectangle
*update
,
420 ObjectRenderer obj_renderer
,
427 if (obj_renderer
== NULL
)
428 obj_renderer
= normal_render
;
430 /* Draw all objects: */
431 list
= layer
->objects
;
433 obj
= (Object
*) list
->data
;
435 if (update
==NULL
|| rectangle_intersects(update
, &obj
->bounding_box
)) {
436 if ((render_bounding_boxes
) && (renderer
->is_interactive
)) {
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
;
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
);
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
);
469 layer_add_object(Layer
*layer
, Object
*obj
)
471 layer
->objects
= g_list_append(layer
->objects
, (gpointer
) obj
);
472 set_parent_layer(obj
, layer
);
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
);
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
);
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
);
497 layer_remove_object(Layer
*layer
, Object
*obj
)
499 layer
->objects
= g_list_remove(layer
->objects
, obj
);
500 set_parent_layer(obj
, NULL
);
504 layer_remove_objects(Layer
*layer
, GList
*obj_list
)
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
);
519 layer_find_objects_intersecting_rectangle(Layer
*layer
, Rectangle
*rect
)
522 GList
*selected_list
;
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
;
541 layer_find_objects_in_rectangle(Layer
*layer
, Rectangle
*rect
)
544 GList
*selected_list
;
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
;
563 layer_find_closest_object(Layer
*layer
, Point
*pos
, real maxdist
)
574 obj
= (Object
*) l
->data
;
576 /* Check bounding box here too. Might give speedup. */
577 dist
= obj
->ops
->distance_from(obj
, pos
);
589 real
layer_find_closest_connectionpoint(Layer
*layer
,
590 ConnectionPoint
**closest
,
600 mindist
= 1000000.0; /* Realy big value... */
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
);
623 int layer_update_extents(Layer
*layer
)
627 Rectangle new_extents
;
631 obj
= (Object
*) l
->data
;
632 new_extents
= obj
->bounding_box
;
636 obj
= (Object
*) l
->data
;
637 rectangle_union(&new_extents
, &obj
->bounding_box
);
641 new_extents
= invalid_extents
;
644 if (rectangle_equals(&new_extents
,&layer
->extents
)) return FALSE
;
646 layer
->extents
= new_extents
;
651 layer_replace_object_with_list(Layer
*layer
, Object
*remove_obj
,
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
;
665 list
->prev
->next
= insert_list
;
666 insert_list
->prev
= list
->prev
;
668 if (list
->next
!= NULL
) {
670 last
= g_list_last(insert_list
);
671 last
->next
= list
->next
;
672 list
->next
->prev
= last
;
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
);
686 layer_get_parent_diagram(Layer
*layer
)
688 return layer
->parent_diagram
;