RESTODLG.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/

/****************************** Module Header *******************************
* Module Name: restodlg.c
*
* Routines that take a dialog resource and create the dialog to edit, or
* the other way around.
*
* Fucntions:
* SynchDialogResource()
* AllocDialogResource()
* ResLinkToDialog()
* ResToDialog()
* GetiClass()
* Duplicate()
* MakeCopyFromRes()
* TypeFromClassStyle()
*
* Comments:
*
****************************************************************************/

#include "dlgedit.h"
#include "dlgfuncs.h"
#include "dlgextrn.h"

STATICFN INT TypeFromClassStyle(INT iClass, DWORD flStyle);



/************************************************************************
* SynchDialogResource
*
* This routine synchronizes the resource buffer with the contents of
* the current dialog being edited. This may involve deleting the old
* contents of the current dialog prior to adding the new data.
*
* It is ok to call this routine even if there is not an existing dialog
* being edited (it will just return) and it should be called before any
* operation that needs the in memory copy of the dialog to accurately
* reflect the contents of the current dialog, such as just before a
* save to disk.
*
* Returns:
* TRUE if all goes well (includes the case where nothing was done).
* FALSE if an error occurs updating the resource.
*
************************************************************************/

BOOL SynchDialogResource(VOID)
{
PRES pRes;
PRESLINK prl;
PRESLINK prlNew;
PRESLINK prlPrev;

if (!gfEditingDlg)
return TRUE;

/*
* Allocate a resource for the current dialog.
*/
if (!(pRes = AllocDialogResource(FALSE, FALSE)))
return FALSE;

/*
* Allocate a new link for it.
*/
if (!(prlNew = AllocResLink(pRes)))
return FALSE;

/*
* Free the local copy of the dialog resource now that the
* link has been created (and the resource copied to global
* memory).
*/
MyFree(pRes);

/*
* Does a link for the dialog already exist?
*/
if (gcd.prl) {
/*
* Find the existing link and get it's previous link.
*/
for (prl = gprlHead, prlPrev = NULL; prl && prl != gcd.prl;
prlPrev = prl, prl = prl->prlNext)
;

/*
* Start linking it in.
*/
prlNew->prlNext = gcd.prl->prlNext;

if (prlPrev)
prlPrev->prlNext = prlNew;
else
gprlHead = prlNew;

/*
* Delete the old link now that it is replaced.
*/
FreeResLink(gcd.prl);
}
else {
/*
* Search for the end of the list. Get a pointer to the last link.
*/
for (prl = gprlHead, prlPrev = NULL; prl;
prlPrev = prl, prl = prl->prlNext)
;

/*
* Add the new link to the end of the list.
*/
if (prlPrev)
prlPrev->prlNext = prlNew;
else
gprlHead = prlNew;
}

/*
* Update our global with the new link. Clear the "dialog changed"
* flag.
*/
gcd.prl = prlNew;
gfDlgChanged = FALSE;

return TRUE;
}



/************************************************************************
* AllocDialogResource
*
* This function allocates memory and builds a resource file format
* image in it of the current dialog.
*
* Arguments:
* BOOL fTestMode - TRUE if a special test mode version of the current
* dialog should be created.
* BOOL fClipboard - If TRUE, only the selected control(s) will be
* placed in the resource. This is used when putting
* controls or groups of controls into the clipboard.
* If the dialog is selected, this flag is ignored,
* because selecting the dialog implies all the
* controls will be written out also.
*
* Returns:
* Pointer to the resource buffer.
* NULL if unable to create the resource.
*
************************************************************************/

