CUSTCNTL.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: custcntl.c
*
* Contains functions to support custom controls.
*
* Functions:
* NewCustDlgProc()
* OpenCustomDialog()
* SelCustDialog()
* SelCustDlgProc()
* RemCustDlgProc()
* CustomWndProc()
* AddCustomLink()
* RemoveCustomLink()
* CallCustomStyle()
* CallCustomSizeToText()
* ReadCustomProfile()
* WriteCustomProfile()
* NewCustInit()
* NewCustOK()
* OpenDLLFile()
* CallCustomInfoA()
* SelCustInit()
* SelCustSelect()
* SelCustOK()
* RemCustInit()
* RemCustOK()
* AllocCUSTLINK()
* FreeCUSTLINK()
*
* Comments:
*
****************************************************************************/

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

#include <stdlib.h>
#include <string.h>

#include <commdlg.h>


/*
* Minimum margin around the sample control.
*/
#define SAMPLEMARGIN 4


STATICFN VOID NewCustInit(HWND hwnd);
STATICFN BOOL NewCustOK(HWND hwnd);
STATICFN VOID OpenDLLFile(LPTSTR pszFileName);
STATICFN UINT CallCustomInfoA(LPFNCCINFOA lpfnInfoA, LPCCINFO acciW,
INT nControls);
STATICFN VOID SelCustInit(HWND hwnd);
STATICFN VOID SelCustSelect(HWND hwnd);
STATICFN BOOL SelCustOK(HWND hwnd);
STATICFN VOID RemCustInit(HWND hwnd);
STATICFN BOOL RemCustOK(HWND hwnd);
STATICFN PCUSTLINK AllocCUSTLINK(LPCCINFO pcci, BOOL fEmulated,
BOOL fUnicodeDLL, LPTSTR pszFileName, HANDLE hmod);
STATICFN VOID FreeCUSTLINK(PCUSTLINK pclFree);


/*
* Used to return the pwcd that is chosen from the Select Custom
* Control dialog.
*/
static PWINDOWCLASSDESC pwcdChosen;

/*
* Has the window handle of the sample custom control in the
* Select Custom Control dialog.
*/
static HWND hwndCustomSample;




/************************************************************************
* NewCustDlgProc
*
* This is the Add Custom Control dialog procedure.
*
* :
*
************************************************************************/

DIALOGPROC NewCustDlgProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
NewCustInit(hwnd);
return TRUE;

case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
if (NewCustOK(hwnd))
EndDialog(hwnd, IDOK);

break;

case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;

case IDHELPDLG:
WinHelp(ghwndMain, gszHelpFile, HELP_CONTEXT,
HELPID_NEWCUST);
break;
}

return TRUE;

default:
return FALSE;
}
}



/************************************************************************
* NewCustInit
*
* Processes the WM_INITDIALOG message for the New Temporary Custom Control
* dialog procedure.
*
* History:
*
************************************************************************/

STATICFN VOID NewCustInit(
HWND hwnd)
{
TCHAR szStyles[32];

SendDlgItemMessage(hwnd, DID_NEWCUSTCLASS, EM_LIMITTEXT, CCHCCCLASS - 1, 0L);

SendDlgItemMessage(hwnd, DID_NEWCUSTSTYLES, EM_LIMITTEXT, CCHHEXLONGMAX, 0L);
wsprintf(szStyles, L"%#.8lx", awcd[W_CUSTOM].flStyles);
SetDlgItemText(hwnd, DID_NEWCUSTSTYLES, szStyles);

SendDlgItemMessage(hwnd, DID_NEWCUSTCX, EM_LIMITTEXT, 3, 0L);
SetDlgItemInt(hwnd, DID_NEWCUSTCX, awcd[W_CUSTOM].cxDefault, FALSE);

SendDlgItemMessage(hwnd, DID_NEWCUSTCY, EM_LIMITTEXT, 3, 0L);
SetDlgItemInt(hwnd, DID_NEWCUSTCY, awcd[W_CUSTOM].cyDefault, FALSE);

SendDlgItemMessage(hwnd, DID_NEWCUSTTEXT, EM_LIMITTEXT, CCHCCTEXT - 1, 0L);

CenterWindow(hwnd);
}



/************************************************************************
* NewCustOK
*
* Processes the OK button from the New Temporary Custom Control dialog.
*
* History:
*
************************************************************************/

