/* * $Header: /home/barad-dur/vision/forsyth/schenney/sced-0.94/c/RCS/csg_edit.c,v 1.0 1997/05/06 20:30:22 schenney Exp $ * * $Log: csg_edit.c,v $ * Revision 1.0 1997/05/06 20:30:22 schenney * Initial revision * */ #define PATCHLEVEL 0 /* ** ScEd: A Constraint Based Scene Editor. ** Copyright (C) 1994-1998 Stephen Chenney (schenney@cs.berkeley.edu) ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ** sced: A Constraint Based Object Scene Editor ** ** csg_edit.c : CSG functions related to the list of objects displayed. */ #include #include #include #include #include #include #include #include static void Create_New_Tree(ObjectInstancePtr); Boolean CSG_Set_Clear_Attribs(); static void CSG_Update_Selected_References(ObjectInstancePtr, InstanceList); static InstanceList Build_Instance_List_From_Tree(CSGNodePtr); CSGTreeList displayed_trees; int num_displayed_trees = 0; int max_num_displayed_trees = 0; #define ATTRIBS_CONTINUE -1 #define ATTRIBS_TRUE 1 #define ATTRIBS_FALSE 0 static int attribs_done; extern char *top_level_translations; void CSG_Window_Popup(Widget w, XtPointer cl, XtPointer ca) { InstanceList instances = *(InstanceList*)cl; InstanceList elmt; InstanceList victim; ObjectInstancePtr obj; Boolean first_found = ( w ? FALSE : TRUE ); Boolean clear_attribs = FALSE; if ( ! csg_window.shell ) { Create_CSG_Display(); XtAugmentTranslations(csg_window.shell, XtParseTranslationTable(top_level_translations)); } XMapRaised(XtDisplay(csg_window.shell), XtWindow(csg_window.shell)); /* Check for instances. */ for ( elmt = instances ; elmt ; elmt = elmt->next ) { /* Ignore light sources and non-closed objects. */ if ( ! Obj_Is_CSGable(elmt->the_instance) ) { /* Reset the highlight. */ elmt->the_instance->o_flags &= ( ObjAll ^ ObjSelected ); continue; } if ( ! ( victim = Find_Object_In_Instances(elmt->the_instance, main_window.all_instances))) continue; if ( ! first_found ) { first_found = TRUE; clear_attribs = CSG_Set_Clear_Attribs(); } if ( main_window.all_instances == victim ) main_window.all_instances = victim->next; Delete_Element(victim); obj = victim->the_instance; obj->o_flags &= ( ObjAll ^ ObjSelected ); free(victim); if ( clear_attribs ) obj->o_attribs->defined = FALSE; /* Check for the victim in the edit lists. */ if ( ( victim = Find_Object_In_Instances(obj, main_window.edit_instances) ) ) Delete_Edit_Instance(&main_window, victim); /* Update dependencies. It's messy in this case. */ CSG_Update_Selected_References(obj, instances); Create_New_Tree(obj); } Free_Selection_List(instances); *(InstanceList*)cl = NULL; View_Update(&main_window, main_window.all_instances, ViewNone); } void New_CSG_Instance(ObjectInstancePtr inst) { Create_New_Tree(inst); XawTreeForceLayout(csg_tree_widget); } static void Create_New_Tree(ObjectInstancePtr instance) { if ( num_displayed_trees >= max_num_displayed_trees ) { if ( max_num_displayed_trees ) displayed_trees = More(displayed_trees, CSGTreeRoot, max_num_displayed_trees + 5); else displayed_trees = New(CSGTreeRoot, 5); max_num_displayed_trees += 5; } displayed_trees[num_displayed_trees].tree = Create_CSG_Node(NULL, instance, csg_leaf_op); Create_CSG_Widget(displayed_trees[num_displayed_trees].tree); displayed_trees[num_displayed_trees].displayed = TRUE; displayed_trees[num_displayed_trees].instances = NULL; Insert_Element(&(displayed_trees[num_displayed_trees].instances), instance); displayed_trees[num_displayed_trees].orig_bases = NULL; displayed_trees[num_displayed_trees].num_orig_bases = 0; CSG_Add_Instances_To_Displayed( displayed_trees[num_displayed_trees].instances); num_displayed_trees++; changed_scene = TRUE; } void Add_Displayed_Tree(CSGNodePtr root, Boolean display, BaseObjectPtr orig_base) { if ( num_displayed_trees >= max_num_displayed_trees ) { if ( max_num_displayed_trees ) displayed_trees = More(displayed_trees, CSGTreeRoot, max_num_displayed_trees + 5); else displayed_trees = New(CSGTreeRoot, 5); max_num_displayed_trees += 5; } displayed_trees[num_displayed_trees].tree = root; XtDestroyWidget(root->csg_widget); Create_CSG_Widget(root); if ( root->csg_left_child ) { XtVaSetValues(root->csg_left_child->csg_widget, XtNtreeParent, root->csg_widget, NULL); XtVaSetValues(root->csg_right_child->csg_widget, XtNtreeParent, root->csg_widget, NULL); } displayed_trees[num_displayed_trees].displayed = display; displayed_trees[num_displayed_trees].instances = Build_Instance_List_From_Tree(root); if ( orig_base ) { displayed_trees[num_displayed_trees].orig_bases = New(BaseObjectPtr, 1); displayed_trees[num_displayed_trees].orig_bases[0] = orig_base; displayed_trees[num_displayed_trees].num_orig_bases = 1; } else { displayed_trees[num_displayed_trees].orig_bases = NULL; displayed_trees[num_displayed_trees].num_orig_bases = 0; } if ( display ) CSG_Add_Instances_To_Displayed( displayed_trees[num_displayed_trees].instances); num_displayed_trees++; CSG_Reorder_Root_Widgets(); } CSGTreeRoot Delete_Displayed_Tree(CSGNodePtr root) { CSGTreeRoot result; int target_index; int i; for ( target_index = 0 ; displayed_trees[target_index].tree != root ; target_index++ ); result = displayed_trees[target_index]; if ( displayed_trees[target_index].displayed ) CSG_Remove_Instances_From_Displayed( displayed_trees[target_index].instances); Free_Selection_List(displayed_trees[target_index].instances); /* Shuffle the others down. */ for ( i = target_index + 1 ; i < num_displayed_trees ; i++ ) { displayed_trees[i-1] = displayed_trees[i]; } num_displayed_trees--; return result; } static Boolean CSG_Update_Selected_References_Test(ObjectInstancePtr obj, void *ptr) { InstanceList list = (InstanceList)ptr; return ( ! ( Obj_Is_CSGable(obj) && Find_Object_In_Instances(obj, list) ) ); } /* void ** CSG_Update_Selected_References(ObjectInstancePtr obj, InstanceList list) ** Updates all the reference specs for constraints in obj so that ** the only ones left appear in list. */ static void CSG_Update_Selected_References(ObjectInstancePtr obj, InstanceList list) { int i; Constraint_Change_References(obj, CSG_Update_Selected_References_Test, (void*)list); /* Also need to check that all dependents appear in the list. */ for ( i = obj->o_num_depend - 1 ; i >= 0 ; i-- ) { if ( CSG_Update_Selected_References_Test(obj->o_dependents[i].obj, (void*)list) ) Constraint_Remove_References(obj->o_dependents[i].obj, obj); } } void CSG_Add_Instances_To_Displayed(InstanceList new) { InstanceList new_list; new_list = Merge_Selection_Lists(csg_window.all_instances, new); Free_Selection_List(csg_window.all_instances); csg_window.all_instances = new_list; Update_Visible_List(&csg_window); Update_Projection_Extents(csg_window.all_instances); } void CSG_Remove_Instances_From_Displayed(InstanceList old) { InstanceList new_list; InstanceList temp_list; /* Need to undisplay it. */ /* This means removing its instances from the window's lists. */ for ( new_list = old ; new_list ; new_list = new_list->next ) { /* Unset its selected status. */ new_list->the_instance->o_flags &= ( ObjAll ^ ObjSelected ); /* Take it off the edit list. */ if (( temp_list = Find_Object_In_Instances(new_list->the_instance, csg_window.edit_instances)) ) Delete_Edit_Instance(&csg_window, temp_list); } /* Take it off the selection list. */ new_list = Remove_Selection_List(csg_window.all_instances, old); Free_Selection_List(csg_window.all_instances); csg_window.all_instances = new_list; /* Take it off the all list. */ new_list = Remove_Selection_List(csg_window.selected_instances, old); Free_Selection_List(csg_window.selected_instances); csg_window.selected_instances = new_list; View_Update(&csg_window, csg_window.all_instances, ViewNone); } /* ** CSG_Remove_Instances_From_Root(CSGNodePtr tree, CSGNodePtr root) ** Removes those instances that occur in tree from root's list. ** It DOES NOT remove them from any other list. */ void CSG_Remove_Instances_From_Root(CSGNodePtr tree, CSGNodePtr root) { InstanceList old = Build_Instance_List_From_Tree(tree); InstanceList temp; int root_index; for ( root_index = 0 ; displayed_trees[root_index].tree != root ; root_index++ ); if ( displayed_trees[root_index].displayed ) CSG_Remove_Instances_From_Displayed(old); temp = Remove_Selection_List(displayed_trees[root_index].instances, old); Free_Selection_List(displayed_trees[root_index].instances); Free_Selection_List(old); displayed_trees[root_index].instances = temp; } /* ** CSG_Add_Instances_To_Root(CSGNodePtr tree, CSGTreeList root) ** Adds those instances that occur in tree from root's list. ** If it is displayed, adds the instances to the relevant lists. */ void CSG_Add_Instances_To_Root(CSGNodePtr tree, CSGTreeList root) { InstanceList new_insts = Build_Instance_List_From_Tree(tree); Append_Instance_List(&(root->instances), new_insts); if ( root->displayed ) CSG_Add_Instances_To_Displayed(new_insts); } static InstanceList Build_Instance_List_From_Tree(CSGNodePtr tree) { InstanceList left_list; InstanceList right_list; InstanceList result; if ( ! tree ) return NULL; if ( tree->csg_op == csg_leaf_op ) { result = New(InstanceListElmt, 1); result->the_instance = tree->csg_instance; result->next = result->prev = NULL; return result; } left_list = Build_Instance_List_From_Tree(tree->csg_left_child); right_list = Build_Instance_List_From_Tree(tree->csg_right_child); Append_Instance_List(&left_list, right_list); return left_list; } /* void ** CSG_Reorder_Root_Widgets() ** This procedure is needed to solve an annoying problem _ I have no ** control over the order in which distinct trees appear in the tree widget, ** particularly when modifying trees introduces new trees and removes ** some old ones, and makes previously internal nodes root nodes. ** Anyway, the solution is to (kind of) sort the displayed trees. ** For each tree in turn, find the leftmost widget and do a swap if necessary. */ void CSG_Reorder_Root_Widgets() { Position x, min_x; int i, j, min_index; Arg arg; Widget swap; XtSetArg(arg, XtNx, &x); /* Match each displayed tree with the appropriate widget. */ for ( i = 0 ; i < num_displayed_trees - 1 ; i++ ) { XtGetValues(displayed_trees[i].tree->csg_widget, &arg, 1); min_x = x; min_index = i; for ( j = i ; j < num_displayed_trees ; j++ ) { XtGetValues(displayed_trees[j].tree->csg_widget, &arg, 1); if ( x < min_x ) { min_x = x; min_index = j; } } if ( min_index != i ) /* Swap widgets. */ { swap = displayed_trees[min_index].tree->csg_widget; displayed_trees[min_index].tree->csg_widget = displayed_trees[i].tree->csg_widget; displayed_trees[i].tree->csg_widget = swap; CSG_Set_Widget_Values(displayed_trees[min_index].tree); CSG_Set_Widget_Values(displayed_trees[i].tree); if ( displayed_trees[i].tree->csg_left_child ) XtVaSetValues( displayed_trees[i].tree->csg_left_child->csg_widget, XtNtreeParent, displayed_trees[i].tree->csg_widget, NULL); if ( displayed_trees[min_index].tree->csg_left_child ) XtVaSetValues( displayed_trees[min_index].tree->csg_left_child->csg_widget, XtNtreeParent, displayed_trees[min_index].tree->csg_widget, NULL); if ( displayed_trees[i].tree->csg_right_child ) XtVaSetValues( displayed_trees[i].tree->csg_right_child->csg_widget, XtNtreeParent, displayed_trees[i].tree->csg_widget, NULL); if ( displayed_trees[min_index].tree->csg_right_child ) XtVaSetValues( displayed_trees[min_index].tree->csg_right_child->csg_widget, XtNtreeParent, displayed_trees[min_index].tree->csg_widget, NULL); } } } static void CSG_Create_Tree_Widgets(CSGNodePtr tree) { if ( ! tree ) return; Create_CSG_Widget(tree); if ( tree->csg_op != csg_leaf_op ) { CSG_Create_Tree_Widgets(tree->csg_left_child); CSG_Create_Tree_Widgets(tree->csg_right_child); } } /* ** CSG_Insert_Existing_Tree(CSGNodePtr tree, Boolean display) ** Inserts a tree which already largely exists into the list of ** editable trees. Used to modify base types. */ void CSG_Insert_Existing_Tree(CSGNodePtr tree, Boolean display, BaseObjectPtr orig) { CSG_Create_Tree_Widgets(tree); Add_Displayed_Tree(tree, display, orig); } /* void ** CSG_Rename_Instance(ObjectInstancePtr, char*) ** Renames an instance from the CSG window. */ void CSG_Rename_Instance(ObjectInstancePtr inst, char *new_name) { CSGNodePtr node; Rename_Instance(inst, new_name); node = Find_Instance_In_Displayed(inst); XtVaSetValues(node->csg_widget, XtNlabel, new_name, NULL); XawTreeForceLayout(csg_tree_widget); changed_scene = TRUE; } /* void ** CSG_Reset() ** Resets (deletes all instances) the CSG window. */ void CSG_Reset() { CSGNodePtr node; int num_trees; int i; num_trees = num_displayed_trees; for ( i = 0 ; i < num_trees ; i++ ) { node = displayed_trees[0].tree; Delete_Displayed_Tree(node); CSG_Delete_Tree(node, TRUE); } XawTreeForceLayout(csg_tree_widget); View_Update(&csg_window, csg_window.all_instances, ViewNone); } void CSG_Delete_Original_Base(BaseObjectPtr base) { int i, j, k; for ( i = 0 ; i < num_displayed_trees ; i++ ) for ( j = 0 ; j < displayed_trees[i].num_orig_bases ; j++ ) if ( displayed_trees[i].orig_bases[j] == base ) for ( k = j + 1 ; k < displayed_trees[i].num_orig_bases ; k++ ) displayed_trees[i].orig_bases[k-1] = displayed_trees[i].orig_bases[k]; } static void CSG_Attribs_True_Callback(Widget w, XtPointer cl, XtPointer ca) { attribs_done = ATTRIBS_TRUE; } static void CSG_Attribs_False_Callback(Widget w, XtPointer cl, XtPointer ca) { attribs_done = ATTRIBS_FALSE; } static Widget CSG_Attribs_Create_Dialog() { Widget shell; Widget dialog; Arg args[5]; int n; shell = XtCreatePopupShell("Attribs", transientShellWidgetClass, main_window.shell, NULL, 0); /* Create the dialog widget to go inside the shell. */ n = 0; XtSetArg(args[n], XtNlabel, "Clear Attributes?"); n++; dialog = XtCreateManagedWidget("csgAttribsDialog", dialogWidgetClass, shell, args, n); /* Add the button at the bottom of the dialog. */ XawDialogAddButton(dialog, "Yes", CSG_Attribs_True_Callback, NULL); XawDialogAddButton(dialog, "No", CSG_Attribs_False_Callback, NULL); XtVaSetValues(XtNameToWidget(dialog, "label"), XtNborderWidth, 0, NULL); XtRealizeWidget(shell); return shell; } Boolean CSG_Set_Clear_Attribs() { static Widget clear_attribs_shell = NULL; XtAppContext context; XEvent event; if ( ! clear_attribs_shell ) clear_attribs_shell = CSG_Attribs_Create_Dialog(); attribs_done = ATTRIBS_CONTINUE; Position_Shell(clear_attribs_shell, FALSE); XtPopup(clear_attribs_shell, XtGrabExclusive); context = XtWidgetToApplicationContext(main_window.shell); while ( attribs_done == ATTRIBS_CONTINUE ) { XtAppNextEvent(context, &event); XtDispatchEvent(&event); } XtPopdown(clear_attribs_shell); return ( attribs_done == ATTRIBS_TRUE ); }