PRES AllocDialogResource(
BOOL fTestMode,
BOOL fClipboard)
{
NPCTYPE npc;
INT cControls;
BOOL fSelectedOnly = FALSE;
INT cbDlgName;
INT cbCaption;
INT cbPointSize;
INT cbFontName;
INT cbCD;
INT cbResHeader;
INT cbResData;
INT cbResSize;
INT cbMenuName;
INT cbClass;
INT cbText;
INT cbAlloc;
PBYTE pb;
PRES pResBegin;
PRES pResBegin2;
LPTSTR pszClass;
LPTSTR pszMenu;
LPTSTR pszText;
DWORD flStyle;
PDIALOGBOXHEADER pdbh;
PCONTROLDATA pcd;
ORDINAL ordClass;

cControls = cWindows;
if (fClipboard && !gfDlgSelected) {
fSelectedOnly = TRUE;
for (cControls = 0, npc = npcHead; npc; npc = npc->npcNext)
if (npc->fSelected)
cControls++;
}

/*
* If testing, don't allow a dialog to be created with any
* special class, or with the real menu.
*/
if (fTestMode) {
pszClass = NULL;
pszMenu = NULL;
}
else {
pszClass = gcd.di.pszClass;
pszMenu = gcd.di.pszMenu;
}

cbDlgName = NameOrdLen(gcd.pszDlgName);
cbCaption = (gcd.npc->text) ?
(lstrlen(gcd.npc->text) + 1) * sizeof(TCHAR) : sizeof(TCHAR);
cbClass = pszClass ? NameOrdLen(pszClass) : sizeof(TCHAR);
cbMenuName = pszMenu ? NameOrdLen(pszMenu) : sizeof(TCHAR);

if (gcd.fFontSpecified) {
cbPointSize = sizeof(WORD);
cbFontName = (lstrlen(gcd.di.szFontName) + 1) * sizeof(TCHAR);
}
else {
cbPointSize = cbFontName = 0;
}

/*
* Calculate the size of the resource header.
*/
cbResHeader = sizeof(RES) + // The first fixed part.
sizeof(ORDINAL) + // The RT_DIALOG ordinal.
cbDlgName; // The dialog's name.
DWordAlign((PBYTE *)&cbResHeader); // Pad for the dialog's name.
cbResHeader += sizeof(RES2); // The last fixed part.

/*
* Calculate the size of the resource data. This will just include
* the dialog box header right now.
*/
cbResData = SIZEOF_DIALOGBOXHEADER + // The first fixed part.
cbMenuName + // The menu.
cbClass + // The class.
cbCaption + // The caption.
cbPointSize + // The point size.
cbFontName; // The font name.

/*
* Allocate some buffer space. Be sure to round this up to a DWORD
* boundary to allow space for padding if necessary, but don't round
* cbResData field because it will need to be written into the header
* later, and the value that is written is an exact size (not rounded
* up).
*/
cbAlloc = cbResSize = cbResHeader + cbResData;
DWordAlign((PBYTE *)&cbAlloc);
if (!(pResBegin = (PRES)MyAlloc(cbAlloc)))
return NULL;

/*
* Write the resource header.
*/
pdbh = (PDIALOGBOXHEADER)WriteResHeader(pResBegin, 0, ORDID_RT_DIALOG,
gcd.pszDlgName, gcd.di.fResFlags, gcd.di.wLanguage,
gcd.di.DataVersion, gcd.di.Version, gcd.di.Characteristics);

/*
* Write out the style.
*/
flStyle = gcd.npc->flStyle;
if (fTestMode) {
flStyle &= ~awcd[W_DIALOG].flStylesTestBad;
flStyle |= WS_VISIBLE;
}

pdbh->lStyle = flStyle;
pdbh->lExtendedStyle = gcd.npc->flExtStyle;
pdbh->NumberOfItems = (WORD)cControls;

/*
* Write the coordinates.
*
* If we are allocating a template that only has the selected controls
* in it, we put the value of CONTROLS_ONLY in the "cx" field of the
* dialog header. This is what we will check when the user pastes
* something from the clipboard into a dialog to determine whether
* to paste the entire dialog, or only the controls within the dialog
* item array.
*/
pdbh->x = (WORD)gcd.npc->rc.left;
pdbh->y = (WORD)gcd.npc->rc.top;

if (fSelectedOnly)
pdbh->cx = CONTROLS_ONLY;
else
pdbh->cx = (WORD)(gcd.npc->rc.right - gcd.npc->rc.left);

pdbh->cy = (WORD)(gcd.npc->rc.bottom - gcd.npc->rc.top);

pb = (PBYTE)pdbh + SIZEOF_DIALOGBOXHEADER;

/*
* Write the menu name if there is one (we always write at least a null.
*/
pb = NameOrdCpy((LPTSTR)pb, pszMenu ? pszMenu : szEmpty);

/*
* Write the class if there is one (we always write at least a null.
*/
pb = NameOrdCpy((LPTSTR)pb, pszClass ? pszClass : szEmpty);

/*
* Write the caption if there is one (we always write at least a null).
*/
pb = WriteSz((LPTSTR)pb, gcd.npc->text ? gcd.npc->text : szEmpty);

/*
* Write out the font, if there is one specified.
*/
if (gcd.fFontSpecified) {
*(PWORD)pb = (WORD)gcd.di.nPointSize;
pb += sizeof(WORD);

pb = WriteSz((LPTSTR)pb, gcd.di.szFontName);
}

/*
* Pad to a DWORD boundary. This is ok even if there are no controls
* that follow, because we were sure to allocate on an even dword
* boundary above.
*/
DWordPad(&pb);

/*
* Now do dialog items.
*/
for (npc = npcHead; npc; npc = npc->npcNext) {
/*
* Skip the control if it is NOT selected and we only want the
* selected controls.
*/
if (fSelectedOnly && !npc->fSelected)
continue;

/*
* If we are testing, we don't want to really create a control
* with some funny class because it probably won't be found.
* We will substitute our custom class emulator instead.
*/
if (fTestMode && npc->pwcd->fEmulated)
pszClass = szCustomClass;
else
pszClass = npc->pwcd->pszClass;

/*
* Get a pointer to the text. If this is an icon control and
* we are going into test mode, change the text field so that
* it points to an ordinal for DlgEdit's icon to display, or
* the icon resource will probably not be found when the dialog
* is created.
*/
pszText = npc->text;
if (npc->pwcd->iType == W_ICON && fTestMode)
pszText = (LPTSTR)&gordIcon;

cbText = pszText ? NameOrdLen(pszText) : sizeof(TCHAR);
cbClass = pszClass ? NameOrdLen(pszClass) : sizeof(ORDINAL);

cbCD = SIZEOF_CONTROLDATA + // The fixed portion.
cbClass + // The class.
cbText + // The text.
sizeof(WORD); // nExtraStuff field.

/*
* Since we are adding a new control, we dword align the
* previous size of the resource data to ensure the new
* control starts on a dword boundary.
*/
DWordAlign((PBYTE *)&cbResSize);

/*
* Allocate room for this control. This includes room for the
* template structure, class, text and a byte for the cb field
* for the create struct data.
*/
cbAlloc = cbResSize + cbCD;
DWordAlign((PBYTE *)&cbAlloc);
pResBegin2 = (PRES)MyRealloc((PBYTE)pResBegin, cbAlloc);
if (!pResBegin2) {
MyFree(pResBegin);
return NULL;
}

pResBegin = pResBegin2;
pcd = (PCONTROLDATA)((PBYTE)pResBegin + cbResSize);
cbResSize += cbCD;

/*
* Write the style. If testing, remove any styles that can
* cause problems, such as ownerdraw styles. If testing and
* this is an emulated custom control, always make it with
* the default styles no matter what the user has specified.
*/
flStyle = npc->flStyle;
if (fTestMode) {
if (npc->pwcd->fEmulated)
flStyle = awcd[W_CUSTOM].flStyles;
else
flStyle &= ~npc->pwcd->flStylesTestBad;
}

pcd->lStyle = flStyle;
pcd->lExtendedStyle = npc->flExtStyle;

/*
* Write the coordinates.
*/
pcd->x = (WORD)npc->rc.left;
pcd->y = (WORD)npc->rc.top;
pcd->cx = (WORD)(npc->rc.right - npc->rc.left);
pcd->cy = (WORD)(npc->rc.bottom - npc->rc.top);

/*
* Write the id.
*/
pcd->wId = (WORD)npc->id;

pb = (PBYTE)pcd + SIZEOF_CONTROLDATA;

/*
* Write the class. This will be a string, except for the
* predefined control classes, which all have an ordinal
* value defined for them.
*/
if (pszClass) {
pb = NameOrdCpy((LPTSTR)pb, pszClass);
}
else {
WriteOrd(&ordClass, acsd[awcd[npc->pwcd->iType].iClass].idOrd);
pb = NameOrdCpy((LPTSTR)pb, (LPTSTR)&ordClass);
}

/*
* Write the text.
*/
pb = NameOrdCpy((LPTSTR)pb, pszText ? pszText : szEmpty);

/*
* Write out a zero because there are no additional bytes
* of create struct data.
*/
*(PWORD)pb = 0;
pb += sizeof(WORD);

/*
* Pad to a DWORD boundary. This is ok even if there are no more
* controls, because we were sure to allocate on an even dword
* boundary above.
*/
DWordPad(&pb);
}

/*
* Now go back and fill in the resource data size field.
*/
pResBegin->DataSize = cbResSize - cbResHeader;

return pResBegin;
}