STATICFN BOOL NewCustOK(
HWND hwnd)
{
TCHAR szStyles[CCHHEXLONGMAX + 1];
CCINFO cci;

/*
* Read the class field. It is required.
*/
if (!GetDlgItemText(hwnd, DID_NEWCUSTCLASS, cci.szClass, CCHCCCLASS)) {
Message(MSG_NOCLASS);
SetFocus(GetDlgItem(hwnd, DID_NEWCUSTCLASS));
return FALSE;
}

GetDlgItemText(hwnd, DID_NEWCUSTSTYLES, szStyles, CCHHEXLONGMAX + 1);
cci.flStyleDefault = valtoi(szStyles);

if (!(cci.cxDefault = GetDlgItemInt(hwnd, DID_NEWCUSTCX, NULL, FALSE))) {
Message(MSG_GTZERO, ids(IDS_WIDTH));
SetFocus(GetDlgItem(hwnd, DID_NEWCUSTCX));
return FALSE;
}

if (!(cci.cyDefault = GetDlgItemInt(hwnd, DID_NEWCUSTCY, NULL, FALSE))) {
Message(MSG_GTZERO, ids(IDS_HEIGHT));
SetFocus(GetDlgItem(hwnd, DID_NEWCUSTCY));
return FALSE;
}

GetDlgItemText(hwnd, DID_NEWCUSTTEXT, cci.szTextDefault, CCHCCTEXT);

cci.flOptions = 0;
*cci.szDesc = TEXT('\0');
cci.flExtStyleDefault = 0;
cci.flCtrlTypeMask = 0;
cci.cStyleFlags = 0;
cci.aStyleFlags = NULL;
cci.lpfnStyle = NULL;
cci.lpfnSizeToText = NULL;
cci.dwReserved1 = 0;
cci.dwReserved2 = 0;

if (AddCustomLink(&cci, TRUE, FALSE, NULL, NULL))
return TRUE;
else
return FALSE;
}



/************************************************************************
* OpenCustomDialog
*
* Displays the file open dialog and allows a custom DLL to be selected
* and loaded.
*
* History:
*
************************************************************************/

VOID OpenCustomDialog(VOID)
{
BOOL fGotName;
OPENFILENAME ofn;
TCHAR szNewFileName[CCHMAXPATH];
TCHAR szFilter[CCHTEXTMAX];
INT idPrevDlg;

/*
* Begin setting up the globals and the open file dialog structure.
*/
*szNewFileName = CHAR_NULL;

/*
* Build up the filter string.
*/
BuildFilterString(FILE_DLL, szFilter);

ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = ghwndMain;
ofn.hInstance = NULL;
ofn.lpstrFilter = szFilter;
ofn.lpstrCustomFilter = NULL;
ofn.nMaxCustFilter = 0;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szNewFileName;
ofn.nMaxFile = CCHMAXPATH;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrTitle = ids(IDS_DLLOPENTITLE);
ofn.Flags = OFN_HIDEREADONLY | OFN_SHOWHELP | OFN_FILEMUSTEXIST;
ofn.lpstrDefExt = ids(IDS_DLLEXT);
ofn.lpstrInitialDir = NULL;
ofn.lCustData = 0;
ofn.lpfnHook = NULL;
ofn.lpTemplateName = NULL;

/*
* Fire off the dialog box to open the file.
*/
EnteringDialog(DID_COMMONFILEOPENDLL, &idPrevDlg, TRUE);
fGotName = GetOpenFileName(&ofn);
EnteringDialog(idPrevDlg, NULL, FALSE);

if (fGotName)
OpenDLLFile(szNewFileName);
}



/************************************************************************
* OpenDLLFile
*
*
* History:
*
************************************************************************/

STATICFN VOID OpenDLLFile(
LPTSTR pszFileName)
{
HANDLE hmod;
LPFNCCINFOA lpfnInfoA;
LPFNCCINFOW lpfnInfoW;
INT i;
BOOL fSuccess = FALSE;
BOOL fUnicodeDLL;
PCUSTLINK pclT;
INT nControls;
INT nControls2;
LPCCINFO acci;

/*
* Check to see if the DLL has already been loaded.
*/
for (pclT = gpclHead; pclT &&
(pclT->pwcd->fEmulated ||
lstrcmpi(pclT->pszFileName, pszFileName) != 0);
pclT = pclT->pclNext)
;

/*
* Is the DLL already loaded?
*/
if (pclT) {
Message(MSG_CUSTALREADYLOADED, pszFileName);
return;
}

if (!(hmod = LoadLibrary(pszFileName))) {
Message(MSG_CANTLOADDLL, pszFileName);
return;
}

lpfnInfoA = (LPFNCCINFOA)GetProcAddress(hmod, "CustomControlInfoA");
lpfnInfoW = (LPFNCCINFOW)GetProcAddress(hmod, "CustomControlInfoW");

if (!lpfnInfoA && !lpfnInfoW) {
Message(MSG_BADCUSTDLL, pszFileName);
goto Error1;
}

if (lpfnInfoW) {
nControls = (*lpfnInfoW)(NULL);
fUnicodeDLL = TRUE;
}
else {
nControls = (*lpfnInfoA)(NULL);
fUnicodeDLL = FALSE;
}

if (!nControls) {
Message(MSG_CANTINITDLL, pszFileName);
goto Error1;
}

if (!(acci = (LPCCINFO)MyAlloc(nControls * sizeof(CCINFO))))
goto Error1;

if (fUnicodeDLL)
nControls2 = (*lpfnInfoW)(acci);
else
nControls2 = CallCustomInfoA(lpfnInfoA, acci, nControls);

if (!nControls2) {
Message(MSG_CANTINITDLL, pszFileName);
goto Error2;
}

for (i = 0; i < nControls; i++) {
if (!AddCustomLink(&acci[i], FALSE, fUnicodeDLL, pszFileName, hmod))
goto Error2;
}

fSuccess = TRUE;

Error2:
MyFree(acci);

Error1:
if (!fSuccess)
FreeLibrary(hmod);
}



