Sunday, April 24, 2005

Lab 2 - Solved

Hello,

Hope you have successfully found a solution for this Lab. I will present my solution which does not mean that it is the best solution. It is just my solution.

After follow the Lab instructions I have successfully created the project and the Dialog (derived from AcUiDialog). I have created the proposed layout with the Combo and Select button.

Inside the dialog class header, I have added the following:

ads_name m_sset;

This will store my selection set. Next, I have attached an event to my select button which fires the following function:

BeginEditorCommand();

// Issue selection
acedSSFree(m_sset);
int err = acedSSGet(NULL, NULL, NULL, NULL, m_sset);
if (err != RTNORM) {
MessageBox(_T("Selection error."),_T("DlgColor"),MB_ICONEXCLAMATION);
}

long length = 0;
acedSSLength(m_sset, &length);
m_btn_apply.EnableWindow(length > 0);

CompleteEditorCommand();


This will request the user to perform a selection set at AutoCAD screen. Note that we need to hide the dialog and switch back when finished. Note that m_btn_apply should be mapped to the OK button.

After that, we will need to perform the modifications walking through the selection set, get each entity's ObjectId, open it and then change the color according to the selected Combo item. So, the event fired when clicking on the Apply button will be:

CAcUiDialog::OnOK();

// Retrieve selected color in ComboBox
AcCmColor clr;
m_color.GetCurrentItemColor(clr);

long i, length;
ads_name ename;
AcDbObjectId entId;
acedSSLength(m_sset, &length);

// Traverse selected entities
acdbTransactionManager->startTransaction();
for (i = 0; i < length; i++) {

// Get each entity's objectId
acedSSName(m_sset, i, ename);
acdbGetObjectId(entId, ename);
// Open the entity and set its color
AcDbEntity* pEnt = NULL;
if (
acdbTransactionManager->getObject((AcDbObject*&)pEnt,
entId,
AcDb::kForWrite) == Acad::eOk) {
pEnt->
setColor(clr);
}
// Don't need to close because we're using Transaction
}
acdbTransactionManager->endTransaction();
acedSSFree(m_sset);

That's it!
Hope you enjoy this sample.

You may download this sample from here: ARXLAB2.zip!

Stay tuned for the next classes!

16 comments :

Jean-Christophe said...

Hi Fernando,

great lab, and very clear.

Just go on and give us such good classes.

Jean-Cristophe

Anonymous said...

Hi Fernando,

Now, if I want it to save the color value for the next loading of the dialog, what do I need to do?

In order to m_color.SetCurSel(PrevIndexValue);

Am I supposed to place something like in the OnInitDialog():

CAcUiTrueColorComboBox* m_color;
m_color = (CAcUiTrueColorComboBox*)GetDlgItem(IDC_COMBO1);

if (!color_internal)
m_color->SetCurSel(0);
else
m_color->SetCurSel(color_internal);


Thanks,
Luis.

Fernando Malard said...

Hi Luis,

The CAdUiDialog base class already has functions to deal with this. Take a look at SetDialogData() and GetDialogData(). Take a look at CAdUiDialog class at ObjectARX documentation for simple examples on how to use them.

If you prefer, you could also use the Windows Registry to persist your data. If it will be per user data, store inside HKEY_CURRENT_USER key and if it will be per computer data, store inside HKEY_LOCAL_MACHINE key. Take a look at VStudio documentation about Windows Registry functions.

Regards,
Fernando.

Anonymous said...

Thank yu Fernando, I am looking at that, right now.

By the way, do you know the proper signature for:

m_color.FindItemByColor(.. ...);

I know it saids:
m_color.FindItemByColor(const AcCmColor &color);

Please, if you can post a sample.

Anonymous said...

Ok I think I know the sintax:

void CDlgColor::OnBnClickedOk()
{
CAcUiDialog::OnOK();


AcCmColor clr;
m_color.GetCurrentItemColor(clr);

color_internal = m_color.FindItemByColor(clr);

And inside of:

BOOL CDlgColor::OnInitDialog()
{

...

if (color_internal && color_internal != -1)
m_color.SetCurSel(color_internal);
else
m_color.SetCurSel(0);

But the above is not working at all.... :0(

Fernando Malard said...

Hi Luis,

Try the following:

1)Inside you dialog's class header add:

public: static int cbSel;

2)Inside the dialog's cpp file body, add:

int CDlgColor::cbSel = 0;

3)Inside OnBnClickedOk() callback, just after m_color.GetCurrentItemColor(clr); add:

cbSel = m_color.FindItemByColor(clr);

4)Inside OnInitDialog() add:

m_color.SetCurSel(cbSel);

This will store the current value inside a static variable which will stay the same as long as your ARX application is loaded into memory. If the user closes AutoCAD and get back it will be reset to 0.

If you would like to store the last value from session to session you need to use one of the previous mentioned methods.

Regards,
Fernando.

Anonymous said...

Hi Fernando,

I followed your advise, and works, but only for the first items on the control 0-9... if I select any other color on the index,true or book, they are not set, the next time the dialog is called.

Strange, I went into google and cannot find any hints or whatever about this class CAcUiTrueColorComboBox in particular.

Best regards,
Luis Esquivel

Fernando Malard said...

Hi Luis,

Yes, you are right. To avoid this behavior, do the following:

1)Add another static member:

// Inside .H file
static AcCmColor cbColor;

// Inside .CPP file
AcCmColor CDlgColor::cbColor;

2)Inside OnBnClickedOk(), change as below:

cbSel = m_color.FindItemByColor(clr);
cbColor = clr;

3)Before select the cbSel index, inside OnInitDialog(), add the stored color:

m_color.AddColorToMRU(cbColor);
m_color.SetCurSel(cbSel);


