BIM Coordinator Program (INT) April 22, 2024

Find the next step in your career as a Graphisoft Certified BIM Coordinator!

Archicad C++ API
About Archicad add-on development using the C++ API.

Info of selected items

TomWaltz
Participant
A lot of the API examples show how to get/assign parameters of clicked elements.

Does anyone have any tips on getting the API_ElemTypeID typeID, and changing parameters on SELECTED elements?

I'm struggling with which myriad of "change/get/set" functions does what I want!
Tom Waltz
15 REPLIES 15
TomWaltz
Participant
Ralph,
I used the API_ParamOwnerType documentation as a starting point, and it never mentioned the index. It makes sense, though.

Is something more like this what you were describing?:

	API_Elem_Head		**elemHead;
	API_ElemTypeID		typeID = API_ZombieElemID;
	API_ParamOwnerType 		paramOwner;
	API_ChangeParamType 		chgParam;
	API_GetParamsType 		getParams;
	API_Element 				element, mask;
	API_ElementMemo 			memo;
	GSErrCode 				err, err2;
	API_AddParType			** addPars;

        elemHead = (API_Elem_Head **) BMAllocateHandle (sizeof (API_Elem_Head), ALLOCATE_CLEAR, 0);
	BNZeroMemory (&paramOwner, sizeof (API_ParamOwnerType)); 
	paramOwner.libInd = 0; 					// no library part
	paramOwner.typeID = API_DoorID;	 	// Door element
	paramOwner.index = 0; 					// element default 

	BNZeroMemory (&getParams, sizeof (APIAny_GetParamValuesID));

	err = ACAPI_Goodies (APIAny_OpenParametersID, &paramOwner, NULL);
	if (!err) {
		BNZeroMemory (&chgParam, sizeof (API_ChangeParamType)); 

		err = ACAPI_Goodies (APIAny_GetActParametersID, &getParams, NULL);
		if (!err) {
			chgParam.index = 0;
			CHCopyC ("gs_detlevel_3D", chgParam.name);
			chgParam.strValue = "Off";

			addPars = getParams.params;

			char 		parName[32];
			double 	val = 0;
			long 		ii;
			long  	addParNum = BMGetHandleSize (reinterpret_cast (*addPars)) / sizeof (API_AddParType);
			char		buffer [256];
			
			for (ii = 0; ii < addParNum; ii++) {
				CHCopyC ((*addPars)[ii].name, buffer);
				ACAPI_WriteReport (buffer, false); // print name for debugging
				
				if (strncmp (parName,(*addPars)[ii].name, 8)==0) {
					WriteReport("Parameter Matched",  parName); 
					paramOwner.index = ii;  
					chgParam.index = ii;
				}
				if (CHCompareCStrings ((*addPars)[ii].name, parName, CS_CaseInsensitive) == 0) { 
					chgParam.index = ii;
					CHCopyC ((*addPars)[ii].name , chgParam.name);
					CHCopyC ("Off", chgParam.strValue);
					WriteReport("Parameter variables set ", buffer); 
			         	break;
				}
			}
			err2 = ACAPI_Goodies (APIAny_ChangeAParameterID, &chgParam, NULL);
Tom Waltz
Ralph Wessel
Mentor
TomWaltz wrote:
Ralph,
I used the API_ParamOwnerType documentation as a starting point, and it never mentioned the index. It makes sense, though.
Is something more like this what you were describing?:
	BNZeroMemory (&paramOwner, sizeof (API_ParamOwnerType)); 
	paramOwner.libInd = 0; 					// no library part
	paramOwner.typeID = API_DoorID;	 	// Door element
	paramOwner.index = 0; 					// element default 
	err = ACAPI_Goodies (APIAny_OpenParametersID, &paramOwner, NULL);
No, you haven't specified the index of the element to be modified in paramOwner.index. The documentation on API_ParamOwnerType states:

"This structure is used to specify the target of which the parameter list should be opened to edit. Possible targets are:
  • - parameters of a placed element (Library Part instance)
    - default parameters of an element (default tool settings)
    - default parameters of a Library Part itself"
The values required by API_ParamOwnerType are different for each case. Your code would be suitable for the 2nd case (library part default), but you want to do the first (placed element). You should specify the index of a selected element in paramOwner.index in order to modify it. Do you know how to get the index of a selected element?

BTW, I notice the following error in your code comes from an example in the API documentation:
BNZeroMemory (&getParams, sizeof (APIAny_GetActParametersID));
It should be:
BNZeroMemory (&getParams, sizeof (API_GetParamsType));
Ralph Wessel BArch
TomWaltz
Participant
Ralph wrote:
Do you know how to get the index of a selected element?

BTW, I notice the following error in your code comes from an example in the API documentation:
I believe I do. That would be:
{
	API_SelectionInfo 	selectionInfo;
	API_Neig			**selNeigs = NULL;
	API_ElemTypeID 	typeID;
	char				buffer[256];
	char 				msgStr[256];
	GSErrCode		err;

	// ------- Check selection -------
	err = ACAPI_Selection_Get (&selectionInfo, &selNeigs, false);
	if (err != NoError) {
		WriteReport_Err ("ACAPI_Selection_Get", err);
		return API_ZombieElemID;
	}
	index = elemHead.index;

	// ------- Enumerate the selection -------
	for (long i = 0; i < selectionInfo.sel_nElem; i++) {
		typeID = Neig_To_ElemID ((*selNeigs).neigID);
		if (typeID != API_ZombieElemID) {
			sprintf (buffer, "%s #%d", ElemID_To_Name (typeID), (*selNeigs).index);
			WriteReport (buffer);
		}
	}
	return;
I guess my question is whether I return the elemHead.index or the selNeigs.index?
Tom Waltz
Oleg
Expert
There is some rough way, snippets. So I hope you can find a way to solve your task. I didnt tested it, just tried once, so there may be number of bugs, check the source closely.

// helper function
template<class T> inline void ApiClearIt(T & obj)
{
	BNZeroMemory(&obj,sizeof(T));
}

GSErrCode StartElementParamsEditing( API_ElemTypeID elem_type, long elem_index )
{
	API_ParamOwnerType param_owner;
	ApiClearIt( param_owner );
	param_owner.typeID = elem_type;
	param_owner.index = elem_index;
	return ACAPI_Goodies (APIAny_OpenParametersID, &param_owner, NULL); 
}

GSErrCode EndParamsEditing()
{
	return ACAPI_Goodies ( APIAny_CloseParametersID, NULL, NULL );
}

GSErrCode EditParameter( const char* name, const char* value )
{
	API_ChangeParamType param;
	ApiClearIt( param );
	CHCopyC( name, param.name );
	param.strValue = const_cast<char*>(value);
	return ACAPI_Goodies ( APIAny_ChangeAParameterID, &param, NULL );
}

GSErrCode GetParamsEdited( API_GetParamsType& params )
{
	return ACAPI_Goodies ( APIAny_GetActParametersID, &params, NULL );
}

GSErrCode UpdateElementParameters(	API_ElemTypeID elem_type,
									long elem_index,
									API_AddParType** params )
{
	API_Element elem, mask;
	API_ElementMemo memo;
	ApiClearIt( elem );
	ApiClearIt( mask );
	ApiClearIt( memo );
	elem.header.typeID = elem_type;
	elem.header.index = elem_index;
	memo.params = params;
	return ACAPI_Element_Change ( &elem, &mask, &memo, APIMemoMask_AddPars, true );
}

GSErrCode ChangeElementParameter(	API_ElemTypeID elem_type,
									long elem_index,
									const char* name,
									const char* value )
{
	GSErrCode err = StartElementParamsEditing( elem_type, elem_index );
	if ( !err )
	{
		err = EditParameter( name, value );
		// more parameters to edit
		if ( !err )
		{
			API_GetParamsType params;
			params.params = 0;
			err = GetParamsEdited( params );
			if ( !err )
				err = UpdateElementParameters( elem_type, elem_index, params.params );
			ACAPI_DisposeAddParHdl( &params.params );
		}
		EndParamsEditing();
	}	
	return err;
}

GSErrCode ChangeParameterOfSelectedWinDoors( const char* pname, const char* value )
{
	API_SelectionInfo sel; 
	API_Neig** neigs = 0;
	ApiClearIt( sel );
	GSErrCode err = ACAPI_Selection_Get (&sel, &neigs, true);
    BMKillHandle ((GSHandle *) &sel.marquee.coords);
	if ( !err && neigs && sel.typeID == API_SelElems )
	{
		GSSize count = BMGetHandleSize ((GSHandle) neigs) / sizeof (API_Neig);
		for ( GSSize i = 0; i<count; ++i )
		{
			API_ElemTypeID elem_type = API_ZombieElemID;
			ACAPI_Goodies (APIAny_NeigIDToElemTypeID, &(*neigs).neigID, &elem_type );
			if ( elem_type == API_WindowID || elem_type == API_DoorID )
			{
				long elem_index = (*neigs).index;
				GSErrCode edit_err = ChangeElementParameter(elem_type,elem_index,pname,value);
				if ( edit_err )
					err = edit_err;
			}
		}
	}
    BMKillHandle ((GSHandle *) &neigs);
	return err;
}

GSErrCode ChangeDetLevelOfSelectedWinDoors()
{
	ACAPI_OpenUndoableSession ( "Test" );
	GSErrCode err = ChangeParameterOfSelectedWinDoors( "gs_detlevel_3D", "Off" );
	ACAPI_CloseUndoableSession();
	return err;
}
TomWaltz
Participant
Thanks, Oleg. I will probably not be able to really work on this tomorrow, but it looks like you have all the elements I was struggling with.

I owe you big time for this!
Tom Waltz
Ralph Wessel
Mentor
TomWaltz wrote:
I guess my question is whether I return the elemHead.index or the selNeigs.index?
They are both the same thing - an index to a particular element in the Element database. The main difference between the handling of API_Elem_Head and API_Neig in this context is that the type specified in API_Neig describes not only the element type, but some specific part of that type, e.g. a point on a wall, or a reference line on a wall. That's why the type specified in the API_Neig has to be converted to an API_ElemTypeID before you can use it to find the target element.
Ralph Wessel BArch
Learn and get certified!