/************************************************************************
* CallCustomInfoA
*
* Thunks the call from the unicode DlgEdit to the ANSI custom control
* info procedure.
*
* History:
*
************************************************************************/

STATICFN UINT CallCustomInfoA(
LPFNCCINFOA lpfnInfoA,
LPCCINFO acciW,
INT nControls)
{
LPCCINFOA acciA;
INT nControls2;
INT i;
INT j;
LPCCSTYLEFLAGA lpFlagsA;
LPCCSTYLEFLAGW aFlagsW = NULL;
INT cch;

/*
* Allocate the appropriate number of ANSI info structures.
*/
if (!(acciA = (LPCCINFOA)MyAlloc(nControls * sizeof(CCINFOA))))
return 0;

/*
* Call the ANSI info function.
*/
if (nControls2 = (*lpfnInfoA)(acciA)) {
/*
* Copy all the ANSI structures to the UNICODE structures,
* converting strings to UNICODE as we go.
*/
for (i = 0; i < nControls; i++) {
MultiByteToWideChar(CP_ACP, 0, acciA[i].szClass, -1,
acciW[i].szClass, CCHCCCLASS);
acciW[i].flOptions = acciA[i].flOptions;
MultiByteToWideChar(CP_ACP, 0, acciA[i].szDesc, -1,
acciW[i].szDesc, CCHCCDESC);
acciW[i].cxDefault = acciA[i].cxDefault;
acciW[i].cyDefault = acciA[i].cyDefault;
acciW[i].flStyleDefault = acciA[i].flStyleDefault;
acciW[i].flExtStyleDefault = acciA[i].flExtStyleDefault;
acciW[i].flCtrlTypeMask = acciA[i].flCtrlTypeMask;
MultiByteToWideChar(CP_ACP, 0, acciA[i].szTextDefault, -1,
acciW[i].szTextDefault, CCHCCTEXT);

/*
* Is there a table of style flags? If so, we need to build
* up a table of unicode style flags. Note that since we
* allocate this table, the table must be freed when the
* custom link is destroyed!
*/
if (acciA[i].cStyleFlags) {
/*
* If they specified that there are style flags, the pointer
* to the table must not be NULL.
*/
if (!acciA[i].aStyleFlags)
return 0;

if (!(aFlagsW = (LPCCSTYLEFLAGW)MyAlloc(
acciA[i].cStyleFlags * sizeof(CCSTYLEFLAGW))))
return 0;

/*
* Copy all the flags to the new unicode style flag table.
*/
for (j = 0, lpFlagsA = acciA[i].aStyleFlags;
j < acciA[i].cStyleFlags; j++, lpFlagsA++) {
aFlagsW[j].flStyle = lpFlagsA->flStyle;
aFlagsW[j].flStyleMask = lpFlagsA->flStyleMask;

cch = lstrlenA(lpFlagsA->pszStyle) + 1;
aFlagsW[j].pszStyle = (LPWSTR)MyAlloc(cch * sizeof(WCHAR));

if (!aFlagsW[j].pszStyle)
return 0;

MultiByteToWideChar(CP_ACP, 0, lpFlagsA->pszStyle, -1,
aFlagsW[j].pszStyle, cch);
}
}

acciW[i].cStyleFlags = acciA[i].cStyleFlags;
acciW[i].aStyleFlags = aFlagsW;

acciW[i].lpfnStyle = (LPFNCCSTYLE)acciA[i].lpfnStyle;
acciW[i].lpfnSizeToText = (LPFNCCSIZETOTEXT)acciA[i].lpfnSizeToText;
acciW[i].dwReserved1 = acciA[i].dwReserved1;
acciW[i].dwReserved2 = acciA[i].dwReserved2;
}
}

MyFree(acciA);

return nControls2;
}



/************************************************************************
* SelCustDialog
*
* Displays the Select Custom Control dialog to choose which custom
* control tool should be selected.
*
* History:
*
************************************************************************/

PWINDOWCLASSDESC SelCustDialog(VOID)
{
if (DlgBox(DID_SELCUST, (WNDPROC)SelCustDlgProc) == IDOK)
return pwcdChosen;
else
return NULL;
}



/************************************************************************
* SelCustDlgProc
*
* This is the Select Custom Control dialog procedure.
*
* History:
*
************************************************************************/

DIALOGPROC SelCustDlgProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
SelCustInit(hwnd);
return TRUE;

case WM_COMMAND:
switch (LOWORD(wParam)) {
case DID_SELCUSTLIST:
switch (HIWORD(wParam)) {
case LBN_DBLCLK:
if (SelCustOK(hwnd))
EndDialog(hwnd, IDOK);

break;

case LBN_SELCHANGE:
SelCustSelect(hwnd);
break;
}

break;

case IDOK:
if (SelCustOK(hwnd))
EndDialog(hwnd, IDOK);

break;

case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;

case IDHELPDLG:
WinHelp(ghwndMain, gszHelpFile, HELP_CONTEXT,
HELPID_SELCUST);
break;
}

