2006-12-03 Dimitris Glezos <dimitris@glezos.com>
[dia.git] / lib / diagramdata.c
blobbadbd88a48c8422f39874a8577dfd0fc580fd4d9
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 /** \file diagramdata.c This file defines the DiagramData object, which holds (mostly) saveable
20 * data global to a diagram.
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include "intl.h"
28 #include "diagramdata.h"
29 #include "diarenderer.h"
30 #include "paper.h"
31 #include "persistence.h"
33 #include "dynamic_obj.h"
34 #include "diamarshal.h"
37 static const Rectangle invalid_extents = { -1.0,-1.0,-1.0,-1.0 };
38 static void set_parent_layer(gpointer layer, gpointer object);
40 static void diagram_data_class_init (DiagramDataClass *klass);
41 static void diagram_data_init (DiagramData *object);
43 enum {
44 OBJECT_ADD,
45 OBJECT_REMOVE,
46 LAST_SIGNAL
49 static guint diagram_data_signals[LAST_SIGNAL] = { 0, };
51 static gpointer parent_class = NULL;
53 /** Get the type object for the DiagramData class.
55 GType
56 diagram_data_get_type(void)
58 static GType object_type = 0;
60 if (!object_type)
62 static const GTypeInfo object_info =
64 sizeof (DiagramDataClass),
65 (GBaseInitFunc) NULL,
66 (GBaseFinalizeFunc) NULL,
67 (GClassInitFunc) diagram_data_class_init,
68 NULL, /* class_finalize */
69 NULL, /* class_data */
70 sizeof (DiagramData),
71 0, /* n_preallocs */
72 (GInstanceInitFunc)diagram_data_init /* init */
75 object_type = g_type_register_static (G_TYPE_OBJECT,
76 "DiagramData",
77 &object_info, 0);
80 return object_type;
83 /* signal default handlers */
84 static void
85 _diagram_data_object_add (DiagramData* dia,Layer* layer,DiaObject* obj)
89 static void
90 _diagram_data_object_remove (DiagramData* dia,Layer* layer,DiaObject* obj)
94 /** Initialize a new diagram data object.
95 * @param data A diagram data object to initialize.
97 static void
98 diagram_data_init(DiagramData *data)
100 Color* color = persistence_register_color ("new_diagram_bgcolour", &color_white);
101 gboolean compress = persistence_register_boolean ("compress_save", TRUE);
102 Layer *first_layer;
104 data->extents.left = 0.0;
105 data->extents.right = 10.0;
106 data->extents.top = 0.0;
107 data->extents.bottom = 10.0;
109 data->bg_color = *color;
111 get_paper_info (&data->paper, -1, NULL);
113 first_layer = new_layer(g_strdup(_("Background")),data);
115 data->layers = g_ptr_array_new ();
116 g_ptr_array_add (data->layers, first_layer);
117 data->active_layer = first_layer;
119 data->selected_count_private = 0;
120 data->selected = NULL;
122 data->is_compressed = compress; /* Overridden by doc */
126 /** Deallocate memory owned by a DiagramData object.
127 * @param object A DiagramData object to finalize.
129 static void
130 diagram_data_finalize(GObject *object)
132 DiagramData *data = DIA_DIAGRAM_DATA(object);
134 guint i;
136 g_free(data->paper.name);
138 for (i=0;i<data->layers->len;i++) {
139 layer_destroy(g_ptr_array_index(data->layers, i));
141 g_ptr_array_free(data->layers, TRUE);
142 data->active_layer = NULL;
144 g_list_free(data->selected);
145 data->selected = NULL; /* for safety */
146 data->selected_count_private = 0;
149 /** Initialize the DiagramData class data.
150 * @param klass The class object to initialize functions for.
152 static void
153 diagram_data_class_init(DiagramDataClass *klass)
155 GObjectClass *object_class = G_OBJECT_CLASS (klass);
157 parent_class = g_type_class_peek_parent (klass);
159 diagram_data_signals[OBJECT_ADD] =
160 g_signal_new ("object_add",
161 G_TYPE_FROM_CLASS (klass),
162 G_SIGNAL_RUN_FIRST,
163 G_STRUCT_OFFSET (DiagramDataClass, object_add),
164 NULL, NULL,
165 dia_marshal_VOID__POINTER_POINTER,
166 G_TYPE_NONE,
168 G_TYPE_POINTER,
169 G_TYPE_POINTER);
171 diagram_data_signals[OBJECT_REMOVE] =
172 g_signal_new ("object_remove",
173 G_TYPE_FROM_CLASS (klass),
174 G_SIGNAL_RUN_FIRST,
175 G_STRUCT_OFFSET (DiagramDataClass, object_remove),
176 NULL, NULL,
177 dia_marshal_VOID__POINTER_POINTER,
178 G_TYPE_NONE,
180 G_TYPE_POINTER,
181 G_TYPE_POINTER);
183 object_class->finalize = diagram_data_finalize;
184 klass->object_add = _diagram_data_object_add;
185 klass->object_remove = _diagram_data_object_remove;
188 /** Create a new layer in this diagram.
189 * @param name Name of the new layer.
190 * @param parent The DiagramData that the layer will belong to,.
191 * @return A new Layer object.
192 * @bug Must determine if a NULL name is ok.
193 * @bug This belongs in a layers.c file.
194 * @bug Even though this sets parent diagram, it doesn't call the
195 * update_extents functions that add_layer does.
197 Layer *
198 new_layer(gchar *name, DiagramData *parent)
200 Layer *layer;
202 layer = g_new(Layer, 1);
204 layer->name = name;
206 layer->parent_diagram = parent;
207 layer->visible = TRUE;
208 layer->connectable = TRUE;
210 layer->objects = NULL;
212 layer->extents.left = 0.0;
213 layer->extents.right = 10.0;
214 layer->extents.top = 0.0;
215 layer->extents.bottom = 10.0;
217 return layer;
220 /** Destroy a layer object.
221 * @param layer The layer object to deallocate entirely.
222 * @bug This belongs in a layers.c file.
224 void
225 layer_destroy(Layer *layer)
227 g_free(layer->name);
228 destroy_object_list(layer->objects);
229 g_free(layer);
232 /** Raise a layer up one in a diagram.
233 * @param data The diagram that the layer belongs to.
234 * @param layer The layer to raise.
235 * @bug The diagram doesn't really need to be passed, as the layer knows it.
237 void
238 data_raise_layer(DiagramData *data, Layer *layer)
240 guint i;
241 int layer_nr = -1;
242 Layer *tmp;
244 for (i=0;i<data->layers->len;i++) {
245 if (g_ptr_array_index(data->layers, i)==layer)
246 layer_nr = i;
249 g_assert(layer_nr>=0);
251 if (layer_nr < data->layers->len-1) {
252 tmp = g_ptr_array_index(data->layers, layer_nr+1);
253 g_ptr_array_index(data->layers, layer_nr+1) =
254 g_ptr_array_index(data->layers, layer_nr);
255 g_ptr_array_index(data->layers, layer_nr) = tmp;
259 /** Lower a layer by one in a diagram.
260 * @param data The diagram that the layer belongs to.
261 * @param layer The layer to lower.
262 * @bug The diagram doesn't really need to be passed, as the layer knows it.
264 void
265 data_lower_layer(DiagramData *data, Layer *layer)
267 guint i;
268 int layer_nr = -1;
269 Layer *tmp;
271 for (i=0;i<data->layers->len;i++) {
272 if (g_ptr_array_index(data->layers, i)==layer)
273 layer_nr = i;
276 g_assert(layer_nr>=0);
278 if (layer_nr > 0) {
279 tmp = g_ptr_array_index(data->layers, layer_nr-1);
280 g_ptr_array_index(data->layers, layer_nr-1) =
281 g_ptr_array_index(data->layers, layer_nr);
282 g_ptr_array_index(data->layers, layer_nr) = tmp;
286 /** Add a layer object to a diagram.
287 * @param data The diagram to add the layer to.
288 * @param layer The layer to add.
289 * @bug Should just call data_add_layer_at().
291 void
292 data_add_layer(DiagramData *data, Layer *layer)
294 g_ptr_array_add(data->layers, layer);
295 layer->parent_diagram = data;
296 layer_update_extents(layer);
297 data_update_extents(data);
300 void
301 data_add_layer_at(DiagramData *data, Layer *layer, int pos)
303 int len;
304 int i;
306 g_ptr_array_add(data->layers, layer);
307 len = data->layers->len;
309 if ( (pos>=0) && (pos < len)) {
310 for (i=len-1;i>pos;i--) {
311 g_ptr_array_index(data->layers, i) = g_ptr_array_index(data->layers, i-1);
313 g_ptr_array_index(data->layers, pos) = layer;
316 layer->parent_diagram = data;
317 layer_update_extents(layer);
318 data_update_extents(data);
321 /** Set which layer is the active layer in a diagram.
322 * @param data The diagram in which to set the active layer.
323 * @param layer The layer that should be active.
324 * @bug The diagram doesn't really need to be passed, as the layer knows it.
326 void
327 data_set_active_layer(DiagramData *data, Layer *layer)
329 data->active_layer = layer;
332 /** Delete a layer from a diagram.
333 * @param data The diagram to delete the layer from.
334 * @param layer The layer to delete.
335 * @bug The diagram doesn't really need to be passed, as the layer knows it.
337 void
338 data_delete_layer(DiagramData *data, Layer *layer)
340 if (data->layers->len<=1)
341 return;
343 if (data->active_layer == layer) {
344 data_remove_all_selected(data);
346 layer->parent_diagram = NULL;
347 g_ptr_array_remove(data->layers, layer);
349 if (data->active_layer == layer) {
350 data->active_layer = g_ptr_array_index(data->layers, 0);
354 /** Select an object in a diagram. Note that this does not unselect other
355 * objects currently selected in the diagram.
356 * @param data The diagram to select in.
357 * @param obj The object to select.
358 * @bug Does not need to be passed the diagram, as it can be found from the
359 * object.
361 void
362 data_select(DiagramData *data, DiaObject *obj)
364 if (g_list_find (data->selected, obj))
365 return; /* should this be an error?`*/
366 data->selected = g_list_prepend(data->selected, obj);
367 data->selected_count_private++;
370 /** Deselect an object in a diagram. Note that other objects may still be
371 * selected after this function is done.
372 * @param data The diagram to deselect in.
373 * @param obj The object to deselect.
374 * @bug Does not need to be passed the diagram, as it can be found from the
375 * object.
377 void
378 data_unselect(DiagramData *data, DiaObject *obj)
380 if (!g_list_find (data->selected, obj))
381 return; /* should this be an error?`*/
382 data->selected = g_list_remove(data->selected, obj);
383 data->selected_count_private--;
386 /** Clears the list of selected objects.
387 * Does *not* remove these objects from the object list, despite its name.
388 * @param data The diagram to unselect all objects in.
390 void
391 data_remove_all_selected(DiagramData *data)
393 g_list_free(data->selected); /* Remove previous selection */
394 data->selected = NULL;
395 data->selected_count_private = 0;
398 /** Return TRUE if the diagram has visible layers.
399 * @param data The diagram to check.
400 * @return TRUE if at least one layer in the diagram is visible.
402 static gboolean
403 data_has_visible_layers(DiagramData *data)
405 guint i;
406 for (i = 0; i < data->layers->len; i++) {
407 Layer *layer = g_ptr_array_index(data->layers, i);
408 if (layer->visible) return TRUE;
410 return FALSE;
413 /** Set the diagram extents field to the union of the extents of the layers.
414 * @param data The diagram to get the extents for.
416 static void
417 data_get_layers_extents_union(DiagramData *data)
419 guint i;
420 gboolean first = TRUE;
421 Rectangle new_extents;
423 for ( i = 0 ; i<data->layers->len; i++) {
424 Layer *layer = g_ptr_array_index(data->layers, i);
425 if (!layer->visible) continue;
427 layer_update_extents(layer);
429 if (first) {
430 new_extents = layer->extents;
431 first = rectangle_equals(&new_extents,&invalid_extents);
432 } else {
433 if (!rectangle_equals(&layer->extents,&invalid_extents)) {
434 rectangle_union(&new_extents, &layer->extents);
439 data->extents = new_extents;
442 /** Change diagram scaling so that the extents are exactly visible.
443 * @param data The diagram to adjust.
444 * @bug Consider making it a teeny bit larger, or check that *all* objects
445 * calculate their extents correctly.
447 static void
448 data_adapt_scaling_to_extents(DiagramData *data)
450 gdouble pwidth = data->paper.width * data->paper.scaling;
451 gdouble pheight = data->paper.height * data->paper.scaling;
453 gdouble xscale = data->paper.fitwidth * pwidth /
454 (data->extents.right - data->extents.left);
455 gdouble yscale = data->paper.fitheight * pheight /
456 (data->extents.bottom - data->extents.top);
458 data->paper.scaling = (float)MIN(xscale, yscale);
459 data->paper.width = (float)(pwidth / data->paper.scaling);
460 data->paper.height = (float)(pheight / data->paper.scaling);
463 /** Adjust the extents field of a diagram.
464 * @param data The diagram to adjust.
465 * @return TRUE if the extents changed.
467 static gboolean
468 data_compute_extents(DiagramData *data)
470 Rectangle old_extents = data->extents;
472 if (!data_has_visible_layers(data)) {
473 if (data->layers->len > 0) {
474 Layer *layer = g_ptr_array_index(data->layers, 0);
475 layer_update_extents(layer);
477 data->extents = layer->extents;
478 } else {
479 data->extents = invalid_extents;
481 } else {
482 data_get_layers_extents_union(data);
485 if (rectangle_equals(&data->extents,&invalid_extents)) {
486 data->extents.left = 0.0;
487 data->extents.right = 10.0;
488 data->extents.top = 0.0;
489 data->extents.bottom = 10.0;
491 return (!rectangle_equals(&data->extents,&old_extents));
494 /** Update the extents of a diagram and adjust scaling if needed.
495 * @param data Diagram to update.
496 * @return TRUE if the diagram extents changed.
498 gboolean
499 data_update_extents(DiagramData *data)
502 gboolean changed;
504 changed = data_compute_extents(data);
505 if (changed && data->paper.fitto) data_adapt_scaling_to_extents(data);
507 return changed;
510 /** Get a list of selected objects in layer ordering.
511 * @param data The diagram to get objects from.
512 * @return A list of all currently selected objects. These all reside in
513 * the currently active layer. This list should be freed after use.
514 * @bug Does selection update correctly when the layer changes?
516 GList *
517 data_get_sorted_selected(DiagramData *data)
519 GList *list;
520 GList *sorted_list;
521 GList *found;
522 DiaObject *obj;
524 g_assert (g_list_length (data->selected) == data->selected_count_private);
525 if (data->selected_count_private == 0)
526 return NULL;
528 sorted_list = NULL;
529 list = g_list_last(data->active_layer->objects);
530 while (list != NULL) {
531 found = g_list_find(data->selected, list->data);
532 if (found) {
533 obj = (DiaObject *)found->data;
534 sorted_list = g_list_prepend(sorted_list, obj);
536 list = g_list_previous(list);
539 return sorted_list;
542 /** Remove the currently selected objects from the list of objects.
543 * The selected objects are returned in a newly created GList.
544 * @param data The diagram to get and remove the selected objects from.
545 * @return A list of all selected objects in the current diagram. This list
546 * should be freed after use. Unlike data_get_sorted_selected, the
547 * objects in the list are not in the diagram anymore.
549 GList *
550 data_get_sorted_selected_remove(DiagramData *data)
552 GList *list,*tmp;
553 GList *sorted_list;
554 GList *found;
555 DiaObject *obj;
557 g_assert (g_list_length (data->selected) == data->selected_count_private);
558 if (data->selected_count_private == 0)
559 return NULL;
561 sorted_list = NULL;
562 list = g_list_last(data->active_layer->objects);
563 while (list != NULL) {
564 found = g_list_find(data->selected, list->data);
565 if (found) {
566 obj = (DiaObject *)found->data;
567 sorted_list = g_list_prepend(sorted_list, obj);
569 tmp = list;
570 list = g_list_previous(list);
571 data->active_layer->objects =
572 g_list_remove_link(data->active_layer->objects, tmp);
573 } else {
574 list = g_list_previous(list);
578 return sorted_list;
582 /** Emits a GObject signal on DiagramData
583 * @param data The DiagramData that emits the signal.
584 * @param layer The Layer that the fired signal carries.
585 * @param obj The DiaObject that the fired signal carries.
586 * @param signal_name The name of the signal.
588 void
589 data_emit(DiagramData *data, Layer *layer, DiaObject* obj,
590 const char *signal_name)
592 /* check what signal it is */
593 if (strcmp("object_add",signal_name) == 0)
594 g_signal_emit(data, diagram_data_signals[OBJECT_ADD], 0, layer, obj);
596 if (strcmp("object_remove",signal_name) == 0)
597 g_signal_emit(data, diagram_data_signals[OBJECT_REMOVE], 0, layer, obj);
602 /** Render a diagram.
603 * @param data The diagram to render.
604 * @param renderer The renderer to render on.
605 * @param update The area that needs updating.
606 * @param obj_renderer If non-NULL, an alternative renderer of objects.
607 * @param gdata User data passed on to inner calls.
608 * @bug Describe obj_renderer better.
610 void
611 data_render(DiagramData *data, DiaRenderer *renderer, Rectangle *update,
612 ObjectRenderer obj_renderer,
613 gpointer gdata)
615 Layer *layer;
616 int i;
617 int active_layer;
619 if (!renderer->is_interactive) (DIA_RENDERER_GET_CLASS(renderer)->begin_render)(renderer);
621 for (i=0; i<data->layers->len; i++) {
622 layer = (Layer *) g_ptr_array_index(data->layers, i);
623 active_layer = (layer == data->active_layer);
624 if (layer->visible)
625 layer_render(layer, renderer, update, obj_renderer, gdata, active_layer);
628 if (!renderer->is_interactive) (DIA_RENDERER_GET_CLASS(renderer)->end_render)(renderer);
631 /** The default object renderer.
632 * @param obj An object to render.
633 * @param renderer The renderer to render on.
634 * @param active_layer The layer containing the object.
635 * @param data The diagram containing the layer.
636 * @bug The active_layer and data variables can be inferred from the object.
638 static void
639 normal_render(DiaObject *obj, DiaRenderer *renderer,
640 int active_layer,
641 gpointer data)
643 DIA_RENDERER_GET_CLASS(renderer)->draw_object(renderer, obj);
647 int render_bounding_boxes = FALSE;
649 /** Render all components of a single layer. This function also handles
650 * rendering of bounding boxes for debugging purposes.
651 * @param layer The layer to render.
652 * @param renderer The renderer to draw things with.
653 * @param update The rectangle that requires update. Only objects that
654 * intersect with this rectangle will actually be get rendered.
655 * @param obj_renderer A function that will render an object.
656 * @param data The diagram that the layer belongs to.
657 * @param active_layer Which number layer in the diagram is currently active.
658 * @bug data and active_layer can be inferred from layer, though possibly
659 * slowly.
661 void
662 layer_render(Layer *layer, DiaRenderer *renderer, Rectangle *update,
663 ObjectRenderer obj_renderer,
664 gpointer data,
665 int active_layer)
667 GList *list;
668 DiaObject *obj;
670 if (obj_renderer == NULL)
671 obj_renderer = normal_render;
673 /* Draw all objects: */
674 list = layer->objects;
675 while (list!=NULL) {
676 obj = (DiaObject *) list->data;
678 if (update==NULL || rectangle_intersects(update, &obj->bounding_box)) {
679 if ((render_bounding_boxes) && (renderer->is_interactive)) {
680 Point p1, p2;
681 Color col;
682 p1.x = obj->bounding_box.left;
683 p1.y = obj->bounding_box.top;
684 p2.x = obj->bounding_box.right;
685 p2.y = obj->bounding_box.bottom;
686 col.red = 1.0;
687 col.green = 0.0;
688 col.blue = 1.0;
690 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer,0.01);
691 DIA_RENDERER_GET_CLASS(renderer)->draw_rect(renderer, &p1, &p2, &col);
693 (*obj_renderer)(obj, renderer, active_layer, data);
696 list = g_list_next(list);
700 /** Set the parent layer of an object.
701 * @param element A DiaObject that should be part of a layer.
702 * @param user_data The layer it should be part of.
704 static void
705 set_parent_layer(gpointer element, gpointer user_data)
707 ((DiaObject*)element)->parent_layer = (Layer*)user_data;
708 /* FIXME: even group members need a parent_layer and what about parent objects ???
709 * Now I know again why I always try to avoid back-pointers )-; --hb.
710 * If the group objects didn't actually leave the diagram, this wouldn't
711 * be a problem. --LC */
714 /** Get the index of an object in a layer.
715 * @param layer The layer the object is (should be) in.
716 * @param obj The object to look for.
717 * @return The index of the object in the layers list of objects. This is also
718 * the vertical position of the object.
719 * @bug This should be in a separate layer.c file.
720 * @bug The layer could be inferred from the object, in which case the
721 * layer arg is not needed, and we would be sure we always looked in the
722 * right layer.
725 layer_object_index(Layer *layer, DiaObject *obj)
727 return (int)g_list_index(layer->objects, (gpointer) obj);
730 /** Add an object to the top of a layer.
731 * @param layer The layer to add the object to.
732 * @param obj The object to add. This must not already be part of another
733 * layer.
734 * @bug This should be in a separate layer.c file.
735 * @bug This should just call layer_add_object_at().
737 void
738 layer_add_object(Layer *layer, DiaObject *obj)
740 layer->objects = g_list_append(layer->objects, (gpointer) obj);
741 set_parent_layer(obj, layer);
743 /* send a signal that we have added a object to the diagram */
744 g_signal_emit (layer_get_parent_diagram(layer), diagram_data_signals[OBJECT_ADD], 0,layer,obj);
747 /** Add an object to a layer at a specific position.
748 * @param layer The layer to add the object to.
749 * @param obj The object to add. This must not be part of another layer.
750 * @param pos The top-to-bottom position this object should be inserted at.
751 * @bug This should be in a separate layer.c file.
753 void
754 layer_add_object_at(Layer *layer, DiaObject *obj, int pos)
756 layer->objects = g_list_insert(layer->objects, (gpointer) obj, pos);
757 set_parent_layer(obj, layer);
759 /* send a signal that we have added a object to the diagram */
760 g_signal_emit (layer_get_parent_diagram(layer), diagram_data_signals[OBJECT_ADD], 0,layer,obj);
763 /** Add a list of objects to the end of a layer.
764 * @param layer The layer to add objects to.
765 * @param obj_list The list of objects to add. These must not already
766 * be part of another layer.
767 * @bug Determine if the list is kept by g_list_concat.
769 void
770 layer_add_objects(Layer *layer, GList *obj_list)
772 GList *list = obj_list;
774 layer->objects = g_list_concat(layer->objects, obj_list);
775 g_list_foreach(obj_list, set_parent_layer, layer);
777 while (list != NULL)
779 /* send a signal that we have added a object to the diagram */
780 g_signal_emit (layer_get_parent_diagram(layer), diagram_data_signals[OBJECT_ADD], 0,layer,list->data);
782 list = g_list_next(list);
786 /** Add a list of objects to the top of a layer.
787 * @param layer The layer to add objects to.
788 * @param obj_list The list of objects to add. These must not already
789 * be part of another layer.
790 * @bug Determine if the list is kept by g_list_concat.
792 void
793 layer_add_objects_first(Layer *layer, GList *obj_list)
795 GList *list = obj_list;
797 layer->objects = g_list_concat(obj_list, layer->objects);
798 g_list_foreach(obj_list, set_parent_layer, layer);
800 /* Send one signal per object added */
801 while (list != NULL)
803 /* send a signal that we have added a object to the diagram */
804 g_signal_emit (layer_get_parent_diagram(layer), diagram_data_signals[OBJECT_ADD], 0,layer,list->data);
806 list = g_list_next(list);
811 /** Remove an object from a layer.
812 * @param layer The layer to remove the object from.
813 * @param obj The object to remove.
814 * @bug Why don't the layer_add functions deal with dynobj?
816 void
817 layer_remove_object(Layer *layer, DiaObject *obj)
819 layer->objects = g_list_remove(layer->objects, obj);
820 dynobj_list_remove_object(obj);
821 set_parent_layer(obj, NULL);
823 /* send a signal that we have removed a object from the diagram */
824 g_signal_emit (layer_get_parent_diagram(layer), diagram_data_signals[OBJECT_REMOVE], 0,layer,obj);
827 /** Remove a list of objects from a layer.
828 * @param layer The layer to remove the objects from.
829 * @param obj The objects to remove.
830 * @bug This should call layer_remove_object repeatedly.
832 void
833 layer_remove_objects(Layer *layer, GList *obj_list)
835 DiaObject *obj;
836 while (obj_list != NULL) {
837 obj = (DiaObject *) obj_list->data;
839 layer->objects = g_list_remove(layer->objects, obj);
841 obj_list = g_list_next(obj_list);
842 dynobj_list_remove_object(obj);
843 set_parent_layer(obj, NULL);
844 /* send a signal that we have removed a object from the diagram */
845 g_signal_emit (layer_get_parent_diagram(layer), diagram_data_signals[OBJECT_REMOVE], 0,layer,obj);
849 /** Find the objects that intersect a given rectangle.
850 * @param layer The layer to search in.
851 * @param rect The rectangle to intersect with.
852 * @return List of objects whose bounding box intersect the rectangle. The
853 * list should be freed by the caller.
855 GList *
856 layer_find_objects_intersecting_rectangle(Layer *layer, Rectangle *rect)
858 GList *list;
859 GList *selected_list;
860 DiaObject *obj;
862 selected_list = NULL;
863 list = layer->objects;
864 while (list != NULL) {
865 obj = (DiaObject *)list->data;
867 if (rectangle_intersects(rect, &obj->bounding_box)) {
868 if (dia_object_is_selectable(obj)) {
869 selected_list = g_list_prepend(selected_list, obj);
871 /* Objects in closed groups do not get selected, but their parents do.
872 * Since the parents bbox is outside the objects, they will be found
873 * anyway and the inner object can just be skipped. */
876 list = g_list_next(list);
879 return selected_list;
882 /** Find objects entirely contained in a rectangle.
883 * @param layer The layer to search for objects in.
884 * @param rect The rectangle that the objects should be in.
885 * @return A list containing the objects that are entirely contained in the
886 * rectangle. The list should be freed by the caller.
888 GList *
889 layer_find_objects_in_rectangle(Layer *layer, Rectangle *rect)
891 GList *list;
892 GList *selected_list;
893 DiaObject *obj;
895 selected_list = NULL;
896 list = layer->objects;
897 while (list != NULL) {
898 DiaObject *parent;
899 obj = (DiaObject *)list->data;
901 if (rectangle_in_rectangle(rect, &obj->bounding_box)) {
902 if (dia_object_is_selectable(obj)) {
903 selected_list = g_list_prepend(selected_list, obj);
907 list = g_list_next(list);
910 return selected_list;
913 /** Find the object closest to the given point in the layer,
914 * no further away than maxdist, and not included in avoid.
915 * Stops it if finds an object that includes the point.
916 * @param layer The layer to search in.
917 * @param pos The point to compare to.
918 * @param maxdist The maximum distance the object can be from the point.
919 * @param avoid A list of objects that cannot be returned by this search.
920 * @return The closest object, or NULL if no allowed objects are closer than
921 * maxdist.
923 DiaObject *
924 layer_find_closest_object_except(Layer *layer, Point *pos,
925 real maxdist, GList *avoid)
927 GList *l;
928 DiaObject *closest;
929 DiaObject *obj;
930 real dist;
931 GList *avoid_tmp;
933 closest = NULL;
935 for (l = layer->objects; l!=NULL; l = g_list_next(l)) {
936 obj = (DiaObject *) l->data;
938 /* Check bounding box here too. Might give speedup. */
939 dist = obj->ops->distance_from(obj, pos);
941 if (maxdist-dist > 0.00000001) {
942 for (avoid_tmp = avoid; avoid_tmp != NULL; avoid_tmp = avoid_tmp->next) {
943 if (avoid_tmp->data == obj) {
944 goto NEXTOBJECT;
947 closest = obj;
949 NEXTOBJECT:
953 /* If the object is within a closed group, find the group. */
954 closest = dia_object_get_parent_with_flags(closest,
955 DIA_OBJECT_GRABS_CHILD_INPUT);
957 return closest;
960 /** Find the object closest to the given point in the layer,
961 * no further away than maxdist.
962 * Stops it if finds an object that includes the point.
963 * @param layer The layer to search in.
964 * @param pos The point to compare to.
965 * @param maxdist The maximum distance the object can be from the point.
966 * @return The closest object, or NULL if none are closer than maxdist.
968 DiaObject *
969 layer_find_closest_object(Layer *layer, Point *pos, real maxdist)
971 return layer_find_closest_object_except(layer, pos, maxdist, NULL);
975 /** Find the connectionpoint closest to pos in a layer.
976 * @param layer
977 * @param closest
978 * @param pos
979 * @param notthis
980 * @return
982 real
983 layer_find_closest_connectionpoint(Layer *layer,
984 ConnectionPoint **closest,
985 Point *pos,
986 DiaObject *notthis)
988 GList *l;
989 DiaObject *obj;
990 ConnectionPoint *cp;
991 real mindist, dist;
992 int i;
994 mindist = 1000000.0; /* Realy big value... */
996 *closest = NULL;
998 for (l = layer->objects; l!=NULL; l = g_list_next(l) ) {
999 obj = (DiaObject *) l->data;
1001 if (obj == notthis) continue;
1002 if (obj != dia_object_get_parent_with_flags(obj, DIA_OBJECT_GRABS_CHILD_INPUT))
1003 continue;
1004 for (i=0;i<obj->num_connections;i++) {
1005 cp = obj->connections[i];
1006 /* Note: Uses manhattan metric for speed... */
1007 dist = distance_point_point_manhattan(pos, &cp->pos);
1008 if (dist<mindist) {
1009 mindist = dist;
1010 *closest = cp;
1016 return mindist;
1020 layer_update_extents(Layer *layer)
1022 GList *l;
1023 DiaObject *obj;
1024 Rectangle new_extents;
1026 l = layer->objects;
1027 if (l!=NULL) {
1028 obj = (DiaObject *) l->data;
1029 new_extents = obj->bounding_box;
1030 l = g_list_next(l);
1032 while(l!=NULL) {
1033 obj = (DiaObject *) l->data;
1034 rectangle_union(&new_extents, &obj->bounding_box);
1035 l = g_list_next(l);
1037 } else {
1038 new_extents = invalid_extents;
1041 if (rectangle_equals(&new_extents,&layer->extents)) return FALSE;
1043 layer->extents = new_extents;
1044 return TRUE;
1047 void
1048 layer_replace_object_with_list(Layer *layer, DiaObject *remove_obj,
1049 GList *insert_list)
1051 GList *list;
1053 list = g_list_find(layer->objects, remove_obj);
1055 g_assert(list!=NULL);
1056 set_parent_layer(remove_obj, NULL);
1057 dynobj_list_remove_object(remove_obj);
1058 g_list_foreach(insert_list, set_parent_layer, layer);
1060 if (list->prev == NULL) {
1061 layer->objects = insert_list;
1062 } else {
1063 list->prev->next = insert_list;
1064 insert_list->prev = list->prev;
1066 if (list->next != NULL) {
1067 GList *last;
1068 last = g_list_last(insert_list);
1069 last->next = list->next;
1070 list->next->prev = last;
1072 g_list_free_1(list);
1075 static void
1076 layer_remove_dynobj(gpointer obj, gpointer userdata)
1078 dynobj_list_remove_object((DiaObject*)obj);
1081 void layer_set_object_list(Layer *layer, GList *list)
1083 g_list_foreach(layer->objects, set_parent_layer, NULL);
1084 g_list_foreach(layer->objects, layer_remove_dynobj, NULL);
1085 g_list_free(layer->objects);
1086 layer->objects = list;
1087 g_list_foreach(layer->objects, set_parent_layer, layer);
1090 DiagramData *
1091 layer_get_parent_diagram(Layer *layer)
1093 return layer->parent_diagram;