/************************************************************************
* ResLinkToDialog
*
* This function is used to create a dialog out of a dialog resource
* that has been stored in the resource linked list.
*
* Arguments:
* PRESLINK prl - Points to the link that describes the dialog to
* create. It is assumed that the resource is a
* dialog resource.
*
************************************************************************/

VOID ResLinkToDialog(
PRESLINK prl)
{
PRES pRes;

pRes = (PRES)GlobalLock(prl->hRes);
ResToDialog(pRes, TRUE);
GlobalUnlock(prl->hRes);

/*
* If the dialog was successfully created, remember which res link
* it was created from.
*/
if (gfEditingDlg)
gcd.prl = prl;
}



/************************************************************************
* ResToDialog
*
* This function creates a dialog box, complete with controls,
* from a dialog resource template.
*
* Arguments:
* PRES pRes - Pointer to the dialog resource to use.
* BOOL fDoDialog - TRUE if a new dialog should be created, followed
* by the controls. If this is FALSE, just the
* controls will be created and added to the current
* dialog.
*
* Returns:
* TRUE on success, FALSE if an error occured.
*
************************************************************************/

BOOL ResToDialog(
PRES pRes,
BOOL fDoDialog)
{
LPTSTR pszText;
LPTSTR pszClass;
INT x;
INT y;
INT cx;
INT cy;
INT id;
INT iClass;
INT cdit;
INT Type;
DWORD flStyle;
DWORD flExtStyle;
NPCTYPE npc;
LPTSTR pszMenuName;
LPTSTR pszFontName;
INT nPointSize;
LPTSTR pszDlgName;
LPTSTR pszCaption;
PDIALOGBOXHEADER pdbh;
PCONTROLDATA pcd;
PWINDOWCLASSDESC pwcd;
PCUSTLINK pcl;
PRES2 pRes2;
DIALOGINFO di;
CCINFO cci;

/*
* First check that the pointer is ok.
*/
if (!pRes)
return FALSE;

pRes2 = ResourcePart2(pRes);
pdbh = (PDIALOGBOXHEADER)SkipResHeader(pRes);

/*
* Parse out the dialog box header.
* After this point, pcd is pointing to the first dialog control item.
*/
pcd = ParseDialogBoxHeader(pdbh,
&flStyle, &flExtStyle, &cdit, &x, &y, &cx, &cy,
&pszMenuName, &pszClass, &pszCaption,
&nPointSize, &pszFontName);

/*
* Are we pasting the entire dialog?
*/
if (fDoDialog) {
pszDlgName = ResourceName(pRes);

/*
* Determine the best base id for the dialog.
*/
if (IsOrd(pszDlgName))
id = OrdID(pszDlgName);
else
id = NextID(NEXTID_DIALOG, plInclude, 0);

di.fResFlags = pRes2->MemoryFlags;
di.wLanguage = pRes2->LanguageId;
di.pszClass = pszClass;
di.pszMenu = pszMenuName;
di.DataVersion = pRes2->DataVersion;
di.Version = pRes2->Version;
di.Characteristics = pRes2->Characteristics;
di.nPointSize = nPointSize;
lstrcpy(di.szFontName, pszFontName ? pszFontName : szEmpty);

/*
* Create the dialog.
*/
if (!AddControl(&awcd[W_DIALOG], pszCaption, flStyle, flExtStyle, id,
x, y, cx, cy, pszDlgName, &di))
return FALSE;
}

while (cdit--) {
pcd = ParseControlData(pcd, &flStyle, &flExtStyle, &x, &y, &cx, &cy,
&id, &pszClass, &pszText);

/*
* If we are not creating a new dialog, and the id in
* the resource is already in use, we will use the next
* available one instead.
*/
if (!fDoDialog && !IsUniqueID(id))
id = NextID(NEXTID_CONTROL, plInclude, 0);

/*
* Fix up the class. If the class is a predefined ordinal type,
* we will null out pszClass so it doesn't confuse AddControl
* into thinking that there is a string class for this control.
*/
iClass = GetiClass(pszClass);
Type = TypeFromClassStyle(iClass, flStyle);
if (IsOrd(pszClass))
pszClass = NULL;

if (Type == W_CUSTOM) {
/*
* Search the list of installed custom controls for one
* that matches the class.
*/
for (pcl = gpclHead;
pcl && lstrcmpi(pcl->pwcd->pszClass, pszClass) != 0;
pcl = pcl->pclNext)
;

/*
* Was a match found?
*/
if (pcl) {
pwcd = pcl->pwcd;
}
else {
/*
* An existing custom control link for this class was
* not found. We will add an emulated custom control
* to support it. We assume the default style and size
* should be what this control has.
*/
lstrcpy(cci.szClass, pszClass);
cci.flOptions = 0;
*cci.szDesc = TEXT('\0');
cci.cxDefault = cx;
cci.cyDefault = cy;
cci.flStyleDefault = flStyle;
cci.flExtStyleDefault = flExtStyle;
*cci.szTextDefault = TEXT('\0');
cci.cStyleFlags = 0;
cci.aStyleFlags = NULL;
cci.lpfnStyle = NULL;
cci.lpfnSizeToText = NULL;
cci.dwReserved1 = 0;
cci.dwReserved2 = 0;

if (pcl = AddCustomLink(&cci, TRUE, FALSE, NULL, NULL))
pwcd = pcl->pwcd;
else
/*
* Skip this control and continue creating the
* rest of the dialog.
*/
continue;
}
}
else {
pwcd = &awcd[Type];
}

/*
* If we are not creating the entire dialog (we allow existing
* resource files to be a little messed up), and this control
* is a default pushbutton, we will then loop through all the
* existing controls checking for another default pushbutton.
* If one is found, we convert the default pushbutton being
* created into a normal pushbutton instead. It is not allowed
* to have more than one default pushbuttons in the same dialog.
*/
if (!fDoDialog && Type == W_PUSHBUTTON &&
(flStyle & BS_ALL) == BS_DEFPUSHBUTTON) {
for (npc = npcHead; npc; npc = npc->npcNext) {
if (npc->pwcd->iType == W_PUSHBUTTON &&
(npc->flStyle & BS_ALL) == BS_DEFPUSHBUTTON) {
flStyle = (flStyle & ~BS_ALL) | BS_PUSHBUTTON;
break;
}
}
}

npc = AddControl(pwcd, pszText, flStyle, flExtStyle, id,
x, y, cx, cy, NULL, NULL);

/*
* If the control creation succeeded, and we are just adding
* controls (not creating a whole new dialog), select the
* controls as they are added, but don't do any drawing yet.
*/
if (!fDoDialog && npc)
SelectControl2(npc, TRUE);
}

/*
* Update the selected rectangle. This is normally done by
* SelectControl2 but we told it not to so that the selection
* could be done faster. We also select the first control here.
*/
if (!fDoDialog) {
SetAnchorToFirstSel(TRUE);
CalcSelectedRect();
}

ShowWindow(gcd.npc->hwnd, SW_SHOWNA);
ToolboxOnTop();

return TRUE;
}