return TRUE;

default:
return FALSE;
}
}



/************************************************************************
* SelCustInit
*
* Processes the WM_INITDIALOG message for the Select Custom Control
* dialog procedure.
*
* History:
*
************************************************************************/

STATICFN VOID SelCustInit(
HWND hwnd)
{
HWND hwndLB;
INT i;
PCUSTLINK pcl;
LPTSTR pszDesc;

hwndLB = GetDlgItem(hwnd, DID_SELCUSTLIST);

/*
* Insert each custom control into the listbox.
*/
for (pcl = gpclHead; pcl; pcl = pcl->pclNext) {
/*
* Use the short description, if the control has one,
* otherwise use the class name itself.
*/
if (pcl->pszDesc)
pszDesc = pcl->pszDesc;
else
pszDesc = pcl->pwcd->pszClass;

i = (INT)SendMessage(hwndLB, LB_ADDSTRING, 0, (DWORD)pszDesc);
SendMessage(hwndLB, LB_SETITEMDATA, i, (DWORD)pcl);
}

hwndCustomSample = NULL;

/*
* Select the first item.
*/
SendMessage(hwndLB, LB_SETCURSEL, 0, 0L);
SelCustSelect(hwnd);

CenterWindow(hwnd);
}



/************************************************************************
* SelCustSelect
*
* Called every time that a different control is selected in the list box
* in the Select Custom Control dialog. It will create a sample control
* and show it in the Sample box.
*
* History:
*
************************************************************************/

STATICFN VOID SelCustSelect(
HWND hwnd)
{
HWND hwndLB;
INT iSelect;
PCUSTLINK pcl;
PWINDOWCLASSDESC pwcd;
LPTSTR pszClass;
RECT rc;
RECT rcParent;
HWND hwndParent;
INT x;
INT y;
INT cx;
INT cy;
INT cxParent;
INT cyParent;

hwndLB = GetDlgItem(hwnd, DID_SELCUSTLIST);

if ((iSelect = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0)) == LB_ERR)
return;

/*
* Get a pointer to the custom control link (stored in the listbox
* items data field).
*/
pcl = (PCUSTLINK)SendMessage(hwndLB, LB_GETITEMDATA, iSelect, 0L);
pwcd = pcl->pwcd;

/*
* Get the coordinates of the Sample box.
*/
hwndParent = GetDlgItem(hwnd, DID_SELCUSTSAMPLE);
GetWindowRect(hwndParent, &rcParent);
ScreenToClientRect(hwnd, &rcParent);
cxParent = (rcParent.right - rcParent.left) - (2 * SAMPLEMARGIN);
cyParent = (rcParent.bottom - rcParent.top) - (2 * SAMPLEMARGIN);

/*
* Calculate the window size of the sample control.
*/
SetRect(&rc, 0, 0, pwcd->cxDefault, pwcd->cyDefault);
DUToWinRect(&rc);
cx = rc.right - rc.left;
cy = rc.bottom - rc.top;

/*
* Be sure that the control can fit within the sample box. Adjust
* it down if necessary.
*/
if (cx < cxParent) {
x = ((cxParent - cx) / 2) + SAMPLEMARGIN;
}
else {
x = SAMPLEMARGIN;
cx = cxParent;
}

if (cy < cyParent) {
y = ((cyParent - cy) / 2) + SAMPLEMARGIN;
}
else {
y = SAMPLEMARGIN;
cy = cyParent;
}

x += rcParent.left;
y += rcParent.top;

/*
* Destroy the old sample.
*/
if (hwndCustomSample)
DestroyWindow(hwndCustomSample);

/*
* Get the class name to use.
* If the control is emulated, use the special emulator class.
* Otherwise, it is an installed custom control, and we can use
* it's real class string.
*/
if (pwcd->fEmulated)
pszClass = szCustomClass;
else
pszClass = pwcd->pszClass;

/*
* Create the sample control. We always create it visible here,
* even if the style says it isn't.
*/
hwndCustomSample = CreateWindow(
pszClass,
pwcd->pszTextDefault,
pwcd->flStyles | WS_VISIBLE,
x, y, cx, cy,
hwnd,
0,
ghInst,
NULL);
}



/************************************************************************
* SelCustOK
*
* Processes the final selection of a custom control from the
* Select Custom Control dialog.
*
* History:
*
************************************************************************/

STATICFN BOOL SelCustOK(
HWND hwnd)
{
HWND hwndLB;
INT iSelect;
PCUSTLINK pcl;

hwndLB = GetDlgItem(hwnd, DID_SELCUSTLIST);

if ((iSelect = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0)) == LB_ERR)
return FALSE;

/*
* Get a pointer to the custom control link (stored in the listbox
* items data field).
*/
pcl = (PCUSTLINK)SendMessage(hwndLB, LB_GETITEMDATA, iSelect, 0L);

pwcdChosen = pcl->pwcd;

return TRUE;
}