This way you will store the selected color index and the color itself because it can be one of the "out of standard" color list.

When returning to dialog, if this color is not one of standard ones it will be added to the list and the index will be valid.

Regards,
Fernando.

Anonymous said...

Thank you Fernando,

I got the dialog working just fine, and also added another truecolor combobox, and everything is working just fine, I only need to figure how to save the AcCmColor value, into the SetDialogData ...

Again thanks for all the tips!
Luis

Anonymous said...

Hi Fernando,

I been trying to save the value for the colors, as I have mentioned in my previous comments to you, can you tell if I am in the right track?... or if this is not possible?... so far, this is the only issue I have, the other options on the dialog are saved and the next time autocad is open, and the arx, the a dialog grabs, all the last defaults.


// Retrieve selected color in ComboBox
AcCmColor clr;
m_color.GetCurrentItemColor(clr);
void *pBuff = malloc( 4096 );
int cbBuf = 4096;
Acad::ErrorStatus es = clr.serializeOut( pBuff, &cbBuf );

HKEY hkcuAppKey;
if(RegCreateKeyEx(HKEY_CURRENT_USER,
"SOFTWARE\\GBpoly",
NULL,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hkcuAppKey,
NULL) == ERROR_SUCCESS) {
RegSetValueEx(hkcuAppKey,
"IntColor",
NULL,
REG_BINARY ,
(CONST BYTE*)pBuff ,
&cbBuf
);
RegCloseKey(hkcuAppKey);
}

delete pBuff;

And this one to read the color back

static AcCmColor currentColor;
void *pBuff = malloc( 4096 );
int cbBuf = 4096;
HKEY hkcuAppKey;
if(RegCreateKeyEx(HKEY_CURRENT_USER,
"SOFTWARE\\GBpoly",
NULL,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hkcuAppKey,
NULL) == ERROR_SUCCESS) {
RegQueryValueEx(hkcuAppKey,
"IntColor",
NULL,
REG_BINARY,
(LPBYTE)pBuff,
&cbBuf);
RegCloseKey(hkcuAppKey);
}

Acad::ErrorStatus es = currentColor.serializeIn( pBuff, &cbBuf );
delete pBuff;

But I cannot figure out how to make this work.... and if you have a sample, please posted.

Thank you,
Luis

Fernando Malard said...

Hi Luis,

I guess you will need to better understand how AcCmColor works. In fact AutoCAD now supports 2 color modes: ACI and RGB.

Some code fragments:

// get RBG from index
AcCmEntityColor cEntityColor;
Adesk::UInt8 iIndex = 47; Adesk::UInt32 nValue = cEntityColor.lookUpRGB(iIndex);

// Colors are placed each 8 bits
// Low byte is blue; second byte
// is green; third byte is red;
// high byte is color method.

Adesk::UInt8 blue, green, red;
blue = nValue;
nValue = nValue>>8;
green = nValue;
nValue = nValue>>8;
red = nValue;

// Build from RGB
AcCmColor cColor;
cColor.setRGB(red, green, blue);

// Get ACI from RBG
Adesk::UInt8 aci = AcCmEntityColor::lookUpACI(cColor.red(), cColor.green(), cColor.blue());

So maybe the best approach would be store the Adesk::UInt32. Then, from RGB values and colorMethod you could build back the color.

One known problem is that you may not convert back to index from RGB because RGB has much more possible colors then ACI. So you may find some trouble when doing this and to avoid is better to store the colorMode into another variable. If color is byACI, get back the UInt32 and index if it is byColor get back the UInt32 and split the bytes to RGB.

More information at SDK (class AcCmColor).

Hope this help.
Fernando.

Anonymous said...

Hi Fernando,

This is what I end using and it is working just fine, thank you so much for all your help.

Luis.


CString css;
const char* LayName;
if (GetDialogData("INTLAYER" , css) && !css.IsEmpty())
{

m_intLayerName.SetWindowText(css);

LayName = css.GetString();

AcDbLayerTableRecordPointer layer(LayName,acdbHostApplicationServices()->workingDatabase(),AcDb::kForWrite);
if(layer.openStatus() == Acad::eOk) {
mDefColor = layer->color();
}

}
else

m_intLayerName.SetWindowText("GBPOLY_INT_POLYLINES");

int idxDef = m_color.FindItemByColor( mDefColor );
if( idxDef == -1 )
{
idxDef = m_color.AddColorToMRU( mDefColor );
}
m_color.SetCurSel( idxDef );

Abhay Joshi said...

Hi Fernando,

Tried the sample program. But could'nd execute it.

I imported the dialog to ObjectArx 2000 application. by including the required headers. and chaanges in color combo box.

now the scene is program is getting complied but
CAcUiDialog::OnInitDialog();
giving error "An unsupported operation was attempted".

Arx help is not sufficient to help me out..

Abhay Joshi

Fernando Malard said...

Hello Abhay,

The course samples were made for ObjectARX 2004 and above. Some features are compatible with ObjectARX 2000 but others are not.

I haven't tried to port this sample back to 2000 but I guess it will present some problems related to dialogs due AdUi and AcUi classes.

I would recommend you to use the ObjectARX 2004 or, if you really need to use this sample on 2000, recreate it step by step.

Having said that, this error message is related to a wrong control mapping inside your dialog or a missing call to CAcModuleResourceOverride before opening the dialog.

Hope this help.
Fernando Malard.

Andy said...

Hi Fernando,

It is possible to add a VS2005 soultion for this lab? Thanks

Fernando Malard said...

Andy,

You may use the Visual Teefy to do that:

http://through-the-interface.typepad.com/through_the_interface/files/visual_teefy_1.23%20MulitLang%20EULA.zip

Take a look at this post:

http://through-the-interface.typepad.com/through_the_interface/2006/07/migrating_code_.html

Regards,