|  | @@ -26,6 +26,11 @@ laPropContainer* LA_PC_IDN_VALUES;
 | 
	
		
			
				|  |  |  laPropContainer* LA_PC_IDN_MATRIX;
 | 
	
		
			
				|  |  |  laPropContainer* LA_PC_IDN_MATH;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +laNodeCategory* LA_NODE_CATEGORY_INPUT;
 | 
	
		
			
				|  |  | +laNodeCategory* LA_NODE_CATEGORY_MATH;
 | 
	
		
			
				|  |  | +laNodeCategory* LA_NODE_CATEGORY_ROUTE;
 | 
	
		
			
				|  |  | +laNodeCategory* LA_NODE_CATEGORY_DRIVER;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #define LA_IDN_CONTROLLER_RESET_SOCKET(ns)\
 | 
	
		
			
				|  |  |      {ns->IntVal[0]=0; ns->Out->DataType=LA_PROP_INT; ns->Offset=0; ns->Out->Data=&ns->IntVal;}
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -113,7 +118,7 @@ void IDN_InputVisualizeDestroy(laInputVisualizerNode* n){
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  int IDN_InputVisualizeVisit(laInputVisualizerNode* n, laListHandle* l){
 | 
	
		
			
				|  |  |      n->Base.Eval=LA_DAG_FLAG_TEMP;
 | 
	
		
			
				|  |  | -    if(n->In->Source){ laBaseNode* sn=n->In->Source->Parent;LA_VISIT_NODE(sn); }
 | 
	
		
			
				|  |  | +    if(LA_SRC_AND_PARENT(n->In)){ laBaseNode* sn=n->In->Source->Parent;LA_VISIT_NODE(sn); }
 | 
	
		
			
				|  |  |      n->Base.Eval=LA_DAG_FLAG_PERM;
 | 
	
		
			
				|  |  |      lstAppendPointer(l, n);
 | 
	
		
			
				|  |  |      return LA_DAG_FLAG_PERM;
 | 
	
	
		
			
				|  | @@ -486,10 +491,11 @@ int OPINV_RebuildInputMapping(laOperator* a, laEvent *e){
 | 
	
		
			
				|  |  |      return LA_FINISHED;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -laBaseNode* la_CreateInputMapperNode(laNodeRack* ir, laBaseNodeType* NodeType){
 | 
	
		
			
				|  |  | +laBaseNode* la_CreateNode(laNodeRack* ir, laBaseNodeType* NodeType){
 | 
	
		
			
				|  |  |      laBaseNode* bn=memAcquire(NodeType->NodeSize);
 | 
	
		
			
				|  |  |      bn->Type=NodeType; NodeType->Init(bn); lstAppendItem(&ir->Nodes, bn); bn->InRack=ir;
 | 
	
		
			
				|  |  | -    laNotifyUsers("la.input_mapping"); laRecordAndPush(0,"la.input_mapping","Add node", 0);
 | 
	
		
			
				|  |  | +    if(ir->RackType==LA_RACK_TYPE_INPUT){ laNotifyUsers("la.input_mapping"); laRecordAndPush(0,"la.input_mapping","Add node", 0); }
 | 
	
		
			
				|  |  | +    else{ laNotifyUsers("la.drivers"); laRecordAndPush(0,"la.drivers","Add node", 0); }
 | 
	
		
			
				|  |  |      return bn;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  void la_DestroyInputMapperNode(laBaseNode* bn){
 | 
	
	
		
			
				|  | @@ -498,45 +504,42 @@ void la_DestroyInputMapperNode(laBaseNode* bn){
 | 
	
		
			
				|  |  |      memFree(bn);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -int OPINV_AddInputMapperNode(laOperator* a, laEvent *e){
 | 
	
		
			
				|  |  | +int OPINV_AddNode(laOperator* a, laEvent *e){
 | 
	
		
			
				|  |  |      laNodeRack* ir=a->This?a->This->EndInstance:0; if(!ir) return LA_CANCELED;
 | 
	
		
			
				|  |  |      laBaseNodeType* bnt=0;
 | 
	
		
			
				|  |  | +    if(!MAIN.CurrentNodeCategory) MAIN.CurrentNodeCategory=MAIN.NodeCategories.pFirst;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    char* target=strGetArgumentString(a->ExtraInstructionsP,"target");
 | 
	
		
			
				|  |  | +    if(!target || strSame(target,"INPUT")){ MAIN.FilterNodeCategory=LA_RACK_TYPE_INPUT;
 | 
	
		
			
				|  |  | +        if(!(MAIN.CurrentNodeCategory->For&LA_RACK_TYPE_INPUT)) MAIN.CurrentNodeCategory=LA_NODE_CATEGORY_INPUT; }
 | 
	
		
			
				|  |  | +    else{ MAIN.FilterNodeCategory=LA_RACK_TYPE_DRIVER;
 | 
	
		
			
				|  |  | +        if(!(MAIN.CurrentNodeCategory->For&LA_RACK_TYPE_DRIVER)) MAIN.CurrentNodeCategory=LA_NODE_CATEGORY_DRIVER; }
 | 
	
		
			
				|  |  |      char* type=strGetArgumentString(a->ExtraInstructionsP,"type");
 | 
	
		
			
				|  |  | -    if(!type){ laEnableOperatorPanel(a,a->This,e->x,e->y,200,200,0,0,0,0,0,0,0,0,e); return LA_RUNNING; }
 | 
	
		
			
				|  |  | -    elif(strSame(type, "KEYBOARD")){}
 | 
	
		
			
				|  |  | -    elif(strSame(type, "MOUSE")){}
 | 
	
		
			
				|  |  | -    elif(strSame(type, "CONTROLLER")){ la_CreateInputMapperNode(ir, &LA_IDN_CONTROLLER); }
 | 
	
		
			
				|  |  | -    elif(strSame(type, "VISUALIZER")){ la_CreateInputMapperNode(ir, &LA_IDN_VISUALIZER); }
 | 
	
		
			
				|  |  | -    elif(strSame(type, "SPLIT")){ la_CreateInputMapperNode(ir, &LA_IDN_SPLIT); }
 | 
	
		
			
				|  |  | -    elif(strSame(type, "SWITCH")){ la_CreateInputMapperNode(ir, &LA_IDN_SWITCH); }
 | 
	
		
			
				|  |  | -    elif(strSame(type, "COMBINE")){ la_CreateInputMapperNode(ir, &LA_IDN_COMBINE); }
 | 
	
		
			
				|  |  | -    elif(strSame(type, "VALUES")){ la_CreateInputMapperNode(ir, &LA_IDN_VALUES); }
 | 
	
		
			
				|  |  | -    elif(strSame(type, "MATRIX")){ la_CreateInputMapperNode(ir, &LA_IDN_MATRIX); }
 | 
	
		
			
				|  |  | -    elif(strSame(type, "MATH")){ la_CreateInputMapperNode(ir, &LA_IDN_MATH); }
 | 
	
		
			
				|  |  | -    elif(MAIN.ExtraGetInputNodeType && (bnt=MAIN.ExtraGetInputNodeType(type))){ la_CreateInputMapperNode(ir, bnt); }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +    if(!type){ laEnableOperatorPanel(a,a->This,e->x-LA_RH*4,e->y-LA_RH,200,200,0,0,LA_RH*15,0,0,0,0,0,e); return LA_RUNNING; }
 | 
	
		
			
				|  |  | +    else{
 | 
	
		
			
				|  |  | +        for(int i=0;i<MAIN.NodeTypeNext;i++){
 | 
	
		
			
				|  |  | +            if(strSame(MAIN.NodeTypes[i]->TypeName,type)){ la_CreateNode(ir, MAIN.NodeTypes[i]); }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |      return LA_FINISHED;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | -void laui_AddInputMapperNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
 | 
	
		
			
				|  |  | -    laColumn* c=laFirstColumn(uil);
 | 
	
		
			
				|  |  | -    if(MAIN.ExtraAddInputNodes){
 | 
	
		
			
				|  |  | -        MAIN.ExtraAddInputNodes(uil,This,Extra,0,0);
 | 
	
		
			
				|  |  | +int OPMOD_AddNode(laOperator* a, laEvent *e){
 | 
	
		
			
				|  |  | +    laNodeRack* r=a->This->EndInstance;
 | 
	
		
			
				|  |  | +    if(a->ConfirmData){
 | 
	
		
			
				|  |  | +        if(a->ConfirmData->StrData){
 | 
	
		
			
				|  |  | +            for(int i=0;i<MAIN.NodeTypeNext;i++){
 | 
	
		
			
				|  |  | +                if(strSame(MAIN.NodeTypes[i]->TypeName,a->ConfirmData->StrData)){ la_CreateNode(r, MAIN.NodeTypes[i]); break; }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return LA_FINISHED;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    laShowLabel(uil,c,"Sources:",0,0);
 | 
	
		
			
				|  |  | -    laShowItemFull(uil,c,This,"add_node_input",0,"type=KEYBOARD;text=Keyboard",0,0);
 | 
	
		
			
				|  |  | -    laShowItemFull(uil,c,This,"add_node_input",0,"type=MOUSE;text=Mouse",0,0);
 | 
	
		
			
				|  |  | -    laShowItemFull(uil,c,This,"add_node_input",0,"type=CONTROLLER;text=Controller",0,0);
 | 
	
		
			
				|  |  | -    laShowLabel(uil,c,"Operations:",0,0);
 | 
	
		
			
				|  |  | -    laShowItemFull(uil,c,This,"add_node_input",0,"type=SPLIT;text=Split",0,0);
 | 
	
		
			
				|  |  | -    laShowItemFull(uil,c,This,"add_node_input",0,"type=SWITCH;text=Switch",0,0);
 | 
	
		
			
				|  |  | -    laShowItemFull(uil,c,This,"add_node_input",0,"type=COMBINE;text=Combine",0,0);
 | 
	
		
			
				|  |  | -    laShowItemFull(uil,c,This,"add_node_input",0,"type=VALUES;text=Values",0,0);
 | 
	
		
			
				|  |  | -    laShowLabel(uil,c,"Math:",0,0);
 | 
	
		
			
				|  |  | -    laShowItemFull(uil,c,This,"add_node_input",0,"type=MATH;text=Math",0,0);
 | 
	
		
			
				|  |  | -    laShowItemFull(uil,c,This,"add_node_input",0,"type=MATRIX;text=Matrix",0,0);
 | 
	
		
			
				|  |  | -    laShowLabel(uil,c,"Visualizations:",0,0);
 | 
	
		
			
				|  |  | -    laShowItemFull(uil,c,This,"add_node_input",0,"type=VISUALIZER;text=Visualizer",0,0);
 | 
	
		
			
				|  |  | +    return LA_RUNNING;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void laui_AddNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
 | 
	
		
			
				|  |  | +    laColumn* c=laFirstColumn(uil),*cl,*cr; laSplitColumn(uil,c,0.4); cl=laLeftColumn(c,4); cr=laRightColumn(c,0);
 | 
	
		
			
				|  |  | +    laShowItemFull(uil,cl,0,"la.node_categories",LA_WIDGET_COLLECTION,"feedback=NONE;",0,0);
 | 
	
		
			
				|  |  | +    laUiItem* g=laMakeEmptyGroup(uil,cr,"Nodes",0); laUiList* gu=g->Page; laColumn* gc=laFirstColumn(gu); g->Flags|=LA_UI_FLAGS_NO_DECAL|LA_UI_FLAGS_NO_GAP;
 | 
	
		
			
				|  |  | +    laShowItemFull(gu,gc,0,"la.node_categories",LA_WIDGET_COLLECTION_SINGLE,0,laui_NodeCategory,0)->Flags|=LA_UI_FLAGS_NO_DECAL;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  int OPINV_MoveNodeToRack(laOperator* a, laEvent *e){
 | 
	
	
		
			
				|  | @@ -639,9 +642,12 @@ laBoxedTheme* laget_NodeGetTheme(laNodeRack* rack_unused, laBaseNode* n){
 | 
	
		
			
				|  |  |      return 0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void laRegisterNode(laBaseNodeType* type, laPropContainer* pc, laBaseNodeInitF init, laBaseNodeDestroyF destroy, laBaseNodeVisitF visit, laBaseNodeEvalF eval, int nodesize, char* udf_string){
 | 
	
		
			
				|  |  | +void laRegisterNode(laBaseNodeType* type, laPropContainer* pc,
 | 
	
		
			
				|  |  | +                    laBaseNodeInitF init, laBaseNodeDestroyF destroy, laBaseNodeVisitF visit, laBaseNodeEvalF eval,
 | 
	
		
			
				|  |  | +                    int nodesize, char* udf_string, char* type_string, char* UiText, int icon){
 | 
	
		
			
				|  |  |      arrEnsureLength(&MAIN.NodeTypes, MAIN.NodeTypeNext, &MAIN.NodeTypeMax, sizeof(laBaseNode*));
 | 
	
		
			
				|  |  |      type->Init = init; type->Destroy = destroy; type->Visit=visit; type->Eval=eval; type->NodeSize=nodesize; type->pc=pc;
 | 
	
		
			
				|  |  | +    type->TypeName=type_string;type->Name=UiText;type->Icon=icon;
 | 
	
		
			
				|  |  |      MAIN.NodeTypes[MAIN.NodeTypeNext]=type; MAIN.NodeTypeNext++;
 | 
	
		
			
				|  |  |      la_UDFAppendSharedTypePointer(udf_string, type);
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -662,8 +668,8 @@ void la_RegisterInputMapperOperators(){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      laCreateOperatorType("LA_add_input_mapping_page", "Add Page", "Add a page for inpur mapping", 0,0,0,OPINV_AddInputMapperPage,0,'+',0);
 | 
	
		
			
				|  |  |      laCreateOperatorType("LA_add_rack", "Add Rack", "Add a rack for nodes", 0,0,0,OPINV_AddNodesRack,0,'+',0);
 | 
	
		
			
				|  |  | -    at=laCreateOperatorType("LA_add_input_mapper_node", "Add Node", "Add a input mapper node",0,0,0,OPINV_AddInputMapperNode,OPMOD_FinishOnData,'+',0);
 | 
	
		
			
				|  |  | -    at->UiDefine=laui_AddInputMapperNode;
 | 
	
		
			
				|  |  | +    at=laCreateOperatorType("OPINV_AddNode", "Add Node", "Add a node to the rack",0,0,0,OPINV_AddNode,OPMOD_AddNode,'+',0);
 | 
	
		
			
				|  |  | +    at->UiDefine=laui_AddNode;
 | 
	
		
			
				|  |  |      laCreateOperatorType("LA_input_mapping_rebuild", "Rebuild Input Mapping", "Rebuild input mapping for evaluation",0,0,0,OPINV_RebuildInputMapping,0,L'⭮',0);
 | 
	
		
			
				|  |  |      laCreateOperatorType("LA_move_node_to_rack", "Move Node", "Move node to another rack",0,0,0,OPINV_MoveNodeToRack,0,0,0);
 | 
	
		
			
				|  |  |      laCreateOperatorType("LA_delete_node", "Delete Node", "Delete this node",0,0,0,OPINV_DeleteNode,0,0,0);
 | 
	
	
		
			
				|  | @@ -678,8 +684,7 @@ void la_RegisterInputMapperOperators(){
 | 
	
		
			
				|  |  |      laSubGroupExtraFunctions(p,0,laget_NodeGetTheme,laget_BaseNodeGap);
 | 
	
		
			
				|  |  |      laAddSubGroup(pc,"parent_page","Parent Page","Parent page of this rack","la_rack_page",0,0,0,offsetof(laNodeRack,ParentPage),0,0,0,0,0,0,0,LA_UDF_REFER);
 | 
	
		
			
				|  |  |      laAddIntProperty(pc,"type", "Type", "Type of the rack", 0,0,0,0,0,0,0,0,offsetof(laNodeRack,RackType),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
 | 
	
		
			
				|  |  | -    laAddOperatorProperty(pc,"add_node_input","Add Node","Add a node into this rack","LA_add_input_mapper_node",'+',0);
 | 
	
		
			
				|  |  | -    laAddOperatorProperty(pc,"add_node_driver","Add Node","Add a node into this rack","LA_add_driver_node",'+',0);
 | 
	
		
			
				|  |  | +    laAddOperatorProperty(pc,"add_node","Add Node","Add a node into this rack","OPINV_AddNode",'+',0);
 | 
	
		
			
				|  |  |      laAddOperatorProperty(pc,"insert_rack","Insert Rack","Insert a rack","LA_insert_rack",'+',0);
 | 
	
		
			
				|  |  |      laAddOperatorProperty(pc,"move","Move Rack","Move this rack","LA_move_rack",0,0);
 | 
	
		
			
				|  |  |      laAddOperatorProperty(pc,"delete","Delete Rack","Delete this rack","LA_delete_rack",0,0);
 | 
	
	
		
			
				|  | @@ -708,9 +713,9 @@ void la_RegisterInputMapperOperators(){
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      pc=laAddPropertyContainer("la_input_controller_node_socket", "Controller Socket", "One value from a controller output",0,0,sizeof(laInputControllerNodeSocket),0,0,1|LA_PROP_OTHER_ALLOC);
 | 
	
		
			
				|  |  |      laAddStringProperty(pc,"which","Which","Select which output from the controller",0,0,0,0,1,offsetof(laInputControllerNodeSocket,Which),0,0,laset_InputControllerNodeSocketWhich,0,LA_AS_IDENTIFIER);
 | 
	
		
			
				|  |  | -    laAddFloatProperty(pc,"axis", "🡘", "Axis value", LA_WIDGET_VALUE_METER,0,0,1,-1,0,0,0,offsetof(laInputControllerNodeSocket,RealVal),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
 | 
	
		
			
				|  |  | -    laAddFloatProperty(pc,"axis2d", "2D Axis", "2D Axis value", LA_WIDGET_VALUE_METER,0,0,1,-1,0,0,0,offsetof(laInputControllerNodeSocket,RealVal),0,0,2,0,0,0,0,0,0,0,LA_READ_ONLY);
 | 
	
		
			
				|  |  | -    p=laAddEnumProperty(pc,"switch", "SW", "Switch value", LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(laInputControllerNodeSocket,IntVal),0,0,0,laget_SocketEnumArrayLength,0,0,0,0,0,LA_READ_ONLY);
 | 
	
		
			
				|  |  | +    laAddFloatProperty(pc,"axis", "🡘", "Axis value", LA_WIDGET_VALUE_METER,0,0,1,-1,0,0,0,offsetof(laInputControllerNodeSocket,RealVal),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY|LA_UDF_IGNORE);
 | 
	
		
			
				|  |  | +    laAddFloatProperty(pc,"axis2d", "2D Axis", "2D Axis value", LA_WIDGET_VALUE_METER,0,0,1,-1,0,0,0,offsetof(laInputControllerNodeSocket,RealVal),0,0,2,0,0,0,0,0,0,0,LA_READ_ONLY|LA_UDF_IGNORE);
 | 
	
		
			
				|  |  | +    p=laAddEnumProperty(pc,"switch", "SW", "Switch value", LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(laInputControllerNodeSocket,IntVal),0,0,0,laget_SocketEnumArrayLength,0,0,0,0,0,LA_READ_ONLY|LA_UDF_IGNORE);
 | 
	
		
			
				|  |  |      laAddEnumItemAs(p,"IDLE", "Idle", "Button is not pressed", 0, 0);
 | 
	
		
			
				|  |  |      laAddEnumItemAs(p,"ACTIVE", "Active", "Button is pressed", 1, 0);
 | 
	
		
			
				|  |  |      laAddSubGroup(pc, "out", "Out","Output value","la_out_socket",0,0,0,offsetof(laInputControllerNodeSocket,Out),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
	
		
			
				|  | @@ -719,9 +724,9 @@ void la_RegisterInputMapperOperators(){
 | 
	
		
			
				|  |  |      LA_PC_IDN_VISUALIZER=pc; laPropContainerExtraFunctions(pc,0,0,0,laui_DefaultNodeOperationsPropUiDefine);
 | 
	
		
			
				|  |  |      laAddSubGroup(pc,"base","Base","Base node","la_base_node",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);
 | 
	
		
			
				|  |  |      laAddSubGroup(pc, "in", "In","Input value","la_in_socket",0,0,0,offsetof(laInputVisualizerNode,In),0,0,0,0,0,0,0,LA_UDF_SINGLE);
 | 
	
		
			
				|  |  | -    laAddFloatProperty(pc,"axis", "🡘", "Axis value", LA_WIDGET_VALUE_METER,0,0,1,-1,0,0,0,offsetof(laInputVisualizerNode,RealVal),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY);
 | 
	
		
			
				|  |  | -    laAddFloatProperty(pc,"axis2d", "2D Axis", "2D Axis value", LA_WIDGET_VALUE_METER_2D,0,0,1,-1,0,0,0,offsetof(laInputVisualizerNode,RealVal),0,0,2,0,0,0,0,0,0,0,LA_READ_ONLY);
 | 
	
		
			
				|  |  | -    p=laAddEnumProperty(pc,"switch", "SW", "Switch value", LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(laInputVisualizerNode,IntVal),0,0,0,laget_VisualizerArrayLength,0,0,0,0,0,LA_READ_ONLY);
 | 
	
		
			
				|  |  | +    laAddFloatProperty(pc,"axis", "🡘", "Axis value", LA_WIDGET_VALUE_METER,0,0,1,-1,0,0,0,offsetof(laInputVisualizerNode,RealVal),0,0,0,0,0,0,0,0,0,0,LA_READ_ONLY|LA_UDF_IGNORE);
 | 
	
		
			
				|  |  | +    laAddFloatProperty(pc,"axis2d", "2D Axis", "2D Axis value", LA_WIDGET_VALUE_METER_2D,0,0,1,-1,0,0,0,offsetof(laInputVisualizerNode,RealVal),0,0,2,0,0,0,0,0,0,0,LA_READ_ONLY|LA_UDF_IGNORE);
 | 
	
		
			
				|  |  | +    p=laAddEnumProperty(pc,"switch", "SW", "Switch value", LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(laInputVisualizerNode,IntVal),0,0,0,laget_VisualizerArrayLength,0,0,0,0,0,LA_READ_ONLY|LA_UDF_IGNORE);
 | 
	
		
			
				|  |  |      laAddEnumItemAs(p,"IDLE", "Idle", "Button is not pressed", 0, 0);
 | 
	
		
			
				|  |  |      laAddEnumItemAs(p,"ACTIVE", "Active", "Button is pressed", 1, 0);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -853,23 +858,25 @@ void la_RegisterInputMapperOperators(){
 | 
	
		
			
				|  |  |      laAddEnumItemAs(p,"ATAN", "Arctangent", "atan(L)", LA_MATH_NODE_OP_ATAN, 0);
 | 
	
		
			
				|  |  |      laAddEnumItemAs(p,"ATAN2", "Atan2", "atan2(L,R) where L or R can be zero", LA_MATH_NODE_OP_ATAN2, 0);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    LA_IDN_REGISTER(LA_IDN_CONTROLLER,LA_PC_IDN_CONTROLLER, IDN_ControllerInit, IDN_ControllerDestroy, IDN_ControllerVisit, IDN_ControllerEval, laInputControllerNode);
 | 
	
		
			
				|  |  | -    LA_IDN_REGISTER(LA_IDN_VISUALIZER,LA_PC_IDN_VISUALIZER, IDN_InputVisualizeInit, IDN_InputVisualizeDestroy, IDN_InputVisualizeVisit, IDN_InputVisualizerEval, laInputVisualizerNode);
 | 
	
		
			
				|  |  | -    LA_IDN_REGISTER(LA_IDN_SPLIT,LA_PC_IDN_SPLIT, IDN_SplitInit, IDN_SplitDestroy, IDN_SplitVisit, IDN_SplitEval, laSplitNode);
 | 
	
		
			
				|  |  | -    LA_IDN_REGISTER(LA_IDN_SWITCH,LA_PC_IDN_SWITCH, IDN_SwitchInit, IDN_SwitchDestroy, IDN_SwitchVisit, IDN_SwitchEval, laSwitchNode);
 | 
	
		
			
				|  |  | -    LA_IDN_REGISTER(LA_IDN_COMBINE,LA_PC_IDN_COMBINE, IDN_CombineInit, IDN_CombineDestroy, IDN_CombineVisit, IDN_CombineEval, laCombineNode);
 | 
	
		
			
				|  |  | -    LA_IDN_REGISTER(LA_IDN_VALUES,LA_PC_IDN_VALUES, IDN_ValuesInit, IDN_ValuesDestroy, IDN_ValuesVisit, IDN_ValuesEval, laValuesNode);
 | 
	
		
			
				|  |  | -    LA_IDN_REGISTER(LA_IDN_MATRIX,LA_PC_IDN_MATRIX, IDN_MatrixInit, IDN_MatrixDestroy, IDN_MatrixVisit, IDN_MatrixEval, laMatrixNode);
 | 
	
		
			
				|  |  | -    LA_IDN_REGISTER(LA_IDN_MATH,LA_PC_IDN_MATH, IDN_MathInit, IDN_MathDestroy, IDN_MathVisit, IDN_MathEval, laMathNode);
 | 
	
		
			
				|  |  | +    LA_IDN_REGISTER("Controller",L'🕹',LA_IDN_CONTROLLER,LA_PC_IDN_CONTROLLER, IDN_ControllerInit, IDN_ControllerDestroy, IDN_ControllerVisit, IDN_ControllerEval, laInputControllerNode);
 | 
	
		
			
				|  |  | +    LA_IDN_REGISTER("Visualizer",L'🔍',LA_IDN_VISUALIZER,LA_PC_IDN_VISUALIZER, IDN_InputVisualizeInit, IDN_InputVisualizeDestroy, IDN_InputVisualizeVisit, IDN_InputVisualizerEval, laInputVisualizerNode);
 | 
	
		
			
				|  |  | +    LA_IDN_REGISTER("Split",L'⚟',LA_IDN_SPLIT,LA_PC_IDN_SPLIT, IDN_SplitInit, IDN_SplitDestroy, IDN_SplitVisit, IDN_SplitEval, laSplitNode);
 | 
	
		
			
				|  |  | +    LA_IDN_REGISTER("Switch",L'🚦',LA_IDN_SWITCH,LA_PC_IDN_SWITCH, IDN_SwitchInit, IDN_SwitchDestroy, IDN_SwitchVisit, IDN_SwitchEval, laSwitchNode);
 | 
	
		
			
				|  |  | +    LA_IDN_REGISTER("Combine",L'⚞',LA_IDN_COMBINE,LA_PC_IDN_COMBINE, IDN_CombineInit, IDN_CombineDestroy, IDN_CombineVisit, IDN_CombineEval, laCombineNode);
 | 
	
		
			
				|  |  | +    LA_IDN_REGISTER("Values",0,LA_IDN_VALUES,LA_PC_IDN_VALUES, IDN_ValuesInit, IDN_ValuesDestroy, IDN_ValuesVisit, IDN_ValuesEval, laValuesNode);
 | 
	
		
			
				|  |  | +    LA_IDN_REGISTER("Matrix",0,LA_IDN_MATRIX,LA_PC_IDN_MATRIX, IDN_MatrixInit, IDN_MatrixDestroy, IDN_MatrixVisit, IDN_MatrixEval, laMatrixNode);
 | 
	
		
			
				|  |  | +    LA_IDN_REGISTER("Math",0,LA_IDN_MATH,LA_PC_IDN_MATH, IDN_MathInit, IDN_MathDestroy, IDN_MathVisit, IDN_MathEval, laMathNode);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +    LA_NODE_CATEGORY_INPUT=laAddNodeCategory("Input",0,LA_RACK_TYPE_INPUT);
 | 
	
		
			
				|  |  | +    LA_NODE_CATEGORY_MATH=laAddNodeCategory("Math",0,LA_RACK_TYPE_ALL);
 | 
	
		
			
				|  |  | +    LA_NODE_CATEGORY_ROUTE=laAddNodeCategory("Route",0,LA_RACK_TYPE_ALL);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_INPUT, &LA_IDN_CONTROLLER,0);
 | 
	
		
			
				|  |  | +    laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_MATH, &LA_IDN_MATRIX,&LA_IDN_MATH,0);
 | 
	
		
			
				|  |  | +    laNodeCategoryAddNodeTypes(LA_NODE_CATEGORY_ROUTE, &LA_IDN_SPLIT, &LA_IDN_SWITCH, &LA_IDN_COMBINE, &LA_IDN_VALUES, &LA_IDN_VISUALIZER,0);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void laSetExtraNodeFunctions(laUiDefineFunc AddInputNodes, laUiDefineFunc AddDriverNodes, laGetBaseNodeTypeF GetInputNodeType, laGetBaseNodeTypeF GetDriverNodeType){
 | 
	
		
			
				|  |  | -    MAIN.ExtraAddInputNodes=AddInputNodes; MAIN.ExtraAddDriverNodes=AddDriverNodes;
 | 
	
		
			
				|  |  | -    MAIN.ExtraGetInputNodeType=GetInputNodeType; MAIN.ExtraGetDriverNodeType=GetDriverNodeType;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void laMappingRequestRebuild(){ MAIN.InputMapping->NeedRebuild=1; }
 | 
	
		
			
				|  |  |  void laMappingRequestEval(){ MAIN.InputMapping->NeedEval=1; }
 | 
	
	
		
			
				|  | @@ -895,4 +902,34 @@ int la_RebuildInputMapping(){
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if(result==LA_DAG_FLAG_ERR){ while(lstPopPointer(&MAIN.InputMapping->Eval)); return LA_DAG_FLAG_ERR; }
 | 
	
		
			
				|  |  |      return LA_DAG_FLAG_PERM;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +//==================================================================================================
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +laNodeOutSocket* laCreateOutSocket(void* NodeParentOptional, char* label, int DataType){
 | 
	
		
			
				|  |  | +    laNodeOutSocket* os=memAcquire(sizeof(laNodeOutSocket));
 | 
	
		
			
				|  |  | +    strSafeSet(&os->Label, label); os->DataType = DataType; os->Parent=NodeParentOptional;
 | 
	
		
			
				|  |  | +    return os;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +laNodeInSocket* laCreateInSocket(char* label, int DataType){
 | 
	
		
			
				|  |  | +    laNodeInSocket* is=memAcquire(sizeof(laNodeInSocket));
 | 
	
		
			
				|  |  | +    strSafeSet(&is->Label, label); is->DataType = DataType;
 | 
	
		
			
				|  |  | +    return is;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void laDestroyInSocket(laNodeInSocket* s){ strSafeDestroy(&s->Label); memLeave(s); }
 | 
	
		
			
				|  |  | +void laDestroyOutSocket(laNodeOutSocket* s){ strSafeDestroy(&s->Label); memLeave(s); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +laNodeCategory* laAddNodeCategory(char* Name,laUiDefineFunc* Ui,int ForRackTypes){
 | 
	
		
			
				|  |  | +    laNodeCategory* nc=memAcquire(sizeof(laNodeCategory));
 | 
	
		
			
				|  |  | +    lstAppendItem(&MAIN.NodeCategories,nc);
 | 
	
		
			
				|  |  | +    strSafeSet(&nc->Name, Name); nc->Ui=Ui;  nc->For=ForRackTypes;
 | 
	
		
			
				|  |  | +    return nc;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +void laNodeCategoryAddNodeTypes(laNodeCategory* nc, ...){
 | 
	
		
			
				|  |  | +    va_list list; va_start(list,nc);
 | 
	
		
			
				|  |  | +    laBaseNodeType* nt;
 | 
	
		
			
				|  |  | +    while(nt=va_arg(list,laBaseNodeType*)){
 | 
	
		
			
				|  |  | +        lstAppendPointer(&nc->NodeTypes, nt);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    va_end(list);
 | 
	
		
			
				|  |  | +}
 |