/************************************************************************
* RemCustDlgProc
*
* This is the Remove Custom Control dialog procedure.
* It is used to de-install a custom control.
*
* History:
*
************************************************************************/

DIALOGPROC RemCustDlgProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
RemCustInit(hwnd);
return TRUE;

case WM_COMMAND:
switch (LOWORD(wParam)) {
case DID_REMCUSTLIST:
if (HIWORD(wParam) == LBN_DBLCLK) {
if (RemCustOK(hwnd))
EndDialog(hwnd, IDOK);
}

break;

case IDOK:
if (RemCustOK(hwnd))
EndDialog(hwnd, IDOK);

break;

case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;

case IDHELPDLG:
WinHelp(ghwndMain, gszHelpFile, HELP_CONTEXT,
HELPID_REMCUST);
break;
}

return TRUE;

default:
return FALSE;
}
}



/************************************************************************
* RemCustInit
*
* Processes the WM_INITDIALOG message for the Remove Custom Control
* dialog procedure.
*
* History:
*
************************************************************************/

STATICFN VOID RemCustInit(
HWND hwnd)
{
HWND hwndLB;
INT i;
PCUSTLINK pcl;
LPTSTR pszDesc;

hwndLB = GetDlgItem(hwnd, DID_REMCUSTLIST);

/*
* Insert each custom control into the listbox.
*/
for (pcl = gpclHead; pcl; pcl = pcl->pclNext) {
/*
* Use the short description, if the control has one,
* otherwise use the class name itself.
*/
if (pcl->pszDesc)
pszDesc = pcl->pszDesc;
else
pszDesc = pcl->pwcd->pszClass;

i = (INT)SendMessage(hwndLB, LB_ADDSTRING, 0, (DWORD)pszDesc);
SendMessage(hwndLB, LB_SETITEMDATA, i, (DWORD)pcl);
}

/*
* Select the first item.
*/
SendMessage(hwndLB, LB_SETCURSEL, 0, 0L);

CenterWindow(hwnd);
}



/************************************************************************
* RemCustOK
*
* Processes the selection of a custom control to delete from the
* Remove Custom Control dialog.
*
* History:
*
************************************************************************/

STATICFN BOOL RemCustOK(
HWND hwnd)
{
HWND hwndLB;
INT iSelect;
PCUSTLINK pcl;
NPCTYPE npc;

hwndLB = GetDlgItem(hwnd, DID_REMCUSTLIST);

if ((iSelect = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0)) != LB_ERR) {
/*
* Get a pointer to the custom control link (stored in the listbox
* items data field).
*/
pcl = (PCUSTLINK)SendMessage(hwndLB, LB_GETITEMDATA, iSelect, 0L);

/*
* Cannot delete if any controls in the current dialog
* are of this type.
*/
for (npc = npcHead; npc; npc = npc->npcNext) {
if (pcl->pwcd == npc->pwcd) {
Message(MSG_CUSTCNTLINUSE);
return FALSE;
}
}

RemoveCustomLink(pcl);
}

return TRUE;
}



/****************************************************************************
* CustomWndProc
*
* This is the window procedure for the emulated Custom control.
*
* History:
*
****************************************************************************/

WINDOWPROC CustomWndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg) {
case WM_PAINT:
{
HDC hDC;
PAINTSTRUCT ps;
RECT rc;
TCHAR szText[CCHTEXTMAX];

hDC = BeginPaint(hwnd, &ps);

SelectObject(hDC, GetStockObject(LTGRAY_BRUSH));
GetClientRect(hwnd, &rc);
Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
GetWindowText(hwnd, szText, CCHTEXTMAX);
#ifdef JAPAN
{
TCHAR szTmp[CCHTEXTMAX];

KDExpandCopy(szTmp, szText, CCHTEXTMAX);
lstrcpy(szText, szTmp);
}
#endif
SetBkMode(hDC, TRANSPARENT);

if (gcd.hFont)
SelectObject(hDC, gcd.hFont);

DrawText(hDC, szText, -1, &rc,
DT_CENTER | DT_NOCLIP | DT_VCENTER | DT_SINGLELINE);

EndPaint(hwnd, &ps);
}

break;

default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}

return 0;
}



/************************************************************************
* AddCustomLink
*
* Adds a new custom control to the linked list.
*
* Note that normally duplicates are checked for, but it allows multiple
* links to be added with the same class if it is a DLL control. This
* is to support multiple control types being added from the same DLL.
* Because of this, if the caller is adding a non-emulated link, they
* are responsible for checking the list for duplicates first!
*
* There is one special case. If it is adding a DLL link, and an
* emulated link with the same class name is found, it will walk the
* current list of controls and replace all of them with the new DLL
* control type, then delete the emulated link. This is to support
* the case where the user creates some controls of class FOO, where
* FOO is emulated, then later loads the FOO DLL. All controls of
* this emulated class will be changed to be the real FOO class, and
* the DLL FOO link replaces the emulated one.
*
* History:
*
************************************************************************/

