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 lib/object.c implementation of DiaObject related functions */
28 #include "diagramdata.h" /* for Layer */
32 #include "dummy_dep.h"
36 /** Initialize an already allocated object with the given number of handles
37 * and connections. This does not create the actual Handle and Connection
38 * objects, which are expected to be added later.
39 * @param obj A newly allocated object with no handles or connections
40 * previously allocated.
41 * @param num_handles the number of handles to allocate room for.
42 * @param num_connections the number of connections to allocate room for.
45 object_init(DiaObject
*obj
,
49 obj
->num_handles
= num_handles
;
51 obj
->handles
= g_malloc0(sizeof(Handle
*) * num_handles
);
55 obj
->num_connections
= num_connections
;
56 if (num_connections
>0)
57 obj
->connections
= g_malloc0(sizeof(ConnectionPoint
*) * num_connections
);
59 obj
->connections
= NULL
;
62 /** Destroy an objects allocations and disconnect it from everything else.
63 * After calling this function, the object is no longer valid for use
64 * in a diagram. Note that this does not deallocate the object itself.
65 * @param obj The object being destroyed.
68 object_destroy(DiaObject
*obj
)
70 object_unconnect_all(obj
);
76 g_free(obj
->connections
);
81 /** Copy the object-level information of this object.
82 * This includes type, position, bounding box, number of handles and
83 * connections, operations, parentability, parent and children.
84 * After this copying you have to fix up:
88 * In particular the children lists will contain the same objects, which
89 * is not a valid situation.
90 * @param from The object being copied from
91 * @param to The object being copied to. This object does not need to
92 * have been object_init'ed, but if it is, its handles and
93 * connections arrays will be freed.
94 * @bugs Any existing children list will not be freed and will leak.
97 object_copy(DiaObject
*from
, DiaObject
*to
)
99 to
->type
= from
->type
;
100 to
->position
= from
->position
;
101 to
->bounding_box
= from
->bounding_box
;
103 to
->num_handles
= from
->num_handles
;
104 if (to
->handles
!= NULL
) g_free(to
->handles
);
105 if (to
->num_handles
>0)
106 to
->handles
= g_malloc(sizeof(Handle
*)*to
->num_handles
);
110 to
->num_connections
= from
->num_connections
;
111 if (to
->connections
!= NULL
) g_free(to
->connections
);
112 if (to
->num_connections
>0)
113 to
->connections
= g_malloc0(sizeof(ConnectionPoint
*) * to
->num_connections
);
115 to
->connections
= NULL
;
119 to
->flags
= from
->flags
;
120 to
->parent
= from
->parent
;
121 to
->children
= g_list_copy(from
->children
);
124 /** A hash function of a pointer value. Not the most well-spreadout
125 * function, as it has the same low bits as the pointer.
128 pointer_hash(gpointer some_pointer
)
130 return (guint
) some_pointer
;
134 /** Copy a list of objects, keeping connections and parent-children
135 * relation ships between the objects. It is assumed that the
136 * ops->copy function correctly creates the connections and handles
138 * @param list_orig The original list. This list will not be changed,
139 * nor will its objects.
140 * @return A list with the list_orig copies copied.
141 * @bugs Any children of an object in the list that are not themselves
142 * in the list will cause a NULL entry in the children list.
145 object_copy_list(GList
*list_orig
)
151 GHashTable
*hash_table
;
154 hash_table
= g_hash_table_new((GHashFunc
) pointer_hash
, NULL
);
156 /* First ops->copy the entire list */
159 while (list
!= NULL
) {
160 obj
= (DiaObject
*)list
->data
;
162 obj_copy
= obj
->ops
->copy(obj
);
164 g_hash_table_insert(hash_table
, obj
, obj_copy
);
166 list_copy
= g_list_append(list_copy
, obj_copy
);
168 list
= g_list_next(list
);
171 /* Rebuild the connections and parent/child references between the
172 objects in the list: */
174 while (list
!= NULL
) {
175 obj
= (DiaObject
*)list
->data
;
176 obj_copy
= g_hash_table_lookup(hash_table
, obj
);
178 if (obj_copy
->parent
)
179 obj_copy
->parent
= g_hash_table_lookup(hash_table
, obj_copy
->parent
);
181 if (object_flags_set(obj_copy
, DIA_OBJECT_CAN_PARENT
)
182 && obj_copy
->children
)
184 GList
*child_list
= obj_copy
->children
;
187 DiaObject
*child_obj
= (DiaObject
*) child_list
->data
;
188 child_list
->data
= g_hash_table_lookup(hash_table
, child_obj
);
189 child_list
= g_list_next(child_list
);
193 for (i
=0;i
<obj
->num_handles
;i
++) {
194 ConnectionPoint
*con_point
;
195 con_point
= obj
->handles
[i
]->connected_to
;
197 if ( con_point
!= NULL
) {
198 DiaObject
*other_obj
;
199 DiaObject
*other_obj_copy
;
202 other_obj
= con_point
->object
;
203 other_obj_copy
= g_hash_table_lookup(hash_table
, other_obj
);
205 if (other_obj_copy
== NULL
)
206 break; /* other object was not on list. */
209 while (other_obj
->connections
[con_point_nr
] != con_point
) {
213 object_connect(obj_copy
, obj_copy
->handles
[i
],
214 other_obj_copy
->connections
[con_point_nr
]);
218 list
= g_list_next(list
);
221 g_hash_table_destroy(hash_table
);
226 /** Move a number of objects the same distance. Any children of objects in
227 * the list are moved as well. This is intended to be called from within
228 * object_list_move_delta.
229 * @param objects The list of objects to move. This list must not contain
230 * any object that is a child (at any depth) of another object.
231 * @see parent_list_affected_hierarchy
232 * @param delta How far to move the objects.
233 * @param affected Whether to check parent boundaries???
234 * @return Undo information for the move, or NULL if no objects moved.
235 * @bugs If a parent and is child are both in the list, is the child moved
237 * @bugs The return Change object only contains info for a single object.
240 object_list_move_delta_r(GList
*objects
, Point
*delta
, gboolean affected
)
245 ObjectChange
*objchange
= NULL
;
247 if (delta
->x
== 0 && delta
->y
== 0)
251 while (list
!= NULL
) {
252 obj
= (DiaObject
*) list
->data
;
255 point_add(&pos
, delta
);
257 if (obj
->parent
&& affected
)
263 parent_handle_extents(obj
->parent
, &p_ext
);
264 parent_handle_extents(obj
, &c_ext
);
265 new_delta
= parent_move_child_delta(&p_ext
, &c_ext
, delta
);
266 point_add(&pos
, &new_delta
);
267 point_add(delta
, &new_delta
);
269 objchange
= obj
->ops
->move(obj
, &pos
);
271 if (object_flags_set(obj
, DIA_OBJECT_CAN_PARENT
) && obj
->children
)
272 objchange
= object_list_move_delta_r(obj
->children
, delta
, FALSE
);
274 list
= g_list_next(list
);
279 /** Move a set of objects a given amount.
280 * @param objects The list ob objects to move.
281 * @param delta The amount to move them.
282 * @bugs Why does this work? Seems like some objects are moved more than once.
285 object_list_move_delta(GList
*objects
, Point
*delta
)
290 ObjectChange
*objchange
= NULL
;
292 objects
= parent_list_affected_hierarchy(objects
);
294 /* The recursive function object_list_move_delta cannot process the toplevel
295 (in selection) objects so we have to have this extra loop */
298 obj
= (DiaObject
*) list
->data
;
301 process
= g_list_append(process
, obj
);
302 objchange
= object_list_move_delta_r(process
, delta
, (obj
->parent
!= NULL
) );
303 g_list_free(process
);
305 list
= g_list_next(list
);
310 /** Destroy a list of objects by calling ops->destroy on each in turn.
311 * @param list_to_be_destroyed A of objects list to destroy. The list itself
312 * will also be freed.
315 destroy_object_list(GList
*list_to_be_destroyed
)
320 list
= list_to_be_destroyed
;
321 while (list
!= NULL
) {
322 obj
= (DiaObject
*)list
->data
;
324 obj
->ops
->destroy(obj
);
327 list
= g_list_next(list
);
330 g_list_free(list_to_be_destroyed
);
333 /** Add a new handle to an object. This is merely a utility wrapper around
334 * object_add_handle_at().
335 * @param obj The object to add a handle to. This object must have been
337 * @param handle The new handle to add. The handle will be inserted as the
338 * last handle in the list.
341 object_add_handle(DiaObject
*obj
, Handle
*handle
)
343 object_add_handle_at(obj
, handle
, obj
->num_handles
);
346 /** Add a new handle to an object at a given position. This is merely
347 a utility wrapper around object_add_handle_at().
348 * @param obj The object to add a handle to. This object must have been
350 * @param handle The new handle to add.
351 * @param pos Where in the list of handles (0 <= pos <= obj->num_handles) to
355 object_add_handle_at(DiaObject
*obj
, Handle
*handle
, int pos
)
359 g_assert(0 <= pos
&& pos
<= obj
->num_handles
);
364 g_realloc(obj
->handles
, obj
->num_handles
*sizeof(Handle
*));
366 for (i
=obj
->num_handles
-1; i
> pos
; i
--) {
367 obj
->handles
[i
] = obj
->handles
[i
-1];
369 obj
->handles
[pos
] = handle
;
372 /** Remove a handle from an object.
373 * @param obj The object to remove a handle from.
374 * @param handle The handle to remove. If the handle does not exist on this
375 * object, an error message is displayed. The handle is not
376 * freed by this call.
379 object_remove_handle(DiaObject
*obj
, Handle
*handle
)
384 for (i
=0;i
<obj
->num_handles
;i
++) {
385 if (obj
->handles
[i
] == handle
)
390 message_error("Internal error, object_remove_handle: Handle doesn't exist");
394 for (i
=handle_nr
;i
<(obj
->num_handles
-1);i
++) {
395 obj
->handles
[i
] = obj
->handles
[i
+1];
397 obj
->handles
[obj
->num_handles
-1] = NULL
;
402 g_realloc(obj
->handles
, obj
->num_handles
*sizeof(Handle
*));
405 /** Add a new connectionpoint to an object.
406 * This is merely a convenience handler to add a connectionpoint at the
407 * end of an objects connectionpoint list.
408 * @see object_add_connectionpoint_at.
411 object_add_connectionpoint(DiaObject
*obj
, ConnectionPoint
*conpoint
)
413 object_add_connectionpoint_at(obj
, conpoint
, obj
->num_connections
);
416 /** Add a new connectionpoint to an object.
417 * @param obj The object to add the connectionpoint to.
418 * @param conpoint The connectionpoiint to add.
419 * @param pos Where in the list to add the connectionpoint
420 * (0 <= pos <= obj->num_connections).
423 object_add_connectionpoint_at(DiaObject
*obj
,
424 ConnectionPoint
*conpoint
, int pos
)
428 obj
->num_connections
++;
431 g_realloc(obj
->connections
,
432 obj
->num_connections
*sizeof(ConnectionPoint
*));
434 for (i
=obj
->num_connections
-1; i
> pos
; i
--) {
435 obj
->connections
[i
] = obj
->connections
[i
-1];
437 obj
->connections
[pos
] = conpoint
;
440 /** Remove an existing connectionpoint from and object.
441 * @param obj The object to remove the connectionpoint from.
442 * @param conpoint The connectionpoint to remove. The connectionpoint
443 * will not be freed by this function, but any handles
444 * connected to the connectionpoint will be
446 * If the connectionpoint does not exist on the object,
447 * an error message is displayed.
450 object_remove_connectionpoint(DiaObject
*obj
, ConnectionPoint
*conpoint
)
455 for (i
=0;i
<obj
->num_connections
;i
++) {
456 if (obj
->connections
[i
] == conpoint
)
461 message_error("Internal error, object_remove_connectionpoint: "
462 "ConnectionPoint doesn't exist");
466 object_remove_connections_to(conpoint
);
468 for (i
=nr
;i
<(obj
->num_connections
-1);i
++) {
469 obj
->connections
[i
] = obj
->connections
[i
+1];
471 obj
->connections
[obj
->num_connections
-1] = NULL
;
473 obj
->num_connections
--;
476 g_realloc(obj
->connections
, obj
->num_connections
*sizeof(ConnectionPoint
*));
480 /** Make a connection between the handle and the connectionpoint.
481 * @param obj The object having the handle.
482 * @param handle The handle being connected. This handle must not have
483 * connect_type HANDLE_NONCONNECTABLE, or an incomprehensible
484 * error message is displayed to the user.
485 * @param connectionpoint The connection point to connect to.
488 object_connect(DiaObject
*obj
, Handle
*handle
,
489 ConnectionPoint
*connectionpoint
)
491 if (handle
->connect_type
==HANDLE_NONCONNECTABLE
) {
492 message_error("Error? trying to connect a non connectable handle.\n"
493 "Check this out...\n");
496 handle
->connected_to
= connectionpoint
;
497 connectionpoint
->connected
=
498 g_list_prepend(connectionpoint
->connected
, obj
);
501 /** Disconnect handle from whatever it may be connected to.
502 * @param connected_obj The object having the handle.
503 * @param handle The handle to disconnect
506 object_unconnect(DiaObject
*connected_obj
, Handle
*handle
)
508 ConnectionPoint
*connectionpoint
;
510 connectionpoint
= handle
->connected_to
;
512 if (connectionpoint
!=NULL
) {
513 connectionpoint
->connected
=
514 g_list_remove(connectionpoint
->connected
, connected_obj
);
515 handle
->connected_to
= NULL
;
519 /** Remove all connections to the given connectionpoint.
520 * After this call, the connectionpoints connected field will be NULL,
521 * the list will have been freed, and no handles will be connected to the
523 * @param conpoint A connectionpoint.
526 object_remove_connections_to(ConnectionPoint
*conpoint
)
529 DiaObject
*connected_obj
;
532 list
= conpoint
->connected
;
533 while (list
!= NULL
) {
534 connected_obj
= (DiaObject
*)list
->data
;
536 for (i
=0;i
<connected_obj
->num_handles
;i
++) {
537 if (connected_obj
->handles
[i
]->connected_to
== conpoint
) {
538 connected_obj
->handles
[i
]->connected_to
= NULL
;
541 list
= g_list_next(list
);
543 g_list_free(conpoint
->connected
);
544 conpoint
->connected
= NULL
;
547 /** Remove all connections to and from an object.
548 * @param obj An object to disconnect from all connectionpoints and handles.
551 object_unconnect_all(DiaObject
*obj
)
555 for (i
=0;i
<obj
->num_handles
;i
++) {
556 object_unconnect(obj
, obj
->handles
[i
]);
558 for (i
=0;i
<obj
->num_connections
;i
++) {
559 object_remove_connections_to(obj
->connections
[i
]);
563 /** Save the object-specific parts of an object.
564 * Note that this does not save the attributes of an object, merely the
565 * basic data (currently position and bounding box).
566 * @param obj An object to save.
567 * @param obj_node An XML node to save the data to.
570 object_save(DiaObject
*obj
, ObjectNode obj_node
)
572 data_add_point(new_attribute(obj_node
, "obj_pos"),
574 data_add_rectangle(new_attribute(obj_node
, "obj_bb"),
578 /** Load the object-specific parts of an object.
579 * Note that this does not load the attributes of an object, merely the
580 * basic data (currently position and bounding box).
581 * @param obj An object to load into.
582 * @param obj_node An XML node to load the data from.
585 object_load(DiaObject
*obj
, ObjectNode obj_node
)
589 obj
->position
.x
= 0.0;
590 obj
->position
.y
= 0.0;
591 attr
= object_find_attribute(obj_node
, "obj_pos");
593 data_point( attribute_first_data(attr
), &obj
->position
);
595 obj
->bounding_box
.left
= obj
->bounding_box
.right
= 0.0;
596 obj
->bounding_box
.top
= obj
->bounding_box
.bottom
= 0.0;
597 attr
= object_find_attribute(obj_node
, "obj_bb");
599 data_rectangle( attribute_first_data(attr
), &obj
->bounding_box
);
602 /** Returns the layer that the given object belongs to.
603 * @param obj An object.
604 * @return The layer that contains the object, or NULL if the object is
608 dia_object_get_parent_layer(DiaObject
*obj
) {
609 return obj
->parent_layer
;
612 /** Returns true if `obj' is currently selected.
613 * Note that this takes time proportional to the number of selected
614 * objects, so don't use it frivolously.
615 * Note that if the objects is not in a layer (e.g. grouped), it is never
616 * considered selected.
617 * @param obj An object to test for selectedness.
618 * @return TRUE if the object is selected.
621 dia_object_is_selected (const DiaObject
*obj
)
623 Layer
*layer
= obj
->parent_layer
;
624 DiagramData
*diagram
= layer
? layer
->parent_diagram
: NULL
;
627 /* although this is a little bogus, it is better than crashing
628 * It appears as if neither group members nor "parented" objects do have their
629 * parent_layer set (but they aren't slected either, are they ? --hb
630 * No, grouped objects at least aren't selectable, but they may need
631 * to test selectedness when rendering beziers. Parented objects are
632 * a different thing, though. */
636 selected
= diagram
->selected
;
637 for (; selected
!= NULL
; selected
= g_list_next(selected
)) {
638 if (selected
->data
== obj
) return TRUE
;
643 /** Return the top-most object in the parent chain that has the given
645 * @param obj An object to start at. If this is NULL, NULL is returned.
646 * @param flags The flags to check. If 0, the top-most parent is returned.
647 * If more than one flag is given, the top-most parent that has all the given
648 * flags set is returned.
649 * @returns An object that either has all the flags set or
650 * is obj itself. It is guaranteed that no parent of this object has all the
654 dia_object_get_parent_with_flags(DiaObject
*obj
, guint flags
)
656 DiaObject
*top
= obj
;
660 while (obj
->parent
!= NULL
) {
662 if ((obj
->flags
& flags
) == flags
) {
669 /** Utility function: Checks if an objects can be selected.
670 * Reasons for not being selectable include:
671 * Being inside a closed group.
672 * Being in a non-active layer.
674 * @param obj An object to test.
675 * @returns TRUE if the object is not currently selected.
678 dia_object_is_selectable(DiaObject
*obj
)
680 if (obj
->parent_layer
== NULL
) {
683 return obj
->parent_layer
== obj
->parent_layer
->parent_diagram
->active_layer
684 && obj
== dia_object_get_parent_with_flags(obj
, DIA_OBJECT_GRABS_CHILD_INPUT
);
688 /****** DiaObject register: **********/
690 static guint
hash(gpointer key
)
692 char *string
= (char *)key
;
704 static gint
compare(gpointer a
, gpointer b
)
706 return strcmp((char *)a
, (char *)b
)==0;
709 static GHashTable
*object_type_table
= NULL
;
711 /** Initialize the object registry. */
713 object_registry_init(void)
715 object_type_table
= g_hash_table_new( (GHashFunc
) hash
, (GCompareFunc
) compare
);
718 /** Register the type of an object.
719 * This should be called as part of dia_plugin_init calls in modules that
720 * define objects for sheets. If an object type with the given name is
721 * already registered (typically due to a user saving a local copy), a
722 * warning is display to the user.
723 * @param type The type information.
726 object_register_type(DiaObjectType
*type
)
728 if (g_hash_table_lookup(object_type_table
, type
->name
) != NULL
) {
729 message_warning("Several object-types were named %s.\n"
730 "Only first one will be used.\n"
731 "Some things might not work as expected.\n",
735 g_hash_table_insert(object_type_table
, type
->name
, type
);
739 /** Performs a function on each registered object type.
740 * @param func A function foo(DiaObjectType, gpointer) to call.
741 * @param user_data Data passed through to the functions.
744 object_registry_foreach (GHFunc func
, gpointer user_data
)
746 g_hash_table_foreach (object_type_table
, func
, user_data
);
749 /** Get the object type information associated with a name.
750 * @param name A type name.
751 * @return A DiaObjectType for an object type with the given name, or
752 * NULL if no such type is registered.
755 object_get_type(char *name
)
757 return (DiaObjectType
*) g_hash_table_lookup(object_type_table
, name
);
760 /** True if all the given flags are set, false otherwise.
761 * @param obj An object to test.
762 * @param flags Flags to check if they are set. See definitions in object.h
763 * @return TRUE if all the flags given are set on the object.
766 object_flags_set(DiaObject
*obj
, gint flags
)
768 return (obj
->flags
& flags
) == flags
;
771 /** Utility function that always returns FALSE given any object.
772 * @param obj Not used.
776 object_return_false(DiaObject
*obj
)
781 /** Utility function that always returns NULL given any object.
782 * @param obj Not used.
786 object_return_null(DiaObject
*obj
)
791 /** Utility function that always returns nothing given any object.
792 * @param obj Not used.
795 object_return_void(DiaObject
*obj
)
800 /** Load an object from XML based on its properties.
801 * This function is suitable for implementing the object load function
802 * for an object with normal attributes. Any version-dependent handling
803 * should be done after calling this function.
804 * @param type The type of the object, used for creation.
805 * @param obj_node The XML node defining the object.
806 * @param version The version of the object found in the XML structure.
807 * @param filename The name of the file that the XML came from, for error
809 * @return A newly created object with properties loaded.
812 object_load_using_properties(const DiaObjectType
*type
,
813 ObjectNode obj_node
, int version
,
814 const char *filename
)
817 Point startpoint
= {0.0,0.0};
818 Handle
*handle1
,*handle2
;
820 obj
= type
->ops
->create(&startpoint
,NULL
, &handle1
,&handle2
);
821 object_load_props(obj
,obj_node
);
825 /** Save an object into an XML structure based on its properties.
826 * This function is suitable for implementing the object save function
827 * for an object with normal attributes.
828 * @param obj The object to save.
829 * @param obj_node The XML structure to save into.
830 * @param version The version of the objects structure this will be saved as
831 * (for allowing backwards compatibility).
832 * @param filename The name of the file being saved to, for error messages.
835 object_save_using_properties(DiaObject
*obj
, ObjectNode obj_node
,
836 int version
, const char *filename
)
838 object_save_props(obj
,obj_node
);
841 /** Copy an object based solely on its properties.
842 * This function is suitable for implementing the object save function
843 * for an object with normal attributes.
844 * @param obj An object to copy.
846 DiaObject
*object_copy_using_properties(DiaObject
*obj
)
848 Point startpoint
= {0.0,0.0};
849 Handle
*handle1
,*handle2
;
850 DiaObject
*newobj
= obj
->type
->ops
->create(&startpoint
,NULL
,
852 object_copy_props(newobj
,obj
,FALSE
);
856 /** Return a box that all 'real' parts of the object is bounded by.
857 * In most cases, this is the same as the enclosing box, but things like
858 * bezier controls would lie outside of this.
859 * @param obj The object to get the bounding box for.
860 * @return A pointer to a Rectangle object. This object should *not*
861 * be freed after use, as it belongs to the object.
864 dia_object_get_bounding_box(const DiaObject
*obj
) {
865 return &obj
->bounding_box
;
868 /** Return a box that encloses all interactively rendered parts of the object.
869 * @param obj The object to get the enclosing box for.
870 * @return A pointer to a Rectangle object. This object should *not*
871 * be freed after use, as it belongs to the object.
873 Rectangle
*dia_object_get_enclosing_box(const DiaObject
*obj
) {
874 /* I believe we can do this comparison, as it is only to compare for cases
875 * where it would be set explicitly to 0.
877 if (obj
->enclosing_box
.top
== 0.0 &&
878 obj
->enclosing_box
.bottom
== 0.0 &&
879 obj
->enclosing_box
.left
== 0.0 &&
880 obj
->enclosing_box
.right
== 0.0) {
881 return &obj
->bounding_box
;
883 } return &obj
->enclosing_box
;
888 /* The below are for debugging purposes only. */
890 /** Check that a DiaObject maintains its invariants and constrains.
891 * @param obj An object to check
892 * @return TRUE if the object is OK. */
894 dia_object_sanity_check(const DiaObject
*obj
, const gchar
*msg
) {
897 dia_assert_true(obj
->type
!= NULL
,
898 "%s: Object %p has null type\n",
901 dia_assert_true(obj
->type
->name
!= NULL
&&
902 g_utf8_validate(obj
->type
->name
, -1, NULL
),
903 "%s: Object %p has illegal type name %p (%s)\n",
904 msg
, obj
, obj
->type
->name
);
905 /* Check the position vs. the bounding box */
906 /* Check the handles */
907 dia_assert_true(obj
->num_handles
>= 0,
908 "%s: Object %p has < 0 (%d) handles\n",
909 msg
, obj
, obj
->num_handles
);
910 if (obj
->num_handles
!= 0) {
911 dia_assert_true(obj
->handles
!= NULL
,
912 "%s: Object %p has null handles\n", obj
);
914 for (i
= 0; i
< obj
->num_handles
; i
++) {
915 Handle
*h
= obj
->handles
[i
];
916 dia_assert_true(h
!= NULL
, "%s: Object %p handle %d is null\n",
919 /* Check handle id */
920 dia_assert_true((h
->id
>= 0 && h
->id
<= HANDLE_MOVE_ENDPOINT
)
921 || (h
->id
>= HANDLE_CUSTOM1
&& h
->id
<= HANDLE_CUSTOM9
),
922 "%s: Object %p handle %d (%p) has wrong id %d\n",
923 msg
, obj
, i
, h
, h
->id
);
924 /* Check handle type */
925 dia_assert_true(h
->type
>= 0 && h
->type
<= NUM_HANDLE_TYPES
,
926 "%s: Object %p handle %d (%p) has wrong type %d\n",
927 msg
, obj
, i
, h
, h
->type
);
928 /* Check handle pos is legal pos */
929 /* Check connect type is legal */
930 dia_assert_true(h
->connect_type
>= 0
931 && h
->connect_type
<= HANDLE_CONNECTABLE_NOBREAK
,
932 "%s: Object %p handle %d (%p) has wrong connect type %d\n",
933 msg
, obj
, i
, h
, h
->connect_type
);
934 /* Check that if connected, connection makes sense */
935 do { /* do...while(FALSE) to make aborting easy */
936 ConnectionPoint
*cp
= h
->connected_to
;
938 gboolean found
= FALSE
;
940 if (!dia_assert_true(cp
->object
!= NULL
,
941 "%s: Handle %d (%p) on object %p connects to CP %p with NULL object\n",
942 msg
, i
, h
, obj
, cp
)) break;
943 if (!dia_assert_true(cp
->object
->type
!= NULL
,
944 "%s: Handle %d (%p) on object %p connects to CP %p with untyped object %p\n",
945 msg
, i
, h
, obj
, cp
, cp
->object
)) break;
946 if (!dia_assert_true(cp
->object
->type
->name
!= NULL
&&
947 g_utf8_validate(cp
->object
->type
->name
, -1, NULL
),
948 "%s: Handle %d (%p) on object %p connects to CP %p with untyped object %p\n",
949 msg
, i
, h
, obj
, cp
, cp
->object
)) break;
950 dia_assert_true(fabs(cp
->pos
.x
- h
->pos
.x
) < 0.0000001 &&
951 fabs(cp
->pos
.y
- h
->pos
.y
) < 0.0000001,
952 "%s: Handle %d (%p) on object %p has pos %f, %f,\nbut its CP %p of object %p has pos %f, %f\n",
953 msg
, i
, h
, obj
, h
->pos
.x
, h
->pos
.y
,
954 cp
, cp
->object
, cp
->pos
.x
, cp
->pos
.y
);
955 for (conns
= cp
->connected
; conns
!= NULL
; conns
= g_list_next(conns
)) {
956 DiaObject
*obj2
= (DiaObject
*)conns
->data
;
959 for (j
= 0; j
< obj2
->num_handles
; j
++) {
960 if (obj2
->handles
[j
]->connected_to
== cp
) found
= TRUE
;
963 dia_assert_true(found
== TRUE
,
964 "%s: Handle %d (%p) on object %p is connected to %p on object %p, but is not in its connect list\n",
965 msg
, i
, h
, obj
, cp
, cp
->object
);
970 /* Check the connections */
971 dia_assert_true(obj
->num_connections
>= 0,
972 "%s: Object %p has < 0 (%d) connection points\n",
973 msg
, obj
, obj
->num_connections
);
974 if (obj
->num_connections
!= 0) {
975 dia_assert_true(obj
->connections
!= NULL
,
976 "%s: Object %p has NULL connections array\n",
979 for (i
= 0; i
< obj
->num_connections
; i
++) {
981 ConnectionPoint
*cp
= obj
->connections
[i
];
983 dia_assert_true(cp
!= NULL
, "%s: Object %p has null CP at %d\n", msg
, obj
, i
);
985 dia_assert_true(cp
->object
== obj
,
986 "%s: Object %p CP %d (%p) points to other obj %p\n",
987 msg
, obj
, i
, cp
, cp
->object
);
988 dia_assert_true((cp
->directions
& (~DIR_ALL
)) == 0,
989 "%s: Object %p CP %d (%p) has illegal directions %d\n",
990 msg
, obj
, i
, cp
, cp
->directions
);
991 dia_assert_true((cp
->flags
& CP_FLAGS_MAIN
) == cp
->flags
,
992 "%s: Object %p CP %d (%p) has illegal flags %d\n",
993 msg
, obj
, i
, cp
, cp
->flags
);
994 dia_assert_true(cp
->name
== NULL
995 || g_utf8_validate(cp
->name
, -1, NULL
),
996 "%s: Object %p CP %d (%p) has non-UTF8 name %s\n",
997 msg
, obj
, i
, cp
, cp
->name
);
999 for (connected
= cp
->connected
; connected
!= NULL
;
1000 connected
= g_list_next(connected
)) {
1002 obj2
= connected
->data
;
1003 dia_assert_true(obj2
!= NULL
, "%s: Object %p CP %d (%p) has NULL object at index %d\n",
1004 msg
, obj
, i
, cp
, j
);
1007 gboolean found_handle
= FALSE
;
1008 dia_assert_true(obj2
->type
->name
!= NULL
&&
1009 g_utf8_validate(obj2
->type
->name
, -1, NULL
),
1010 "%s: Object %p CP %d (%p) connected to untyped object %p (%s) at index %d\n",
1011 msg
, obj
, i
, cp
, obj2
, obj2
->type
->name
, j
);
1012 /* Check that connected object points back to this CP */
1013 for (k
= 0; k
< obj2
->num_handles
; k
++) {
1014 if (obj2
->handles
[k
] != NULL
&&
1015 obj2
->handles
[k
]->connected_to
== cp
) {
1016 found_handle
= TRUE
;
1019 dia_assert_true(found_handle
,
1020 "%s: Object %p CP %d (%p) connected to %p (%s) at index %d, but no handle points back\n",
1021 msg
, obj
, i
, cp
, obj2
, obj2
->type
->name
, j
);
1027 /* Check the children */