/************************************************************************
* TypeFromClassStyle
*
* This function returns the type of a control (one of the W_ constants)
* based on the class in iClass and the style in flStyle.
*
* Arguments:
* INT iClass = The class of the control, as an IC_* defined constant.
* DWORD flStyle = The style of the control.
*
* Returns:
* The type of the control (W_* constant).
* W_NOTHING is the error return.
*
************************************************************************/

STATICFN INT TypeFromClassStyle(
INT iClass,
DWORD flStyle)
{
switch (iClass) {
case IC_BUTTON:
return rgmpiClsBtnType[flStyle & BS_ALL];

case IC_EDIT:
return W_EDIT;

case IC_SCROLLBAR:
return (flStyle & SBS_VERT) ? W_VERTSCROLL : W_HORZSCROLL;

case IC_STATIC:
return rgmpiClsStcType[flStyle & SS_ALL];

case IC_LISTBOX:
return W_LISTBOX;

case IC_COMBOBOX:
return W_COMBOBOX;

case IC_CUSTOM:
return W_CUSTOM;

case IC_DIALOG:
return W_DIALOG;

default:
return W_NOTHING;
}
}



/************************************************************************
* GetiClass
*
* This function returns the class identifier number for the
* window class of the control, given the class string from the
* dialog template.
*
* An ordinal class is a special ordinal that is used to identify
* each of the standard control classes. It is used in the
* dialog template to save space. The class string passed in
* can be an ordinal and if so, it will be checked against these
* predefined ordinal class values for a match.
*
* Arguments:
* LPTSTR pszClass - The class string or ordinal.
*
* Returns:
* The class identifier, one of the IC_* symbols in dlgedit.h.
* If the class cannot be determined, it assumes it is a custom
* class and returns IC_CUSTOM.
*
************************************************************************/