PCUSTLINK AddCustomLink(
LPCCINFO pcci,
BOOL fEmulated,
BOOL fUnicodeDLL,
LPTSTR pszFileName,
HANDLE hmod)
{
PCUSTLINK pcl;
PCUSTLINK pclT;
PCUSTLINK pclPrev;
NPCTYPE npc;
HWND hwndOld;

if (!(pcl = AllocCUSTLINK(pcci, fEmulated, fUnicodeDLL, pszFileName, hmod)))
return NULL;

if (fEmulated) {
/*
* Search the list for another link with the same class.
*/
for (pclT = gpclHead;
pclT && lstrcmpi(pclT->pwcd->pszClass, pcci->szClass) != 0;
pclT = pclT->pclNext)
;

/*
* Was a duplicate found?
*/
if (pclT) {
FreeCUSTLINK(pcl);
Message(MSG_CUSTALREADYLOADED, pcci->szClass);

return NULL;
}
}
else {
/*
* Search the list for another link with the same class that
* is an emulated control.
*/
for (pclT = gpclHead;
pclT &&
(lstrcmpi(pclT->pwcd->pszClass, pcci->szClass) != 0 ||
!pclT->pwcd->fEmulated);
pclT = pclT->pclNext)
;

/*
* Was a duplicate found?
*/
if (pclT) {
/*
* At this point we know that this is a DLL link replacing
* an existing emulated control class. We want to go through
* the existing controls and replace any of this class with
* the new DLL class. This allows a user to load a dialog
* with some emulated controls, then later install the custom
* DLL and have all the existing controls of that class
* change to show the real control.
*/
for (npc = npcHead; npc; npc = npc->npcNext) {
/*
* Is the control of the type that we are replacing?
*/

if (npc->pwcd == pclT->pwcd) { 
hwndOld = npc->hwnd;

/*
* Unsubclass the old control window, then switch
* the pwcd pointer before calling CreateControl.
*/
SetWindowLong(hwndOld, GWL_WNDPROC,
(DWORD)npc->pwcd->pfnOldWndProc);
UNSETPCINTOHWND(hwndOld);
npc->pwcd = pcl->pwcd;

/*
* Create a control of the new type in the same position.
*/
if (CreateControl(npc, npc->text, npc->flStyle,
npc->flExtStyle, npc->id, &npc->rc,
hwndOld, NULL)) {
/*
* Get rid of the old control window.
*/
DestroyWindow(hwndOld);

/*
* Adjust the size and position of its drag window.
*/
SizeDragToControl(npc);
}
}
}

/*
* Remove the old link, now that all the controls that
* used it are gone.
*/
RemoveCustomLink(pclT);
}
}

/*
* Search for the end of the list. Get a pointer to the last link.
*/
for (pclT = gpclHead, pclPrev = NULL; pclT;
pclPrev = pclT, pclT = pclT->pclNext)
;

/*
* Add the new link to the list. Add it to the end if there are
* other links, or initialize the head pointer if this is the
* first one.
*/
if (pclPrev)
pclPrev->pclNext = pcl;
else
gpclHead = pcl;

return pcl;
}



/************************************************************************
* AllocCUSTLINK
*
* Allocates a CUSTLINK structure and initializes it. This includes
* allocating an associated WINDOWCLASSDESC structure.
*
* History:
*
************************************************************************/

STATICFN PCUSTLINK AllocCUSTLINK(
LPCCINFO pcci,
BOOL fEmulated,
BOOL fUnicodeDLL,
LPTSTR pszFileName,
HANDLE hmod)
{
PCUSTLINK pcl;
PWINDOWCLASSDESC pwcd;

if (!(pwcd = (PWINDOWCLASSDESC)MyAlloc(sizeof(WINDOWCLASSDESC))))
return NULL;

/*
* Initialize the structure to be like an emulated custom control.
*/
*pwcd = awcd[W_CUSTOM];

/*
* Now override some values.
*/
pwcd->flStyles = pcci->flStyleDefault;
pwcd->flExtStyle = pcci->flExtStyleDefault;
pwcd->cxDefault = pcci->cxDefault;
pwcd->cyDefault = pcci->cyDefault;
pwcd->fEmulated = fEmulated;
pwcd->fUnicodeDLL = fUnicodeDLL;
pwcd->hmod = hmod;
pwcd->cStyleFlags = pcci->cStyleFlags;
pwcd->aStyleFlags = pcci->aStyleFlags;
pwcd->lpfnStyle = (PROC)pcci->lpfnStyle;
pwcd->lpfnSizeToText = (PROC)pcci->lpfnSizeToText;
pwcd->flCtrlTypeMask = pcci->flCtrlTypeMask;

if (pcci->flOptions & CCF_NOTEXT)
pwcd->fHasText = FALSE;
else
pwcd->fHasText = TRUE;

if (pcci->lpfnSizeToText && pwcd->fHasText)
pwcd->fSizeToText = TRUE;

/*
* Copy the class name.
*/
if (!(pwcd->pszClass = NameOrdDup(pcci->szClass)))
goto error1;

/*
* Copy the default text. This is an optional field.
*/
if (*pcci->szTextDefault) {
if (!(pwcd->pszTextDefault = NameOrdDup(pcci->szTextDefault)))
goto error2;
}
else {
pwcd->pszTextDefault = NULL;
}

if (!(pcl = (PCUSTLINK)MyAlloc(sizeof(CUSTLINK))))
goto error3;

/*
* Copy the DLL file name (NULL for emulated controls).
*/
if (pszFileName && *pszFileName) {
if (!(pcl->pszFileName = NameOrdDup(pszFileName)))
goto error4;
}
else {
pcl->pszFileName = NULL;
}

/*
* Copy the descriptive text. This is an optional field.
*/
if (*pcci->szDesc) {
if (!(pcl->pszDesc = NameOrdDup(pcci->szDesc)))
goto error5;
}
else {
pcl->pszDesc = NULL;
}

pcl->pclNext = NULL;
pcl->pwcd = pwcd;

return pcl;

error5:
if (pcl->pszFileName)
MyFree(pcl->pszFileName);

error4:
MyFree(pcl);

error3:
if (pwcd->pszTextDefault)
MyFree(pwcd->pszTextDefault);

error2:
MyFree(pwcd->pszClass);

error1:
MyFree(pwcd);

return NULL;
}



