/* * $Header: /home/barad-dur/vision/forsyth/schenney/sced-0.94/c/RCS/csg_events.c,v 1.0 1997/05/06 20:30:22 schenney Exp $ * * $Log: csg_events.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_events.c: Functions for dealing with events in the csg tree window. */ #include #include #include #include #include #include #include #include #include #include #include #include #define Calculate_Motion_Rectangle(e) \ motion_rectangle.x = e->xmotion.x - motion_rectangle.width / 2;\ motion_rectangle.y = e->xmotion.y - motion_rectangle.height / 2; static void Create_Complete_Shell(); static void CSG_Complete_Name_Callback(Widget, XtPointer, XtPointer); static void CSG_Initialise_Preview(); static void CSG_Prepare_Preview(CSGNodePtr); static void CSG_Complete(CSGNodePtr, char*); extern Widget csg_display_label[2]; extern Widget csg_tree_viewport; static CSGEvent current_event = csg_none; static Widget current_widget; static Boolean drawing = FALSE; static Widget destination_widget; static XRectangle motion_rectangle; static GC motion_rect_gc = 0; static Boolean attach_after; static Boolean attach_left; static CSGOperation attach_op; static CSGNodePtr attach_sibling; static Widget complete_label_shell; static Widget complete_label_dialog; #define COMPLETE_RUN 0 #define COMPLETE_CANCEL 1 #define COMPLETE_DONE 2 static int complete_finished; static WindowInfo csg_preview_window; static BaseObject csg_preview_base; static ObjectInstance csg_preview_obj; static Boolean preview_initialised = FALSE; void CSG_Tree_Notify_Func(Widget w, XEvent *e, String *s, Cardinal *num) { if ( drawing ) { XDrawRectangles(XtDisplay(csg_tree_widget), XtWindow(csg_tree_widget), motion_rect_gc, &motion_rectangle, 1); drawing = FALSE; } if ( current_event == csg_attach ) { XBell(XtDisplay(csg_window.shell), 0); /* XtRemoveGrab(csg_tree_widget); */ XtRemoveGrab(csg_tree_viewport); current_event = csg_none; } if ( current_event == csg_move ) { CSGNodePtr destination_node; Position x; int i, min_dist; /* Find the node closest to the left. */ min_dist = MAX_SIGNED_SHORT; destination_node = NULL; for ( i = 0 ; i < num_displayed_trees ; i++ ) { XtVaGetValues(displayed_trees[i].tree->csg_widget, XtNx, &x, NULL); if ( e->xmotion.x - (int)x > 0 && e->xmotion.x - (int)x < min_dist ) destination_node = displayed_trees[i].tree; } /* Find the current widget's node. */ for ( i = 0 ; displayed_trees[i].tree->csg_widget != current_widget ; i++ ); CSG_Move_Tree(displayed_trees[i].tree, destination_node); /* XtRemoveGrab(csg_tree_widget); */ XtRemoveGrab(csg_tree_viewport); current_event = csg_none; } } void CSG_Tree_Motion_Func(Widget w, XEvent *e, String *s, Cardinal *num) { if ( drawing ) { XDrawRectangles(XtDisplay(csg_tree_widget), XtWindow(csg_tree_widget), motion_rect_gc, &motion_rectangle, 1); Calculate_Motion_Rectangle(e); XDrawRectangles(XtDisplay(csg_tree_widget), XtWindow(csg_tree_widget), motion_rect_gc, &motion_rectangle, 1); } } void CSG_Menu_Notify_Func(Widget w, XtPointer cl, XtPointer ca) { int i; if ( drawing ) { XDrawRectangles(XtDisplay(csg_tree_widget), XtWindow(csg_tree_widget), motion_rect_gc, &motion_rectangle, 1); drawing = FALSE; } if ( current_event == csg_attach ) { if ( w == current_widget ) { XBell(XtDisplay(csg_window.shell), 0); /* XtRemoveGrab(csg_tree_widget); */ XtRemoveGrab(csg_tree_viewport); current_event = csg_none; Popup_Error("Cannot Attach a tree to itself", csg_window.shell, "Error"); return; } else { XtVaSetValues(w, XtNmenuName, "AttachMenu", NULL); destination_widget = w; } } if ( current_event == csg_move ) { CSGNodePtr destination_node; int i; /* Find this widget's tree root. */ CSG_Node_Root(Find_Widget_In_Displayed(w), destination_node); /* Find the current widget's node. */ for ( i = 0 ; displayed_trees[i].tree->csg_widget != current_widget ; i++ ); CSG_Move_Tree(displayed_trees[i].tree, destination_node); /* XtRemoveGrab(csg_tree_widget); */ XtRemoveGrab(csg_tree_viewport); current_event = csg_none; } if ( current_event == csg_none ) { current_widget = w; /* Try to find it as a top level widget. */ for ( i = 0 ; i < num_displayed_trees && displayed_trees[i].tree->csg_widget != current_widget ; i++ ); if ( i != num_displayed_trees ) { /* Set the display flag. */ if ( displayed_trees[i].displayed ) { XtVaSetValues(csg_display_label[0], XtNlabel, "Hide", NULL); XtVaSetValues(csg_display_label[1], XtNlabel, "Hide", NULL); } else { XtVaSetValues(csg_display_label[0], XtNlabel, "Display", NULL); XtVaSetValues(csg_display_label[1], XtNlabel, "Display", NULL); } } } } void Init_Motion_Sequence(Widget w, XtPointer cl, XtPointer ca) { Position x, y; Dimension width, height; drawing = TRUE; current_event = (CSGEvent)cl; /* XtAddGrab(csg_tree_widget, TRUE, FALSE); */ XtAddGrab(csg_tree_viewport, TRUE, FALSE); if ( ! motion_rect_gc ) { XGCValues gc_vals; Colormap col_map; Pixel foreground_pixel, background_pixel; /* Get the colormap. */ XtVaGetValues(current_widget, XtNcolormap, &col_map, XtNforeground, &foreground_pixel, XtNbackground, &background_pixel, NULL); gc_vals.function = GXxor; if (DefaultDepthOfScreen(XtScreen(csg_window.shell)) == 1) gc_vals.foreground = foreground_pixel; else { XColor motion_color; /* Allocate the color for the drag rectangle. */ motion_color.red = MAX_UNSIGNED_SHORT; motion_color.green = 0; motion_color.blue = 0; XAllocColor(XtDisplay(csg_window.shell), col_map, &motion_color); gc_vals.foreground = motion_color.pixel; } /* Allocate the GCs. */ gc_vals.foreground ^= background_pixel; motion_rect_gc = XtGetGC(csg_window.shell, GCFunction | GCForeground, &gc_vals); } XtVaGetValues(current_widget, XtNx, &x, XtNy, &y, XtNwidth, &width, XtNheight, &height, NULL); motion_rectangle.x = (short)x; motion_rectangle.y = (short)y; motion_rectangle.width = (unsigned short)width; motion_rectangle.height = (unsigned short)height; XDrawRectangles(XtDisplay(csg_tree_widget), XtWindow(csg_tree_widget), motion_rect_gc, &motion_rectangle, 1); } void Combine_Menu_Callback(Widget w, XtPointer cl, XtPointer ca) { CSGNodePtr src, dest, dest_parent; int i; BaseObjectPtr *bases; int num_bases; /* XtRemoveGrab(csg_tree_widget); */ XtRemoveGrab(csg_tree_viewport); current_event = csg_none; for ( i = 0 ; displayed_trees[i].tree->csg_widget != current_widget ; i++ ); src = displayed_trees[i].tree; bases = displayed_trees[i].orig_bases; num_bases = displayed_trees[i].num_orig_bases; dest = Find_Widget_In_Displayed(destination_widget); /* Check for dest among src's children. */ CSG_Node_Root(dest, dest_parent); if ( src == dest_parent ) { XBell(XtDisplay(csg_window.shell), 0); return; } /* The src is at the root of the tree. Delete it from the displayed trees.*/ Delete_Displayed_Tree(src); src->csg_parent = NULL; /* Combine it using the appropriate operator. */ CSG_Add_Node(src, dest, (CSGOperation)cl, bases, num_bases); if ( num_bases ) free(bases); XawTreeForceLayout(csg_tree_widget); if ( src->csg_parent->csg_parent ) CSG_Check_Tree_Ordering(src->csg_parent->csg_parent); changed_scene = TRUE; } void CSG_Menu_Button_Up_Func(Widget w, XEvent *e, String *s, Cardinal *n) { CSGNodePtr node; if ( current_event == csg_attach && current_widget != w ) { node = Find_Widget_In_Displayed(destination_widget); destination_widget = w; if ( node->csg_op == csg_leaf_op ) { if ( node->csg_parent ) XtVaSetValues(destination_widget, XtNmenuName, "ExternalMenu", NULL); else XtVaSetValues(destination_widget, XtNmenuName, "ExternalRootMenu", NULL); } else { if ( node->csg_parent ) XtVaSetValues(destination_widget, XtNmenuName, "InternalMenu", NULL); else XtVaSetValues(destination_widget, XtNmenuName, "InternalRootMenu", NULL); } } } void Cancel_Combine_Menu_Callback(Widget w, XtPointer cl, XtPointer ca) { CSGNodePtr node; node = Find_Widget_In_Displayed(destination_widget); /* XtRemoveGrab(csg_tree_widget); */ XtRemoveGrab(csg_tree_viewport); current_event = csg_none; if ( node->csg_op == csg_leaf_op ) { if ( node->csg_parent ) XtVaSetValues(destination_widget, XtNmenuName, "ExternalMenu",NULL); else XtVaSetValues(destination_widget, XtNmenuName, "ExternalRootMenu", NULL); } else { if ( node->csg_parent ) XtVaSetValues(destination_widget, XtNmenuName, "InternalMenu", NULL); else XtVaSetValues(destination_widget, XtNmenuName, "InternalRootMenu", NULL); } } void CSG_Display_Callback(Widget w, XtPointer cl, XtPointer ca) { int i; for ( i = 0 ; displayed_trees[i].tree->csg_widget != current_widget ; i++ ); if ( displayed_trees[i].displayed ) { CSG_Remove_Instances_From_Displayed(displayed_trees[i].instances); displayed_trees[i].displayed = FALSE; } else { CSG_Add_Instances_To_Displayed(displayed_trees[i].instances); displayed_trees[i].displayed = TRUE; } } char* Complete_Get_Name(char *default_name) { XtAppContext context; XEvent event; if ( ! complete_label_shell ) Create_Complete_Shell(); XtVaSetValues(complete_label_dialog, XtNvalue, default_name, NULL); Position_Shell(complete_label_shell, FALSE); XtPopup(complete_label_shell, XtGrabExclusive); /* Get and dispatch events. */ complete_finished = COMPLETE_RUN; context = XtWidgetToApplicationContext(main_window.shell); while ( ! complete_finished ) { XtAppNextEvent(context, &event); XtDispatchEvent(&event); } XtPopdown(complete_label_shell); if ( complete_finished == COMPLETE_DONE ) return XawDialogGetValueString(complete_label_dialog); else return NULL; } void CSG_Complete_Callback(Widget w, XtPointer cl, XtPointer ca) { CSGNodePtr tree; int i = 0; char default_name[16]; char *label; attach_after = ( cl ? TRUE : FALSE ); if ( ! attach_after ) { for ( i = 0 ; displayed_trees[i].tree->csg_widget != current_widget ; i++ ); tree = displayed_trees[i].tree; } else tree = Find_Widget_In_Displayed(current_widget); /* Prompt for the name (and give an opportunity to cancel). */ sprintf(default_name, "CSG_%d", object_count[csg_obj]); label = Complete_Get_Name(default_name); if ( ! label ) return; CSG_Complete(tree, label); } static void CSG_Complete_Cancel_Callback(Widget w, XtPointer cl, XtPointer ca) { complete_finished = COMPLETE_CANCEL; } static void CSG_Complete_Name_Callback(Widget w, XtPointer cl, XtPointer ca) { complete_finished = COMPLETE_DONE; } static void CSG_Modify_Dependent_Instances(BaseObjectPtr old, BaseObjectPtr new) { InstanceList main_instances = NULL; InstanceList csg_instances = NULL; InstanceList agg_instances = NULL; int i; for ( i = old->b_num_instances - 1 ; i >= 0 ; i-- ) { if ( old->b_instances[i]->o_flags & ObjPending ) { Base_Change(old->b_instances[i], new, FALSE, NULL); if ( Find_Object_In_Instances(old->b_instances[i], main_window.all_instances) ) Insert_Element(&main_instances, old->b_instances[i]); else if ( Find_Object_In_Instances(old->b_instances[i], csg_window.all_instances) ) Insert_Element(&csg_instances, old->b_instances[i]); else if ( Find_Object_In_Instances(old->b_instances[i], agg_window.all_instances) ) Insert_Element(&agg_instances, old->b_instances[i]); } } View_Update(&main_window, main_instances, CalcView); View_Update(&csg_window, csg_instances, CalcView); if ( agg_window.shell ) View_Update(&agg_window, agg_instances, CalcView); Update_Projection_Extents(main_instances); Update_Projection_Extents(csg_instances); Update_Projection_Extents(agg_instances); } BaseObjectPtr CSG_Complete_Tree(CSGNodePtr tree, char *label, Boolean is_root) { CSGTreeRoot old_root; BaseObjectPtr new_base; WireframePtr *main_wireframe = New(WireframePtr, 1); WireframePtr *simple_wireframe = New(WireframePtr, 1); int ref_num; Boolean use_full; int i; main_wireframe[0] = CSG_Generate_Wireframe(tree, 0, TRUE); if ( ! main_wireframe[0] ) return NULL; simple_wireframe[0] = Wireframe_Simplify(main_wireframe[0], TRUE); Edge_Table_Build(simple_wireframe[0]); if (( ref_num = Select_Base_Reference(simple_wireframe, tree, &use_full)) == -1 ) { Wireframe_Destroy(main_wireframe[0]); Wireframe_Destroy(simple_wireframe[0]); free(main_wireframe); free(simple_wireframe); return NULL; } new_base = Add_CSG_Base_Object(tree, label, 0, main_wireframe, simple_wireframe, ref_num, use_full); CSG_Free_Widgets(tree); CSG_Update_Reference_Constraints(tree, tree); if ( is_root ) old_root = Delete_Displayed_Tree(tree); else CSG_Remove_Node(tree); object_count[csg_obj]++; Base_Add_Select_Option(new_base); if ( is_root ) { /* Modify any instances. */ for ( i = 0 ; i < old_root.num_orig_bases ; i++ ) CSG_Modify_Dependent_Instances(old_root.orig_bases[i], new_base); if ( old_root.num_orig_bases ) free(old_root.orig_bases); } return new_base; } static void CSG_Complete(CSGNodePtr tree, char *label) { BaseObjectPtr new_base; Cursor time_cursor; /* Change the cursor. */ time_cursor = XCreateFontCursor(XtDisplay(csg_window.shell), XC_watch); XDefineCursor(XtDisplay(csg_window.shell), XtWindow(csg_window.shell), time_cursor); XFlush(XtDisplay(csg_window.shell)); /* We now have a name, move on with it. */ if ( attach_after ) { if ( tree == tree->csg_parent->csg_left_child ) { attach_left = TRUE; attach_sibling = tree->csg_parent->csg_right_child; } else { attach_left = FALSE; attach_sibling = tree->csg_parent->csg_left_child; } attach_op = tree->csg_parent->csg_op; } new_base = CSG_Complete_Tree(tree, label, ! attach_after); if ( new_base && attach_after ) Create_New_Object_From_Base(&csg_window, new_base, TRUE); attach_after = FALSE; /* Change the cursor back. */ XDefineCursor(XtDisplay(csg_window.shell), XtWindow(csg_window.shell),None); XFreeCursor(XtDisplay(csg_window.shell), time_cursor); } void CSG_Attach_Instance(ObjectInstancePtr inst) { CSGNodePtr new_node; new_node = Create_CSG_Node(NULL, inst, csg_leaf_op); Create_CSG_Widget(new_node); CSG_Add_Node(new_node, attach_sibling, attach_op, NULL, 0); if ( attach_left ) CSG_Reorder_Children(new_node->csg_parent); XawTreeForceLayout(csg_tree_widget); } void CSG_Complete_Action_Func(Widget w, XEvent *e,String *s, Cardinal *num) { CSG_Complete_Name_Callback(w, NULL, NULL); } static void Create_Complete_Shell() { Arg args[5]; int n; XtSetArg(args[0], XtNallowShellResize, TRUE); complete_label_shell = XtCreatePopupShell("Complete", transientShellWidgetClass, main_window.shell, args, 1); /* Create the dialog widget to go inside the shell. */ n = 0; XtSetArg(args[n], XtNlabel, "Object Name:"); n++; XtSetArg(args[n], XtNvalue, ""); n++; complete_label_dialog = XtCreateManagedWidget("completeDialog", dialogWidgetClass, complete_label_shell, args, n); /* Add the buttons at the bottom of the dialog. */ XawDialogAddButton(complete_label_dialog, "Complete", CSG_Complete_Name_Callback, (XtPointer)TRUE); XawDialogAddButton(complete_label_dialog, "Cancel", CSG_Complete_Cancel_Callback, NULL); XtOverrideTranslations(XtNameToWidget(complete_label_dialog, "value"), XtParseTranslationTable(":Return: csg_complete()")); XtRealizeWidget(complete_label_shell); } void CSG_Break_Callback(Widget w, XtPointer cl, XtPointer ca) { CSGNodePtr node; CSGNodePtr root; CSGNodePtr parent = NULL; int i; node = Find_Widget_In_Displayed(current_widget); CSG_Node_Root(node, root); for ( i = 0 ; displayed_trees[i].tree != root ; i++ ); if ( node->csg_parent ) parent = node->csg_parent->csg_parent; CSG_Remove_Node(node); Add_Displayed_Tree(node, displayed_trees[i].displayed, NULL); XawTreeForceLayout(csg_tree_widget); if ( parent ) CSG_Check_Tree_Ordering(parent); changed_scene = TRUE; } void CSG_Delete_Callback(Widget w, XtPointer cl, XtPointer ca) { CSGNodePtr node; CSGNodePtr parent = NULL; CSGTreeRoot old_root; node = Find_Widget_In_Displayed(current_widget); if ( node->csg_parent ) { parent = node->csg_parent->csg_parent; CSG_Remove_Node(node); } else { old_root = Delete_Displayed_Tree(node); if ( old_root.num_orig_bases ) free(old_root.orig_bases); } CSG_Delete_Tree(node, TRUE); XawTreeForceLayout(csg_tree_widget); if ( parent ) CSG_Check_Tree_Ordering(parent); changed_scene = TRUE; } void CSG_Copy_Callback(Widget w, XtPointer cl, XtPointer ca) { CSGNodePtr src = Find_Widget_In_Displayed(current_widget); CSGNodePtr new_tree = CSG_Copy_Tree(src, NULL); Add_Displayed_Tree(new_tree, FALSE, NULL); XawTreeForceLayout(csg_tree_widget); changed_scene = TRUE; } void CSG_Reorder_Callback(Widget w, XtPointer cl, XtPointer ca) { CSG_Reorder_Children(Find_Widget_In_Displayed(current_widget)); XawTreeForceLayout(csg_tree_widget); changed_scene = TRUE; } void CSG_Export_Callback(Widget w, XtPointer cl, XtPointer ca) { Base_Select_Popup(csg_obj, csg_select_export); } void CSG_Modify_Existing_Callback(Widget w, XtPointer cl, XtPointer ca) { Base_Select_Popup(csg_obj, csg_select_edit); } void CSG_Copy_Existing_Callback(Widget w, XtPointer cl, XtPointer ca) { Base_Select_Popup(csg_obj, csg_select_copy); } void CSG_Delete_Existing_Callback(Widget w, XtPointer cl, XtPointer ca) { Base_Select_Popup(csg_obj, csg_select_delete); } static Boolean CSG_Update_Reference_Constraints_Test(ObjectInstancePtr obj, void *ptr) { return ( ! Find_Instance_In_Tree(obj, (CSGNodePtr)ptr) ); } void CSG_Update_Reference_Constraints(CSGNodePtr target, CSGNodePtr tree) { int i; if ( ! target ) return; if ( target->csg_op == csg_leaf_op ) { Constraint_Change_References(target->csg_instance, CSG_Update_Reference_Constraints_Test, (void*)tree); for ( i = target->csg_instance->o_num_depend - 1 ; i >= 0 ; i-- ) if ( CSG_Update_Reference_Constraints_Test( target->csg_instance->o_dependents[i].obj, (void*)tree) ) Constraint_Remove_References( target->csg_instance->o_dependents[i].obj, target->csg_instance); return; } CSG_Update_Reference_Constraints(target->csg_left_child, tree); CSG_Update_Reference_Constraints(target->csg_right_child, tree); } void CSG_Preview_Callback(Widget w, XtPointer cl, XtPointer ca) { CSGNodePtr tree = Find_Widget_In_Displayed(current_widget); if ( ! preview_initialised ) CSG_Initialise_Preview(); CSG_Prepare_Preview(tree); Preview_Callback(NULL, (XtPointer)&csg_preview_window, NULL); Preview_Sensitize(FALSE); } static void CSG_Prepare_Preview(CSGNodePtr tree) { if ( csg_preview_window.selected_instances ) free(csg_preview_window.selected_instances); csg_preview_window = csg_window; csg_preview_window.selected_instances = New(InstanceListElmt, 1); csg_preview_window.selected_instances->next = csg_preview_window.selected_instances->prev = NULL; csg_preview_window.selected_instances->the_instance = &csg_preview_obj; csg_preview_base.b_struct = (void*)tree; Calculate_CSG_Bounds(tree); } static void CSG_Initialise_Preview() { csg_preview_window.selected_instances = NULL; csg_preview_base.b_label = "CSGPreviewObj"; csg_preview_base.b_class = csg_obj; csg_preview_obj.o_label = "CSGPreviewObj"; csg_preview_obj.o_parent = &csg_preview_base; Identity_Transform(csg_preview_obj.o_transform); NewIdentityMatrix(csg_preview_obj.o_inverse); csg_preview_obj.o_attribs = (void*)&(sced_preferences.default_attributes); preview_initialised = TRUE; }