INT GetiClass(
LPTSTR pszClass)
{
INT i;
WORD idOrd;

if (IsOrd(pszClass)) {
idOrd = OrdID(pszClass);
for (i = 0; i < IC_DIALOG; i++) {
if (acsd[i].idOrd == idOrd)
return i;
}
}
else {
for (i = 0; i < IC_DIALOG; i++) {
if (lstrcmpi(ids(acsd[i].idsClass), pszClass) == 0)
return i;
}
}

/*
* Not found. Assume it is a user defined class.
*/
return IC_CUSTOM;
}



/************************************************************************
* Duplicate
*
* This routine duplicates the current selection.
*
************************************************************************/

VOID Duplicate(VOID)
{
PRES pRes;

if (gcSelected) {
/*
* Store the current selection in a dialog resource.
*/
if (!(pRes = AllocDialogResource(FALSE, TRUE)))
return;

MakeCopyFromRes(pRes);
}
}



/************************************************************************
* MakeCopyFromRes
*
* This function uses the given dialog template to either add a new
* dialog to the current resource file, or drop controls from the
* template into the current dialog. If copying a dialog, it is created
* right away. If copying controls, an operation is begun to start
* tracking them to their final destination in the current dialog.
*
* The caller of this function should NOT free pRes. This will
* be done either before the function returns, or after the drag
* operation is complete.
*
* Arguments:
* PRES pRes - Points to the dialog resource that contains
* the dialog or controls to make a copy of.
*
************************************************************************/