/************************************************************************
* RemoveCustomLink
*
* Removes and frees a custom control link from the list.
*
* History:
*
************************************************************************/

VOID RemoveCustomLink(
PCUSTLINK pclFree)
{
PCUSTLINK pcl;
PCUSTLINK pclPrev;

/*
* Search for the link in the list.
*/
for (pcl = gpclHead, pclPrev = NULL; pcl != pclFree;
pclPrev = pcl, pcl = pcl->pclNext)
;

/*
* Link was not found.
*/
if (!pcl)
return;

/*
* Remove the link from the list.
*/
if (pclPrev)
pclPrev->pclNext = pclFree->pclNext;
else
gpclHead = pclFree->pclNext;

/*
* Finally, free the link completely.
*/
FreeCUSTLINK(pclFree);
}



/************************************************************************
* FreeCUSTLINK
*
* Frees a CUSTLINK structure. This includes freeing the
* associated WINDOWCLASSDESC structure.
*
* History:
*
************************************************************************/

STATICFN VOID FreeCUSTLINK(
PCUSTLINK pclFree)
{
PCUSTLINK pcl;
INT i;

/*
* Do we need to unload the associated DLL?
*/
if (pclFree->pwcd->hmod) {
/*
* Run throught the custom list looking to see if any other
* installed custom control has the same module handle as the
* one that we are freeing.
*/
for (pcl = gpclHead;
pcl &&
(pcl == pclFree || pcl->pwcd->hmod != pclFree->pwcd->hmod);
pcl = pcl->pclNext)
;

/*
* If none were found, it is safe to unload this library.
* Otherwise, we must leave the library loaded for the
* others!
*/
if (!pcl)
FreeLibrary(pclFree->pwcd->hmod);
}

MyFree(pclFree->pwcd->pszClass);

if (pclFree->pwcd->pszTextDefault)
MyFree(pclFree->pwcd->pszTextDefault);

/*
* Is this a non-unicode DLL? If so, then when it was loaded,
* the dialog editor allocated a table of unicode style strings.
* This table must now be freed. If the DLL was a unicode one,
* then the table pointed to by aStyleFlags belongs to the DLL,
* and it must NOT be freed.
*/
if (pclFree->pwcd->hmod && !pclFree->pwcd->fUnicodeDLL) {
for (i = 0; i < pclFree->pwcd->cStyleFlags; i++)
MyFree(pclFree->pwcd->aStyleFlags[i].pszStyle);

if (pclFree->pwcd->aStyleFlags)
MyFree(pclFree->pwcd->aStyleFlags);
}

MyFree(pclFree->pwcd);

if (pclFree->pszFileName)
MyFree(pclFree->pszFileName);

if (pclFree->pszDesc)
MyFree(pclFree->pszDesc);

MyFree(pclFree);
}



/************************************************************************
* CallCustomStyle
*
*
* History:
*
************************************************************************/

