This is a patch for the idea that I mentioned a bit ago.I need it for the stuff I'm working on... I just dived into the dia code, so I may not have had the best knowledge of the internals.
I'll be glad to make any changes that are required ...After applying this patch, dia should act exactly as it did before, as parenting is disabled by default.
If you wanna play around with this, apply the patch, then edit objects/standard/box.c and change "BOX_CAN_PARENT FALSE" to "BOX_CAN_PARENT_TRUE" and recompile. From that point, you'll be able to add children to any box object.
I'm also including 2 sample (silly) diagrams that make use of parenting for no reason whatsoever :)
Here's a summary of this... (if it's turned on) * An object can have a parent object, and can have many children.* Dragging a tool onto an existing object makes the dropped object a child of the existing object at that point.
* The created object using drag'n'drop is limited to within the parent. If it's bigger in any dimension, that dimension is sized down to fit. If there's no way it can fit, an error message is presented and the object is not created.* Creating an object by selecting a tool and dragging inside the diagram will make the new object a child if the upper left corner is within another object * Creating via this method is also limited to within the parent (i.e. you can't drag beyond the parent) * Moving a parent, moves all the children (Moving a child does not move the parent)
* Deleting a parent deletes all the children * Copy/Paste keeps relationships * Save/Load keeps relationships * You cannot move an object beyond the parent (any side) * You cannot resize an object outwards beyond the parent (any side) * You cannot resize an object inwards beyond any children that it hasA lot of the things that a "Group" is used for can be done with parenting. Some even better. e.g. you can create a big square with 9 squares within it aranged in a 3 x 3 formation. You can drag the main square around automatically moving the children. Then, you can individually move each square (and any children it might have) without having to "break the group."
Of course, parenting is less efficient than grouping. (Don't know by how much).
Note #1: I changed the g_new() call to g_new0() during group creation. I mention this because it's not exactly relevant to parenting work. (It just caused a side-effect which I had to fix). Some fields might not have been reset to 0 and left with junk.
Note #2: When you perform "Copy" on selected objects, a copy of the list is made. When you perform "Paste" yet another copy is performed. Is that intentional or is that a bug?
Question: How can I determine the order in which the objects are present ? i.e. let's say I draw a square, then a square within a square, etc. Each new square will be "above" the previous squares. I didn't see any z-order code or anything like that during creation of objects. I need this to fix a small problem with using the "Parent" menu command. (Not critical, just an annoyance).
Attachment:
p.dia
Description: Binary data
Index: app/Makefile.am =================================================================== RCS file: /cvs/gnome/dia/app/Makefile.am,v retrieving revision 1.103 diff -u -r1.103 Makefile.am --- app/Makefile.am 8 Apr 2003 17:43:58 -0000 1.103 +++ app/Makefile.am 27 Jun 2003 07:55:59 -0000 @@ -145,6 +145,8 @@ diapsft2renderer.c \ persistence.h \ persistence.c \ + parent.c \ + parent.h \ $(print_files) dia_SOURCES = \ Index: app/commands.c =================================================================== RCS file: /cvs/gnome/dia/app/commands.c,v retrieving revision 1.115 diff -u -r1.115 commands.c --- app/commands.c 25 Apr 2003 16:34:48 -0000 1.115 +++ app/commands.c 27 Jun 2003 07:55:59 -0000 @@ -150,7 +150,7 @@ DDisplay *ddisp; ddisp = ddisplay_active(); - copy_list = diagram_get_sorted_selected(ddisp->diagram); + copy_list = parent_list_affected(diagram_get_sorted_selected(ddisp->diagram)); cnp_store_objects(object_copy_list(copy_list)); g_list_free(copy_list); @@ -169,18 +169,20 @@ diagram_selected_break_external(ddisp->diagram); - cut_list = diagram_get_sorted_selected(ddisp->diagram); + cut_list = parent_list_affected(diagram_get_sorted_selected(ddisp->diagram)); cnp_store_objects(object_copy_list(cut_list)); - change = undo_delete_objects(ddisp->diagram, cut_list); + change = undo_delete_objects_children(ddisp->diagram, cut_list); (change->apply)(change, ddisp->diagram); - + ddisplay_do_update_menu_sensitivity(ddisp); diagram_flush(ddisp->diagram); + diagram_modified(ddisp->diagram); undo_set_transactionpoint(ddisp->diagram->undo); + } void @@ -405,6 +407,7 @@ edit_delete_callback(gpointer data, guint action, GtkWidget *widget) { GList *delete_list; + GList *ptr; DDisplay *ddisp; Change *change; @@ -414,10 +417,9 @@ diagram_selected_break_external(ddisp->diagram); delete_list = diagram_get_sorted_selected(ddisp->diagram); - - change = undo_delete_objects(ddisp->diagram, delete_list); + change = undo_delete_objects_children(ddisp->diagram, delete_list); (change->apply)(change, ddisp->diagram); - + diagram_modified(ddisp->diagram); ddisplay_do_update_menu_sensitivity(ddisp); @@ -917,16 +919,28 @@ } void +objects_parent_callback(gpointer data, guint action, GtkWidget *widget) +{ + diagram_parent_selected(ddisplay_active()->diagram); +} + +void +objects_unparent_callback(gpointer data, guint action, GtkWidget *widget) +{ + diagram_unparent_selected(ddisplay_active()->diagram); +} + +void objects_group_callback(gpointer data, guint action, GtkWidget *widget) { diagram_group_selected(ddisplay_active()->diagram); -} +} void objects_ungroup_callback(gpointer data, guint action, GtkWidget *widget) { diagram_ungroup_selected(ddisplay_active()->diagram); -} +} void dialogs_properties_callback(gpointer data, guint action, GtkWidget *widget) Index: app/commands.h =================================================================== RCS file: /cvs/gnome/dia/app/commands.h,v retrieving revision 1.34 diff -u -r1.34 commands.h --- app/commands.h 30 Aug 2002 15:01:53 -0000 1.34 +++ app/commands.h 27 Jun 2003 07:55:59 -0000 @@ -74,6 +74,8 @@ GtkWidget *widget); void objects_place_down_callback(gpointer data, guint action, GtkWidget *widget); +void objects_parent_callback(gpointer data, guint action, GtkWidget *widget); +void objects_unparent_callback(gpointer data, guint action, GtkWidget *widget); void objects_group_callback(gpointer data, guint action, GtkWidget *widget); void objects_ungroup_callback(gpointer data, guint action, GtkWidget *widget); Index: app/create_object.c =================================================================== RCS file: /cvs/gnome/dia/app/create_object.c,v retrieving revision 1.22 diff -u -r1.22 create_object.c --- app/create_object.c 6 Oct 2002 18:55:11 -0000 1.22 +++ app/create_object.c 27 Jun 2003 07:56:00 -0000 @@ -43,6 +43,8 @@ Handle *handle1; Handle *handle2; Object *obj; + Object *parent_obj; + real click_distance; ddisplay_untransform_coords(ddisp, (int)event->x, (int)event->y, @@ -52,10 +54,22 @@ snap_to_grid(ddisp, &clickedpoint.x, &clickedpoint.y); + click_distance = ddisplay_untransform_length(ddisp, 3.0); + + parent_obj = diagram_find_clicked_object(ddisp->diagram, &clickedpoint, + click_distance); + + obj = dia_object_default_create (tool->objtype, &clickedpoint, tool->user_data, &handle1, &handle2); + if(parent_obj && parent_obj -> can_parent) /* starting point is within another object */ + { + obj -> parent = parent_obj; + parent_obj -> children = g_list_append(parent_obj -> children, obj); + } + diagram_add_object(ddisp->diagram, obj); /* Try a connect */ @@ -162,6 +176,9 @@ return; ddisplay_untransform_coords(ddisp, event->x, event->y, &to.x, &to.y); + + /* make sure the new object is restricted to its parent */ + parent_handle_move_out_check(tool -> obj, &to); /* Move to ConnectionPoint if near: */ if ((tool->handle != NULL && Index: app/diagram.c =================================================================== RCS file: /cvs/gnome/dia/app/diagram.c,v retrieving revision 1.62 diff -u -r1.62 diagram.c --- app/diagram.c 19 Jan 2003 16:38:20 -0000 1.62 +++ app/diagram.c 27 Jun 2003 07:56:00 -0000 @@ -200,12 +200,16 @@ dia->data->selected_count > 0); gtk_widget_set_sensitive(GTK_WIDGET(items->bring_to_front), dia->data->selected_count > 0); - + + gtk_widget_set_sensitive(GTK_WIDGET(items->parent), + (dia->data->selected_count > 1)); + gtk_widget_set_sensitive(GTK_WIDGET(items->unparent), + (dia->data->selected_count > 0)); gtk_widget_set_sensitive(GTK_WIDGET(items->group), - dia->data->selected_count > 1); + (dia->data->selected_count > 1)); gtk_widget_set_sensitive(GTK_WIDGET(items->ungroup), (dia->data->selected_count > 0)); - + gtk_widget_set_sensitive(GTK_WIDGET(items->align_h_l), dia->data->selected_count > 1); gtk_widget_set_sensitive(GTK_WIDGET(items->align_h_c), @@ -608,6 +612,92 @@ } } +gint diagram_parent_sort_cb(object_extent ** a, object_extent **b) +{ + if((*a) -> extent -> left < (*b) -> extent -> left) + return 1; + else if((*a) -> extent -> left > (*b) -> extent -> left) + return -1; + else + if((*a) -> extent -> top < (*b) -> extent -> top) + return 1; + else if((*a) -> extent -> top > (*b) -> extent -> top) + return -1; + else + return 0; +} + + +/* needs faster algorithm */ +void diagram_parent_selected(Diagram *dia) +{ + GList *list = dia -> data -> selected; + int length = g_list_length(list); + int idx, idx2; + object_extent *oe; + GPtrArray *rects = g_ptr_array_sized_new(length); + while(list) + { + oe = g_new(object_extent, 1); + oe -> object = list -> data; + oe -> extent = parent_handle_extents(list -> data); + g_ptr_array_add(rects, oe); + list = g_list_next(list); + } + /* sort all the objects by its left position */ + g_ptr_array_sort(rects, diagram_parent_sort_cb); + + for(idx = 0; idx < length; idx++) + { + object_extent *rect = g_ptr_array_index(rects, idx); + if(rect -> object -> parent) + continue; + + for(idx2 = idx + 1; idx2 < length; idx2++) + { + object_extent *rect2 = g_ptr_array_index(rects, idx2); + if(!rect2 -> object -> can_parent) + continue; + + if(rect -> extent -> right <= rect2 -> extent -> right + && rect -> extent -> bottom <= rect2 -> extent -> bottom) + { + rect -> object -> parent = rect2 -> object; + rect2 -> object -> children = g_list_append(rect2 -> object -> children, rect -> object); + break; + } + } + } + g_ptr_array_free(rects, TRUE); +} + +void diagram_unparent_selected(Diagram *dia) +{ + GList *list = dia -> data -> selected; + GList *child_ptr; + Object *obj, *child; + while(list) + { + obj = (Object *) list -> data; + if(!obj -> can_parent || !obj -> children) + { + list = g_list_next(list); + continue; + } + + child_ptr = obj -> children; + while(child_ptr) + { + child = (Object *) child_ptr -> data; + child -> parent = NULL; + child_ptr = g_list_next(child_ptr); + } + g_list_free(obj -> children); + obj -> children = NULL; + list = g_list_next(list); + } +} + void diagram_group_selected(Diagram *dia) { GList *list; @@ -616,6 +706,7 @@ Object *obj; GList *orig_list; + dia -> data -> selected = parent_list_affected(dia -> data -> selected); orig_list = g_list_copy(dia->data->active_layer->objects); Index: app/diagram.h =================================================================== RCS file: /cvs/gnome/dia/app/diagram.h,v retrieving revision 1.28 diff -u -r1.28 diagram.h --- app/diagram.h 19 Jan 2003 16:38:20 -0000 1.28 +++ app/diagram.h 27 Jun 2003 07:56:00 -0000 @@ -48,6 +48,14 @@ UndoStack *undo; }; +struct _object_extent +{ + Object *object; + Rectangle *extent; +}; + +typedef struct _object_extent object_extent; + GList *dia_open_diagrams(void); /* Read only! */ Diagram *diagram_load(const char *filename, DiaImportFilter *ifilter); @@ -86,6 +94,7 @@ Point *pos, Object *notthis); void diagram_update_extents(Diagram *dia); +gint diagram_parent_sort_cb(object_extent ** a, object_extent **b); void diagram_update_menu_sensitivity (Diagram *dia, UpdatableMenuItems *items); void diagram_update_menubar_sensitivity(Diagram *dia, UpdatableMenuItems *items); @@ -97,6 +106,8 @@ void diagram_place_up_selected(Diagram *dia); void diagram_group_selected(Diagram *dia); void diagram_ungroup_selected(Diagram *dia); +void diagram_parent_selected(Diagram *dia); +void diagram_unparent_selected(Diagram *dia); void diagram_set_filename(Diagram *dia, char *filename); Index: app/disp_callbacks.c =================================================================== RCS file: /cvs/gnome/dia/app/disp_callbacks.c,v retrieving revision 1.67 diff -u -r1.67 disp_callbacks.c --- app/disp_callbacks.c 29 Apr 2003 20:20:57 -0000 1.67 +++ app/disp_callbacks.c 27 Jun 2003 07:56:00 -0000 @@ -337,7 +337,7 @@ /* Make sure object updates its data and its connected: */ p = obj->position; - (obj->ops->move)(obj,&p); + (obj->ops->move)(obj,&p); diagram_update_connections_object(ddisp->diagram,obj,TRUE); object_add_updates(obj, ddisp->diagram); @@ -728,23 +728,115 @@ ddisplay_really_destroy(ddisp); } -void +/* returns NULL if object cannot be created */ +Object * ddisplay_drop_object(DDisplay *ddisp, gint x, gint y, ObjectType *otype, gpointer user_data) { Point droppoint; + Point droppoint_orig; Handle *handle1, *handle2; - Object *obj; + Object *obj, *p_obj; GList *list; + real click_distance; ddisplay_untransform_coords(ddisp, x, y, &droppoint.x, &droppoint.y); + /* save it before snap_to_grid modifies it */ + droppoint_orig = droppoint; + snap_to_grid(ddisp, &droppoint.x, &droppoint.y); obj = dia_object_default_create (otype, &droppoint, user_data, &handle1, &handle2); + + click_distance = ddisplay_untransform_length(ddisp, 3.0); + + p_obj = diagram_find_clicked_object(ddisp->diagram, &droppoint_orig, + click_distance); + + if(p_obj && p_obj-> can_parent) /* the tool was dropped inside an object that takes children*/ + { + Rectangle *p_ext, *c_ext; + int new_height = 0, new_width = 0; + real parent_height, child_height, parent_width, child_width; + Point new_pos; + + obj -> parent = p_obj; + p_obj -> children = g_list_append(p_obj -> children, obj); + + p_ext = parent_handle_extents(p_obj); + c_ext = parent_handle_extents(obj); + + parent_height = p_ext -> bottom - p_ext -> top; + child_height = c_ext -> bottom - c_ext -> top; + + parent_width = p_ext -> right - p_ext -> left; + child_width = c_ext -> right - c_ext -> left; + + /* we need the pre-snap position */ + c_ext -> left = droppoint_orig.x; + c_ext -> top = droppoint_orig.y; + c_ext -> right = c_ext -> left + child_width; + c_ext -> bottom = c_ext -> top + child_height; + + /* check if the top of the child is inside the parent, but the bottom is not */ + if(c_ext -> top < p_ext -> bottom && c_ext -> bottom > p_ext -> bottom) + { + /* check if child is smaller than parent height wise */ + if(child_height < parent_height) + new_height = child_height; + /* check if parent is bigger than 1 in height */ + else if(parent_height > 1) + new_height = round_up(parent_height) - 1; + } + else + { + new_height = child_height; + } + /* check if the left of the child is inside the partent, but the right is not */ + if(c_ext -> left < p_ext -> right && c_ext -> right > p_ext -> right) + { + /* check if child is smaller than parent width wise */ + if(child_width < parent_width) + new_width = child_width; + /* check if parent is bigger than 1 in width */ + else if(parent_width > 1) + new_width = round_up(parent_width) - 1; + } + else + { + new_width = child_width; + } + + g_free(p_ext); + g_free(c_ext); + + /* if we can't fit in both directions, produce an error */ + if(!new_height && !new_width) + { + message_error(_("The object you dropped cannot fit into its parent. \nEither expand the parent object, or drop the object elsewhere.")); + obj -> parent -> children = g_list_remove(obj -> parent -> children, obj); + obj -> ops -> destroy (obj); + return NULL; + } + /* if we can't fit height wise, make height same as of the parent */ + else if(!new_height) + new_height = parent_height; + /* if we can't fit width wise, make the width same as of the parent */ + else if(!new_width) + new_width = parent_width; + + new_pos.x = droppoint.x + new_width; + new_pos.y = droppoint.y + new_height; + obj -> ops -> move_handle(obj, handle2, &new_pos, + HANDLE_MOVE_USER,0); + + } + + diagram_add_object(ddisp->diagram, obj); diagram_remove_all_selected(ddisp->diagram, TRUE); /* unselect all */ diagram_select(ddisp->diagram, obj); @@ -767,4 +859,5 @@ undo_set_transactionpoint(ddisp->diagram->undo); if (prefs.reset_tools_after_create) tool_reset(); + return obj; } Index: app/disp_callbacks.h =================================================================== RCS file: /cvs/gnome/dia/app/disp_callbacks.h,v retrieving revision 1.8 diff -u -r1.8 disp_callbacks.h --- app/disp_callbacks.h 8 Sep 2002 15:49:49 -0000 1.8 +++ app/disp_callbacks.h 27 Jun 2003 07:56:00 -0000 @@ -43,7 +43,7 @@ gint ddisplay_delete (GtkWidget *widget, GdkEvent *event, gpointer data); void ddisplay_destroy (GtkWidget *widget, gpointer data); -void ddisplay_drop_object(DDisplay *ddisp, gint x, gint y, ObjectType *otype, +Object *ddisplay_drop_object(DDisplay *ddisp, gint x, gint y, ObjectType *otype, gpointer user_data); void ddisplay_im_context_commit(GtkIMContext *context, const gchar *str, DDisplay *ddisp); Index: app/interface.c =================================================================== RCS file: /cvs/gnome/dia/app/interface.c,v retrieving revision 1.90 diff -u -r1.90 interface.c --- app/interface.c 30 Apr 2003 21:03:36 -0000 1.90 +++ app/interface.c 27 Jun 2003 07:56:01 -0000 @@ -128,7 +128,7 @@ grid_toggle_snap(GtkWidget *widget, gpointer data) { DDisplay *ddisp = (DDisplay *)data; - ddisplay_set_snap_to_grid(ddisp, + ddisplay_set_snap_to_grid(ddisp, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))); } @@ -182,7 +182,7 @@ zoom_add_zoom_amount(GtkWidget *menu, gchar *text, DDisplay *ddisp) { GtkWidget *menuitem = gtk_menu_item_new_with_label(text); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - gtk_signal_connect(GTK_OBJECT(menuitem), + gtk_signal_connect(GTK_OBJECT(menuitem), "activate", (GtkSignalFunc)zoom_activate_callback, ddisp); g_object_set_data(G_OBJECT(menuitem), "zoomamount", text); @@ -195,7 +195,7 @@ GtkMenu *menu = GTK_MENU(user_data); - gtk_menu_popup(menu, NULL, NULL, NULL, NULL, + gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time); /* stop the signal emission so the button doesn't grab the * pointer from us */ @@ -262,6 +262,14 @@ return FALSE; } +inline int round_up (double x) +{ + if(x - (int) x > 0.001) + return (int) x + 1; + else + return (int) x ; +} + static void display_data_received_callback (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, @@ -275,6 +283,7 @@ ddisplay_drop_object(ddisp, x, y, object_get_type((gchar *)tooldata->extra_data), tooldata->user_data); + gtk_drag_finish (context, TRUE, FALSE, time); } else gtk_drag_finish (context, FALSE, FALSE, time); @@ -598,7 +607,7 @@ } if (tooldata->type != -1) { - tool_select (tooldata->type, tooldata->extra_data, + tool_select (tooldata->type, tooldata->extra_data, tooldata->user_data,widget); } } @@ -626,7 +635,7 @@ if (tool_data[i].callback_data.type == MODIFY_TOOL) { modify_tool_button = GTK_WIDGET(button); } - + if (tool_data[i].icon_data==NULL) { ObjectType *type; type = @@ -982,7 +991,7 @@ hbox = gtk_hbox_new (FALSE, 1); gtk_container_set_border_width (GTK_CONTAINER (hbox), 0); gtk_container_add (GTK_CONTAINER (frame), hbox); - + /* Color area: */ alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); gtk_container_set_border_width (GTK_CONTAINER (alignment), 3); @@ -1107,7 +1116,7 @@ { Diagram *diagram = NULL; DDisplay *ddisp; - gchar *sPath = NULL, *pFrom, *pTo; + gchar *sPath = NULL, *pFrom, *pTo; pFrom = strstr((gchar *) data->data, "file:"); while (pFrom) { @@ -1126,7 +1135,7 @@ if (diagram != NULL) { diagram_update_extents(diagram); layer_dialog_set_diagram(diagram); - + ddisp = new_display(diagram); } Index: app/load_save.c =================================================================== RCS file: /cvs/gnome/dia/app/load_save.c,v retrieving revision 1.55 diff -u -r1.55 load_save.c --- app/load_save.c 1 May 2003 21:28:15 -0000 1.55 +++ app/load_save.c 27 Jun 2003 07:56:01 -0000 @@ -68,7 +68,7 @@ static GList * read_objects(xmlNodePtr objects, Layer *layer, - GHashTable *objects_hash,const char *filename) + GHashTable *objects_hash,const char *filename, Object *parent) { GList *list; ObjectType *type; @@ -80,6 +80,7 @@ int version; GHashTable* unknown_hash; GString* unknown_str; + xmlNodePtr child_node; unknown_hash = g_hash_table_new(g_str_hash, g_str_equal); unknown_str = g_string_new("Unknown types while reading diagram file"); @@ -87,7 +88,7 @@ list = NULL; obj_node = objects->xmlChildrenNode; - + while ( obj_node != NULL) { if (xmlIsBlankNode(obj_node)) { obj_node = obj_node->next; @@ -100,7 +101,7 @@ typestr = xmlGetProp(obj_node, "type"); versionstr = xmlGetProp(obj_node, "version"); id = xmlGetProp(obj_node, "id"); - + version = 0; if (versionstr != NULL) { version = atoi(versionstr); @@ -108,7 +109,7 @@ } type = object_get_type((char *)typestr); - + if (!type) { if (NULL == g_hash_table_lookup(unknown_hash, typestr)) g_hash_table_insert(unknown_hash, g_strdup(typestr), 0); @@ -118,16 +119,33 @@ obj = type->ops->load(obj_node, version, filename); layer_add_object(layer,obj); list = g_list_append(list, obj); - + + if(parent) + { + obj -> parent = parent; + parent -> children = g_list_append(parent -> children, obj); + } + g_hash_table_insert(objects_hash, g_strdup((char *)id), obj); + + child_node = obj_node -> children; + while(child_node) + { + if(strcmp(child_node -> name, "children") == 0) + { + list = g_list_concat(list, read_objects(child_node, layer, objects_hash, filename, obj)); + break; + } + child_node = child_node -> next; + } } if (typestr) xmlFree(typestr); if (id) xmlFree (id); } else if (strcmp(obj_node->name, "group")==0) { obj = group_create(read_objects(obj_node, layer, - objects_hash, filename)); + objects_hash, filename, NULL)); layer_add_object(layer,obj); - + list = g_list_append(list, obj); } else { /* silently ignore other nodes */ @@ -449,7 +467,7 @@ /* Read in all objects: */ - list = read_objects(layer_node, layer, objects_hash, filename); + list = read_objects(layer_node, layer, objects_hash, filename, NULL); layer->objects = list; read_connections( list, layer_node, objects_hash); @@ -483,24 +501,31 @@ char buffer[31]; ObjectNode obj_node; xmlNodePtr group_node; + xmlNodePtr parent_node; GList *list; list = objects; while (list != NULL) { Object *obj = (Object *) list->data; + if(g_hash_table_lookup(objects_hash, obj)) + { + list = g_list_next(list); + continue; + } + if IS_GROUP(obj) { group_node = xmlNewChild(objects_node, NULL, "group", NULL); write_objects(group_objects(obj), group_node, objects_hash, obj_nr, filename); } else { obj_node = xmlNewChild(objects_node, NULL, "object", NULL); - + xmlSetProp(obj_node, "type", obj->type->name); - + g_snprintf(buffer, 30, "%d", obj->type->version); xmlSetProp(obj_node, "version", buffer); - + g_snprintf(buffer, 30, "O%d", *obj_nr); xmlSetProp(obj_node, "id", buffer); @@ -509,10 +534,16 @@ /* Add object -> obj_nr to hash table */ g_hash_table_insert(objects_hash, obj, GINT_TO_POINTER(*obj_nr)); (*obj_nr)++; - + + if(obj -> can_parent && obj -> children) + { + parent_node = xmlNewChild(obj_node, NULL, "children", NULL); + write_objects(obj -> children, parent_node, + objects_hash, obj_nr, filename); } - + list = g_list_next(list); + } } } Index: app/menus.c =================================================================== RCS file: /cvs/gnome/dia/app/menus.c,v retrieving revision 1.101 diff -u -r1.101 menus.c --- app/menus.c 9 Feb 2003 17:53:33 -0000 1.101 +++ app/menus.c 27 Jun 2003 07:56:02 -0000 @@ -207,6 +207,9 @@ GNOMEUIINFO_ITEM_NONE(N_("_Group"), NULL, objects_group_callback), GNOMEUIINFO_ITEM_NONE(N_("_Ungroup"), NULL, objects_ungroup_callback), GNOMEUIINFO_SEPARATOR, + GNOMEUIINFO_ITEM_NONE(N_("_Parent"), NULL, objects_group_parent), + GNOMEUIINFO_ITEM_NONE(N_("_Unparent Children"), NULL, objects_ungroup_unparent), + GNOMEUIINFO_SEPARATOR, GNOMEUIINFO_SUBTREE(N_("Align _Horizontal"), objects_align_h), GNOMEUIINFO_SUBTREE(N_("Align _Vertical"), objects_align_v), GNOMEUIINFO_END @@ -411,7 +414,10 @@ {N_("/Objects/---"), NULL, NULL, 0, "<Separator>"}, {N_("/Objects/_Group"), "<control>G", objects_group_callback, 0}, /* deliberately not using Ctrl+U for Ungroup */ - {N_("/Objects/_Ungroup"), "<control><shift>G", objects_ungroup_callback, 0}, + {N_("/Objects/_Ungroup"), "<control><shift>G", objects_ungroup_callback, 0}, + {N_("/Objects/---"), NULL, NULL, 0, "<Separator>"}, + {N_("/Objects/_Parent"), "<control>L", objects_parent_callback, 0}, + {N_("/Objects/_Unparent Children"), "<control><shift>L", objects_unparent_callback, 0}, {N_("/Objects/---"), NULL, NULL, 0, "<Separator>"}, {N_("/Objects/Align _Horizontal"), NULL, NULL, 0, "<Branch>"}, { "/Objects/Align Horizontal/tearoff", NULL, NULL, 0, "<Tearoff>" }, @@ -1178,7 +1184,7 @@ void -menus_initialize_updatable_items (UpdatableMenuItems *items, +menus_initialize_updatable_items (UpdatableMenuItems *items, GtkItemFactory *factory, const char *display) { static GString *path; @@ -1218,6 +1224,11 @@ items->group = menus_get_item_from_path(path->str, factory); g_string_append (g_string_assign(path, display),"/Objects/Ungroup"); items->ungroup = menus_get_item_from_path(path->str, factory); + + g_string_append (g_string_assign(path, display),"/Objects/Parent"); + items->parent = menus_get_item_from_path(path->str, factory); + g_string_append (g_string_assign(path, display),"/Objects/Unparent Children"); + items->unparent = menus_get_item_from_path(path->str, factory); g_string_append (g_string_assign(path, display),"/Objects/Align Horizontal/Left"); items->align_h_l = menus_get_item_from_path(path->str, factory); Index: app/menus.h =================================================================== RCS file: /cvs/gnome/dia/app/menus.h,v retrieving revision 1.15 diff -u -r1.15 menus.h --- app/menus.h 18 Nov 2002 19:52:21 -0000 1.15 +++ app/menus.h 27 Jun 2003 07:56:02 -0000 @@ -25,7 +25,7 @@ extern const struct zoom_pair zooms[10]; /* all the menu items that can be updated */ -struct _UpdatableMenuItems +struct _UpdatableMenuItems { GtkMenuItem *copy; GtkMenuItem *cut; @@ -42,6 +42,9 @@ GtkMenuItem *group; GtkMenuItem *ungroup; + + GtkMenuItem *parent; + GtkMenuItem *unparent; GtkMenuItem *align_h_l; GtkMenuItem *align_h_c; Index: app/modify_tool.c =================================================================== RCS file: /cvs/gnome/dia/app/modify_tool.c,v retrieving revision 1.39 diff -u -r1.39 modify_tool.c --- app/modify_tool.c 24 Feb 2003 16:24:34 -0000 1.39 +++ app/modify_tool.c 27 Jun 2003 07:56:02 -0000 @@ -111,7 +111,7 @@ /* Find the closest object to select it: */ click_distance = ddisplay_untransform_length(ddisp, 3.0); - + obj = diagram_find_clicked_object(diagram, clickedpoint, click_distance); @@ -194,7 +194,7 @@ gdk_pointer_grab (ddisp->canvas->window, FALSE, GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, NULL, event->time); - tool->start_at = *clickedpoint; + tool->start_at = handle -> pos; tool->start_time = time_micro(); ddisplay_set_all_cursor(get_cursor(CURSOR_SCROLL)); return TRUE; @@ -208,7 +208,7 @@ { Point clickedpoint; Object *clicked_obj; - + ddisplay_untransform_coords(ddisp, (int)event->x, (int)event->y, &clickedpoint.x, &clickedpoint.y); @@ -217,10 +217,9 @@ return; clicked_obj = click_select_object(ddisp, &clickedpoint, event); - if (do_if_clicked_handle(ddisp, tool, &clickedpoint, event)) return; - + if ( clicked_obj != NULL ) { tool->state = STATE_MOVE_OBJECT; tool->object = clicked_obj; @@ -239,7 +238,7 @@ tool->end_box = clickedpoint; tool->x1 = tool->x2 = (int) event->x; tool->y1 = tool->y2 = (int) event->y; - + if (tool->gc == NULL) { tool->gc = gdk_gc_new(ddisp->canvas->window); gdk_gc_set_line_attributes(tool->gc, 1, GDK_LINE_ON_OFF_DASH, @@ -363,8 +362,9 @@ GList *list; int i; Object *obj; - - list = ddisp->diagram->data->selected; + + /* consider non-selected children affected */ + list = parent_list_affected(ddisp->diagram->data->selected); tool->orig_pos = g_new(Point, g_list_length(list)); i=0; while (list != NULL) { @@ -408,15 +408,24 @@ diagram_flush(ddisp->diagram); break; case STATE_MOVE_HANDLE: + full_delta = to; + point_sub(&full_delta, &tool -> start_at); + + /* make sure resizing is restricted to its parent */ + + + /* if resize was blocked by parent, that means the resizing was + outward, thus it won't bother the children so we don't have to + check the children */ + if(!parent_handle_move_out_check(tool -> object, &to)) + parent_handle_move_in_check(tool -> object, &to, &tool -> start_at); + /* Move to ConnectionPoint if near: */ connectionpoint = object_find_connectpoint_display(ddisp, &to, tool->object); - if (event->state & GDK_CONTROL_MASK) { - full_delta = to; - point_sub(&full_delta, &tool->start_at); + if (event->state & GDK_CONTROL_MASK) vertical = (fabs(full_delta.x) < fabs(full_delta.y)); - } if ( (tool->handle->connect_type != HANDLE_NONCONNECTABLE) && (connectionpoint != NULL) ) { @@ -456,7 +465,7 @@ tool->object->ops->move_handle(tool->object, tool->handle, &to, HANDLE_MOVE_USER,0); object_add_updates(tool->object, ddisp->diagram); - + diagram_update_connections_selection(ddisp->diagram); diagram_flush(ddisp->diagram); break; @@ -523,7 +532,8 @@ diagram_update_connections_selection(ddisp->diagram); if (tool->orig_pos != NULL) { - list = ddisp->diagram->data->selected; + /* consider the non-selected children affected */ + list = parent_list_affected(ddisp->diagram->data->selected); dest_pos = g_new(Point, g_list_length(list)); i=0; while (list != NULL) { @@ -531,9 +541,9 @@ dest_pos[i] = obj->position; list = g_list_next(list); i++; } - + undo_move_objects(ddisp->diagram, tool->orig_pos, dest_pos, - g_list_copy(ddisp->diagram->data->selected)); + parent_list_affected(ddisp->diagram->data->selected)); } ddisplay_connect_selected(ddisp); /* pushes UNDO info */ Index: app/undo.c =================================================================== RCS file: /cvs/gnome/dia/app/undo.c,v retrieving revision 1.13 diff -u -r1.13 undo.c --- app/undo.c 1 Nov 2001 13:58:55 -0000 1.13 +++ app/undo.c 27 Jun 2003 07:56:02 -0000 @@ -569,7 +569,7 @@ diagram_unselect_objects(dia, change->obj_list); layer_remove_objects(change->layer, change->obj_list); object_add_updates_list(change->obj_list, dia); - + list = change->obj_list; while (list != NULL) { Object *obj = (Object *)list->data; @@ -582,7 +582,10 @@ if ((active_focus()!=NULL) && (active_focus()->obj == obj)) { remove_focus(); } - + + if(obj -> parent) /* Lose references to deleted object */ + obj -> parent -> children = g_list_remove(obj -> parent -> children, obj); + list = g_list_next(list); } @@ -592,11 +595,23 @@ static void delete_objects_revert(struct DeleteObjectsChange *change, Diagram *dia) { + GList *list; DEBUG_PRINTF(("delete_objects_revert()\n")); change->applied = 0; layer_set_object_list(change->layer, g_list_copy(change->original_objects)); object_add_updates_list(change->obj_list, dia); + + list = change -> obj_list; + while (list) + { + Object *obj = (Object *) list -> data; + if(obj -> parent) /* Restore child references */ + obj -> parent -> children = g_list_append(obj -> parent -> children, obj); + + list = g_list_next(list); + } + diagram_tree_add_objects(diagram_tree(), dia, change->obj_list); } @@ -611,13 +626,24 @@ g_list_free(change->original_objects); } +/* + This function deletes specified objects along with any children + they might have. + undo_delete_objects() only deletes the objects that are specified. +*/ +Change * +undo_delete_objects_children(Diagram *dia, GList *obj_list) +{ + return undo_delete_objects(dia, parent_list_affected(obj_list)); +} + Change * undo_delete_objects(Diagram *dia, GList *obj_list) { struct DeleteObjectsChange *change; change = g_new(struct DeleteObjectsChange, 1); - + change->change.apply = (UndoApplyFunc) delete_objects_apply; change->change.revert = (UndoRevertFunc) delete_objects_revert; change->change.free = (UndoFreeFunc) delete_objects_free; Index: app/undo.h =================================================================== RCS file: /cvs/gnome/dia/app/undo.h,v retrieving revision 1.9 diff -u -r1.9 undo.h --- app/undo.h 24 Jun 2000 14:46:58 -0000 1.9 +++ app/undo.h 27 Jun 2003 07:56:02 -0000 @@ -63,6 +63,8 @@ Change *undo_connect(Diagram *dia, Object *obj, Handle *handle, ConnectionPoint *connectionpoint); Change *undo_unconnect(Diagram *dia, Object *obj, Handle *handle); +Change * +undo_delete_objects_children(Diagram *dia, GList *obj_list); Change *undo_delete_objects(Diagram *dia, GList *obj_list); /* Reads current obj list */ Change *undo_insert_objects(Diagram *dia, GList *obj_list, int applied); Index: lib/group.c =================================================================== RCS file: /cvs/gnome/dia/lib/group.c,v retrieving revision 1.1 diff -u -r1.1 group.c --- lib/group.c 5 Dec 2002 23:47:53 -0000 1.1 +++ lib/group.c 27 Jun 2003 07:56:03 -0000 @@ -81,7 +81,7 @@ Object *obj; dist = 100000.0; - + list = group->objects; while (list != NULL) { obj = (Object *) list->data; @@ -294,10 +294,10 @@ int i; GList *list; int num_conn; - - group = g_new(Group,1); + + group = g_new0(Group,1); obj = &group->object; - + obj->type = &group_type; obj->ops = &group_ops; @@ -313,12 +313,12 @@ part_obj = (Object *) list->data; num_conn += part_obj->num_connections; - + list = g_list_next(list); } - + object_init(obj, 8, num_conn); - + /* Make connectionpoints be that of the 'inner' objects: */ num_conn = 0; list = objects; @@ -328,7 +328,7 @@ for (i=0;i<part_obj->num_connections;i++) { obj->connections[num_conn++] = part_obj->connections[i]; } - + list = g_list_next(list); } @@ -338,7 +338,7 @@ obj->handles[i]->connect_type = HANDLE_NONCONNECTABLE; obj->handles[i]->connected_to = NULL; } - + group_update_data(group); return (Object *)group; } Index: lib/object.c =================================================================== RCS file: /cvs/gnome/dia/lib/object.c,v retrieving revision 1.23 diff -u -r1.23 object.c --- lib/object.c 5 Dec 2002 23:47:53 -0000 1.23 +++ lib/object.c 27 Jun 2003 07:56:03 -0000 @@ -49,7 +49,7 @@ object_destroy(Object *obj) { object_unconnect_all(obj); - + if (obj->handles) g_free(obj->handles); @@ -62,6 +62,7 @@ /* After this copying you have to fix up: handles connections + children/parents */ void object_copy(Object *from, Object *to) @@ -85,6 +86,10 @@ to->connections = NULL; to->ops = from->ops; + + to -> can_parent = from -> can_parent; + to -> parent = from -> parent;; + to -> children = g_list_copy(from -> children); } static guint @@ -109,21 +114,37 @@ list_copy = NULL; while (list != NULL) { obj = (Object *)list->data; + obj_copy = obj->ops->copy(obj); g_hash_table_insert(hash_table, obj, obj_copy); - + list_copy = g_list_append(list_copy, obj_copy); list = g_list_next(list); } - /* Rebuild the connections between the objects in the list: */ + /* Rebuild the connections and parent/child references between the + objects in the list: */ list = list_orig; while (list != NULL) { obj = (Object *)list->data; obj_copy = g_hash_table_lookup(hash_table, obj); - + + if(obj_copy -> parent) + obj_copy -> parent = g_hash_table_lookup(hash_table, obj_copy -> parent); + + if(obj_copy -> can_parent && obj_copy -> children) + { + GList *child_list = obj_copy -> children; + while(child_list) + { + Object *child_obj = (Object *) child_list -> data; + child_list -> data = g_hash_table_lookup(hash_table, child_obj); + child_list = g_list_next(child_list); + } + } + for (i=0;i<obj->num_handles;i++) { ConnectionPoint *con_point; con_point = obj->handles[i]->connected_to; @@ -148,31 +169,72 @@ other_obj_copy->connections[con_point_nr]); } } - + list = g_list_next(list); } - + + g_hash_table_destroy(hash_table); - + return list_copy; } -extern void -object_list_move_delta(GList *objects, Point *delta) +void object_list_move_delta_r(GList *objects, Point *delta, gboolean affected) { GList *list; Object *obj; Point pos; + if(delta -> x == 0 && delta -> y == 0) + return; + list = objects; - while (list != NULL) { + while (list != NULL) + { obj = (Object *) list->data; - + pos = obj->position; point_add(&pos, delta); + if(obj -> parent && affected) + { + Rectangle *p_ext = parent_handle_extents(obj -> parent); + Rectangle *c_ext = parent_handle_extents(obj); + Point new_delta = parent_move_child_delta(p_ext, c_ext, delta); + point_add(&pos, &new_delta); + point_add(delta, &new_delta); + + g_free(p_ext); + g_free(c_ext); + } obj->ops->move(obj, &pos); + if(obj -> can_parent && obj -> children) + object_list_move_delta_r(obj -> children, delta, FALSE); + + list = g_list_next(list); + } +} + +extern void +object_list_move_delta(GList *objects, Point *delta) +{ + GList *list; + Object *obj; + GList *process; + objects = parent_list_affected_hierarchy(objects); + list = objects; + /* The recursive function object_list_move_delta cannot process the toplevel + (in selection) objects so we have to have this extra loop */ + while (list != NULL) + { + obj = (Object *) list->data; + + process = NULL; + process = g_list_append(process, obj); + object_list_move_delta_r(process, delta, (obj -> parent != NULL) ); + g_list_free(process); + list = g_list_next(list); } } @@ -262,12 +324,12 @@ obj->connections[obj->num_connections-1] = conpoint; } -void +void object_add_connectionpoint_at(Object *obj, ConnectionPoint *conpoint, int pos) { int i; - + obj->num_connections++; obj->connections = Index: lib/object.h =================================================================== RCS file: /cvs/gnome/dia/lib/object.h,v retrieving revision 1.39 diff -u -r1.39 object.h --- lib/object.h 5 Dec 2002 23:47:53 -0000 1.39 +++ lib/object.h 27 Jun 2003 07:56:03 -0000 @@ -31,6 +31,7 @@ #include "dia_xml.h" #include "properties.h" #include "diagramdata.h" +#include "../app/parent.h" /* This enumeration gives a bitset of modifier keys currently held down. */ @@ -260,6 +261,7 @@ void object_load(Object *obj, ObjectNode obj_node); GList *object_copy_list(GList *list); +void object_list_move_delta_r(GList *objects, Point *delta, gboolean affected); void object_list_move_delta(GList *objects, Point *delta); void destroy_object_list(GList *list); void object_add_handle(Object *obj, Handle *handle); @@ -327,7 +329,7 @@ Then an older object will be binary compatible, because all new code checks if new ops are supported (!= NULL) */ - void (*(unused[6]))(Object *obj,...); + void (*(unused[6]))(Object *obj,...); }; /* @@ -345,19 +347,22 @@ ObjectType *type; Point position; Rectangle bounding_box; - + int num_handles; Handle **handles; int num_connections; ConnectionPoint **connections; - + ObjectOps *ops; Layer *parent_layer; /* Back-pointer to the owning layer. This may only be set by functions internal to - the layer, and accessed via + the layer, and accessed via dia_object_get_parent_layer() */ + Object *parent; + GList *children; + gboolean can_parent; }; struct _ObjectTypeOps { Index: objects/standard/box.c =================================================================== RCS file: /cvs/gnome/dia/objects/standard/box.c,v retrieving revision 1.36 diff -u -r1.36 box.c --- objects/standard/box.c 24 Apr 2003 19:22:25 -0000 1.36 +++ objects/standard/box.c 27 Jun 2003 07:56:04 -0000 @@ -39,6 +39,8 @@ #define DEFAULT_HEIGHT 1.0 #define DEFAULT_BORDER 0.25 +#define BOX_CAN_PARENT FALSE + typedef struct _Box Box; struct _Box { @@ -267,7 +269,7 @@ renderer_ops->set_linejoin(renderer, LINEJOIN_MITER); if (box->corner_radius > 0) { - renderer_ops->draw_rounded_rect(renderer, + renderer_ops->draw_rounded_rect(renderer, &elem->corner, &lr_corner, &box->border_color, @@ -360,6 +362,7 @@ obj->ops = &box_ops; + elem->corner = *startpoint; elem->width = DEFAULT_WIDTH; elem->height = DEFAULT_HEIGHT; @@ -372,6 +375,8 @@ box->show_background = default_properties.show_background; box->corner_radius = default_properties.corner_radius; + obj -> can_parent = BOX_CAN_PARENT; + element_init(elem, 8, 8); for (i=0;i<8;i++) { @@ -476,6 +481,7 @@ obj->type = &box_type; obj->ops = &box_ops; + obj->can_parent = BOX_CAN_PARENT; element_load(elem, obj_node);