983 lines
28 KiB
C
983 lines
28 KiB
C
/****************************************************************************/
|
|
/* */
|
|
/* Microsoft Confidential */
|
|
/* */
|
|
/* Copyright (c) Microsoft Corp. 1987, 1990 */
|
|
/* All Rights Reserved */
|
|
/* */
|
|
/****************************************************************************/
|
|
/****************************** Module Header *******************************
|
|
* Module Name: restodlg.c
|
|
*
|
|
* Routines that take a dialog resource and create the dialog to edit, or
|
|
* the other way around.
|
|
*
|
|
* History:
|
|
*
|
|
****************************************************************************/
|
|
|
|
#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).
|
|
*
|
|
* Error Returns:
|
|
* FALSE if an error occurs updating the resource.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
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.
|
|
*
|
|
* Error Returns:
|
|
* NULL if unable to create the resource.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
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.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
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.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
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).
|
|
*
|
|
* Error Returns:
|
|
* W_NOTHING
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
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.
|
|
*
|
|
* Error Returns:
|
|
* If the class cannot be determined, it assumes it is a custom
|
|
* class and returns IC_CUSTOM.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
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.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
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.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
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;
|
|
}
|
|
}
|