BOOL CallCustomStyle(
NPCTYPE npc,
PDWORD pflStyleNew,
PDWORD pflExtStyleNew,
LPTSTR pszTextNew)
{
CCSTYLE ccs;
CCSTYLEA ccsA;
BOOL fSuccess;
BOOL fDefCharUsed;
INT idPrevDlg;

/*
* Because we are about ready to display the dialog, we need to
* call EnteringDialog so that the properties bar, toolbox and
* work mode dialog get disabled. The first parameter is the
* dialog id, used so that the proper help will be brought up
* for this dialog. Since we don't have a meaningful help screen
* for any old random custom control, just pass in a value of
* zero, which will cause the Help Contents screen to be
* brought up if the user presses F1 while the dialog is up.
*/
EnteringDialog(0, &idPrevDlg, TRUE);

/*
* Is this a UNICODE DLL?
*/
if (npc->pwcd->fUnicodeDLL) {
ccs.flStyle = *pflStyleNew;
ccs.flExtStyle = *pflExtStyleNew;
lstrcpy(ccs.szText, pszTextNew);
ccs.lgid = gcd.di.wLanguage;
ccs.wReserved1 = 0;

fSuccess = ((LPFNCCSTYLE)(*npc->pwcd->lpfnStyle))(ghwndMain, &ccs);

if (fSuccess) {
*pflStyleNew = ccs.flStyle;
*pflExtStyleNew = ccs.flExtStyle;
lstrcpy(pszTextNew, ccs.szText);
}
}
else {
ccsA.flStyle = *pflStyleNew;
ccsA.flExtStyle = *pflExtStyleNew;
WideCharToMultiByte(CP_ACP, 0, pszTextNew, -1, ccsA.szText, CCHCCTEXT,
NULL, &fDefCharUsed);
ccsA.lgid = gcd.di.wLanguage;
ccsA.wReserved1 = 0;

fSuccess = ((LPFNCCSTYLEA)(*npc->pwcd->lpfnStyle))(ghwndMain, &ccsA);

if (fSuccess) {
*pflStyleNew = ccsA.flStyle;
*pflExtStyleNew = ccsA.flExtStyle;
MultiByteToWideChar(CP_ACP, 0, ccsA.szText, -1, pszTextNew,
CCHTEXTMAX);
}
}

EnteringDialog(idPrevDlg, NULL, FALSE);

return fSuccess;
}



/************************************************************************
* CallCustomSizeToText
*
*
*
* Returns:
*
* History:
*
************************************************************************/

INT CallCustomSizeToText(
NPCTYPE npc)
{
INT x;
INT xDU;
BOOL fDefCharUsed;
CHAR szTextA[CCHTEXTMAX];
PSTR pszTextA;

/*
* Does this custom control have a SizeToText function?
*/
if (!npc->pwcd->lpfnSizeToText)
return -1;

/*
* Is this a UNICODE DLL that we are calling to?
*/
if (npc->pwcd->fUnicodeDLL) {
x = ((LPFNCCSIZETOTEXT)(*npc->pwcd->lpfnSizeToText))
(npc->flStyle, npc->flExtStyle, gcd.hFont, npc->text);
}
else {
/*
* No, not a UNICODE DLL. We must convert from UNICODE to
* ANSI first. NULL text cases must be handled properly.
*/
if (npc->text) {
WideCharToMultiByte(CP_ACP, 0, npc->text, -1, szTextA, CCHTEXTMAX,
NULL, &fDefCharUsed);
pszTextA = szTextA;
}
else {
pszTextA = NULL;
}

x = ((LPFNCCSIZETOTEXTA)(*npc->pwcd->lpfnSizeToText))
(npc->flStyle, npc->flExtStyle, gcd.hFont, pszTextA);
}

/*
* Did the call to the DLL fail?
*/
if (x == -1)
return -1;

/*
* Convert the size in pixels to a size in Dialog Units. Be sure
* that we round any fraction up to the next higher DU. Since
* we know how wide the control must be to fit the text, we must
* be sure that the size does not get rounded down below this
* value when converting to DU's.
*/
xDU = MulDiv(x, 4, gcd.cxChar);
if (MulDiv(xDU, gcd.cxChar, 4) != x)
xDU++;

return xDU;
}



/************************************************************************
* ReadCustomProfile
*
*
* History:
*
************************************************************************/

VOID ReadCustomProfile(VOID)
{
TCHAR szBuf[CCHTEXTMAX];
TCHAR szBuf2[CCHTEXTMAX];
LPTSTR pszKey;

GetPrivateProfileString(szCustomDLL, NULL, szEmpty,
szBuf, CCHTEXTMAX, ids(IDS_DLGEDITINI));

/*
* Get the file name for each custom control DLL and load it.
*/
for (pszKey = szBuf; *pszKey; pszKey += lstrlen(pszKey) + 1) {
if (GetPrivateProfileString(szCustomDLL, pszKey, szEmpty,
szBuf2, CCHTEXTMAX, ids(IDS_DLGEDITINI)))
OpenDLLFile(szBuf2);
}
}



/************************************************************************
* WriteCustomProfile
*
*
* History:
*
************************************************************************/

VOID WriteCustomProfile(VOID)
{
PCUSTLINK pcl;
PCUSTLINK pcl2;
BOOL fSecond;

/*
* Clear out the section.
*/
WritePrivateProfileString(szCustomDLL, NULL, NULL, ids(IDS_DLGEDITINI));

for (pcl = gpclHead; pcl; pcl = pcl->pclNext) {
/*
* Only write out installed DLL's, not emulated controls.
*/
if (pcl->pszFileName) {
/*
* Before writing out the path to the DLL, be sure
* that this DLL's path has not been written out
* already. This would only occur if they have
* multiple control types within the DLL.
*/
for (pcl2 = gpclHead, fSecond = FALSE;
pcl2 && pcl2 != pcl; pcl2 = pcl2->pclNext) {
if (lstrcmpi(pcl2->pszFileName, pcl->pszFileName) == 0) {
fSecond = TRUE;
break;
}
}

if (!fSecond)
WritePrivateProfileString(szCustomDLL, pcl->pwcd->pszClass,
pcl->pszFileName, ids(IDS_DLGEDITINI));
}
}
}