VOID MakeCopyFromRes(
PRES pRes)
{
PDIALOGBOXHEADER pdbh;
PCONTROLDATA pcd;
INT cControls;
INT i;
BOOL fFreeData = TRUE;
INT iType;
INT iClass;
INT nBottom;
INT nBottomLowest;

gpResCopy = pRes;
pdbh = (PDIALOGBOXHEADER)SkipResHeader(gpResCopy);

/*
* If cx is CONTROLS_ONLY, then we know that we only
* want to copy the controls in the template, not
* the entire dialog plus controls.
*/
if (pdbh->cx == CONTROLS_ONLY) {
/*
* Begin copying in new controls into the current dialog.
*/
cControls = pdbh->NumberOfItems;
if (cControls) {
/*
* Seed the rectangle with impossible values.
*/
SetRect(&grcCopy, 32000, 32000, -32000, -32000);
nBottomLowest = 0;

/*
* Loop through all the controls, expanding the rectangle
* to fit around all of them.
*/
pcd = SkipDialogBoxHeader(pdbh);
for (i = 0; i < cControls; i++) {
iClass = GetiClass((LPTSTR)((PBYTE)pcd + SIZEOF_CONTROLDATA));
iType = TypeFromClassStyle(iClass, pcd->lStyle);

if (grcCopy.left > (INT)pcd->x)
grcCopy.left = (INT)pcd->x;

if (grcCopy.top > (INT)pcd->y)
grcCopy.top = (INT)pcd->y;

if (grcCopy.right < (INT)pcd->x + (INT)pcd->cx)
grcCopy.right = (INT)pcd->x + (INT)pcd->cx;

nBottom = ((INT)pcd->y + (INT)pcd->cy) -
GetOverHang(iType, (INT)pcd->cy);
if (nBottom > nBottomLowest)
nBottomLowest = nBottom;

if (grcCopy.bottom < (INT)pcd->y + (INT)pcd->cy)
grcCopy.bottom = (INT)pcd->y + (INT)pcd->cy;

pcd = SkipControlData(pcd);
}

/*
* Begin dragging the new control(s). Set a flag so that
* the resource data is NOT free'd until after the drag
* is finished.
*/
DragNewBegin(grcCopy.right - grcCopy.left,
grcCopy.bottom - grcCopy.top,
grcCopy.bottom - nBottomLowest);
fFreeData = FALSE;

} 
}
else {
/*
* Begin copying in a new dialog, complete with controls.
*/
if (SynchDialogResource()) {
/*
* Remove any existing dialog.
*/
if (gfEditingDlg)
DeleteDialog(FALSE);

if (ResToDialog(gpResCopy, TRUE)) {
SelectControl(gcd.npc, FALSE);
gfResChged = TRUE;
ShowFileStatus(FALSE);
}
}
}

if (fFreeData) {
MyFree(gpResCopy);
gpResCopy = NULL;
}
}