NSIS/Contrib/InstallOptions/InstallerOptions.cpp
2010-03-30 17:50:08 +00:00

1664 lines
52 KiB
C++

/*********************************************************
*
* InstallOptions version 2.0 - Plugin for custom pages
*
* See Readme.html for documentation and license
*
* Unicode support by Jim Park -- 08/01/2007
*
*********************************************************/
#include <windows.h>
#include <windowsx.h>
#include <shlobj.h>
#include <commdlg.h>
#include <cderr.h>
#include "resource.h"
#include "shellapi.h"
#include <nsis/pluginapi.h> // nsis plugin
#ifdef _countof
#define COUNTOF _countof
#else
#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))
#endif
// Use for functions only called from one place to possibly reduce some code
// size. Allows the source code to remain readable by leaving the function
// intact.
#ifdef _MSC_VER
#define INLINE __forceinline
#else
#define INLINE inline
#endif
void *WINAPI MALLOC(int len) { return (void*)GlobalAlloc(GPTR,len); }
void WINAPI FREE(void *d) { if (d) GlobalFree((HGLOBAL)d); }
#define strcpy(x,y) lstrcpy(x,y)
//#define strncpy(x,y,z) lstrcpyn(x,y,z)
#define strdup(x) STRDUP(x)
#define stricmp(x,y) lstrcmpi(x,y)
//#define abs(x) ((x) < 0 ? -(x) : (x))
TCHAR *WINAPI STRDUP(const TCHAR *c)
{
TCHAR *t=(TCHAR*)MALLOC((lstrlen(c)+1)*sizeof(TCHAR));
return lstrcpy(t,c);
}
// Turn a pair of chars into a word
// Turn four chars into a dword
#ifdef __BIG_ENDIAN__ // Not very likely, but, still...
#define CHAR2_TO_WORD(a,b) (((WORD)(b))|((a)<<8))
#define CHAR4_TO_DWORD(a,b,c,d) (((DWORD)CHAR2_TO_WORD(c,d))|(CHAR2_TO_WORD(a,b)<<16))
#else
#define CHAR2_TO_WORD(a,b) (((WORD)(a))|((b)<<8))
#define CHAR4_TO_DWORD(a,b,c,d) (((DWORD)CHAR2_TO_WORD(a,b))|(CHAR2_TO_WORD(c,d)<<16))
#endif
// Field types
// NB - the order of this list is important - see below
#define FIELD_INVALID (0)
#define FIELD_HLINE (1)
#define FIELD_VLINE (2)
#define FIELD_LABEL (3)
#define FIELD_ICON (4)
#define FIELD_BITMAP (5)
#define FIELD_BROWSEBUTTON (6)
#define FIELD_LINK (7)
#define FIELD_BUTTON (8)
#define FIELD_GROUPBOX (9)
#define FIELD_CHECKBOX (10)
#define FIELD_RADIOBUTTON (11)
#define FIELD_TEXT (12)
#define FIELD_FILEREQUEST (13)
#define FIELD_DIRREQUEST (14)
#define FIELD_COMBOBOX (15)
#define FIELD_LISTBOX (16)
#define FIELD_SETFOCUS FIELD_CHECKBOX // First field that qualifies for having the initial keyboard focus
#define FIELD_CHECKLEN FIELD_TEXT // First field to have length of state value checked against MinLen/MaxLen
//---------------------------------------------------------------------
// settings
#define IO_ENABLE_LINK
//#define IO_LINK_UNDERLINED // Uncomment to show links text underlined
//---------------------------------------------------------------------
// Flags
// LBS_NOTIFY 0x00000001 // LISTBOX/CHECKBOX/RADIOBUTTON/BUTTON/LINK - Notify NSIS script when control is "activated" (exact meaning depends on the type of control)
// OFN_OVERWRITEPROMPT 0x00000002 // FILEREQUEST
// OFN_HIDEREADONLY 0x00000004 // FILEREQUEST
// LBS_MULTIPLESEL 0x00000008 // LISTBOX
#define FLAG_READONLY 0x00000010 // TEXT/FILEREQUEST/DIRREQUEST
// BS_LEFTTEXT 0x00000020 // CHECKBOX/RADIOBUTTON
#define TRANSPARENT_BMP 0x00000020 // BITMAP
#define FLAG_PASSWORD 0x00000040 // TEXT/FILEREQUEST/DIRREQUEST
#define FLAG_ONLYNUMBERS 0x00000080 // TEXT/FILEREQUEST/DIRREQUEST
#define FLAG_MULTILINE 0x00000100 // TEXT/FILEREQUEST/DIRREQUEST
#define FLAG_NOWORDWRAP 0x00000200 // TEXT/FILEREQUEST/DIRREQUEST - Disable word-wrap in multi-line text boxes
#define FLAG_WANTRETURN 0x00000400 // TEXT/FILEREQUEST/DIRREQUEST
// LBS_EXTENDEDSEL 0x00000800 // LISTBOX
// OFN_PATHMUSTEXIST 0x00000800 // FILEREQUEST
// OFN_FILEMUSTEXIST 0x00001000 // FILEREQUEST
// OFN_CREATEPROMPT 0x00002000 // FILEREQUEST
#define FLAG_DROPLIST 0x00004000 // COMBOBOX
#define FLAG_RESIZETOFIT 0x00008000 // BITMAP
// WS_TABSTOP 0x00010000 // *ALL*
// WS_GROUP 0x00020000 // *ALL*
#define FLAG_SAVEAS 0x00040000 // FILEREQUEST - Show "Save As" instead of "Open" for FileRequest field
// OFN_EXPLORER 0x00080000 // FILEREQUEST
// WS_HSCROLL 0x00100000 // *ALL*
// WS_VSCROLL 0x00200000 // *ALL*
// WS_DISABLED 0x08000000 // *ALL*
#define FLAG_FOCUS 0x10000000 // Controls that can receive focus
struct TableEntry {
TCHAR *pszName;
int nValue;
};
int WINAPI LookupToken(TableEntry*, TCHAR*);
int WINAPI LookupTokens(TableEntry*, TCHAR*);
void WINAPI ConvertNewLines(TCHAR *str);
// all allocated buffers must be first in the struct
// when adding more allocated buffers to FieldType, don't forget to change this define
#define FIELD_BUFFERS 6
struct FieldType {
TCHAR *pszText;
TCHAR *pszState;
TCHAR *pszRoot;
TCHAR *pszListItems;
TCHAR *pszFilter;
TCHAR *pszValidateText;
int nMinLength;
int nMaxLength;
int nType;
RECT rect;
int nFlags;
HWND hwnd;
UINT nControlID;
int nParentIdx; // this is used to store original windowproc for LINK
HANDLE hImage; // this is used by image/icon field to save the handle to the image
int nField; // field number in INI file
TCHAR *pszHwndEntry; // "HWND" or "HWND2"
long wndProc;
};
// initial buffer size. buffers will grow as required.
// use a value larger than MAX_PATH to prevent need for excessive growing.
#define BUFFER_SIZE 8192 // 8kb of mem is max char count in multiedit
TCHAR szBrowseButtonCaption[] = _T("...");
HWND hConfigWindow = NULL;
HWND hMainWindow = NULL;
HWND hCancelButton = NULL;
HWND hNextButton = NULL;
HWND hBackButton = NULL;
HINSTANCE m_hInstance = NULL;
struct _stack_t *pFilenameStackEntry = NULL;
TCHAR *pszFilename = NULL;
TCHAR *pszTitle = NULL;
TCHAR *pszCancelButtonText = NULL;
TCHAR *pszNextButtonText = NULL;
TCHAR *pszBackButtonText = NULL;
int bBackEnabled = FALSE;
int bCancelEnabled = FALSE; // by ORTIM: 13-August-2002
int bCancelShow = FALSE; // by ORTIM: 13-August-2002
int bRTL = FALSE;
FieldType *pFields = NULL;
#define DEFAULT_RECT 1018
int nRectId = 0;
int nNumFields = 0;
int g_done;
int g_NotifyField; // Field number of notifying control
int WINAPI FindControlIdx(UINT id)
{
for (int nIdx = 0; nIdx < nNumFields; nIdx++)
if (id == pFields[nIdx].nControlID)
return nIdx;
return -1;
}
LRESULT WINAPI mySendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
return SendMessage(hWnd, Msg, wParam, lParam);
}
void WINAPI mySetFocus(HWND hWnd)
{
mySendMessage(hMainWindow, WM_NEXTDLGCTL, (WPARAM)hWnd, TRUE);
}
void WINAPI mySetWindowText(HWND hWnd, LPCTSTR pszText)
{
if (pszText)
SetWindowText(hWnd, pszText);
}
int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) {
static TCHAR szDir[MAX_PATH];
if (uMsg == BFFM_INITIALIZED &&
GetWindowText(pFields[(int)pData].hwnd, szDir, MAX_PATH) > 0)
mySendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)szDir);
return 0;
}
bool INLINE ValidateFields() {
int nIdx;
int nLength;
// In the unlikely event we can't allocate memory, go ahead and return true so we can get out of here.
// May cause problems for the install script, but no memory is problems for us.
for (nIdx = 0; nIdx < nNumFields; nIdx++) {
FieldType *pField = pFields + nIdx;
// this if statement prevents a stupid bug where a min/max length is assigned to a label control
// where the user obviously has no way of changing what is displayed. (can you say, "infinite loop"?)
if (pField->nType >= FIELD_CHECKLEN) {
nLength = mySendMessage(pField->hwnd, WM_GETTEXTLENGTH, 0, 0);
if (((pField->nMaxLength > 0) && (nLength > pField->nMaxLength)) ||
((pField->nMinLength > 0) && (nLength < pField->nMinLength))) {
if (pField->pszValidateText) {
TCHAR szTitle[1024];
GetWindowText(hMainWindow, szTitle, COUNTOF(szTitle));
MessageBox(hConfigWindow, pField->pszValidateText, szTitle, MB_OK|MB_ICONWARNING);
}
mySetFocus(pField->hwnd);
return false;
}
}
}
return true;
}
bool WINAPI SaveSettings(void) {
static TCHAR szField[25];
int nBufLen = BUFFER_SIZE;
TCHAR *pszBuffer = (TCHAR*)MALLOC(nBufLen*sizeof(TCHAR));
if (!pszBuffer) return false;
int nIdx;
int CurrField;
for (nIdx = 0, CurrField = 1; nIdx < nNumFields; nIdx++, CurrField++) {
FieldType *pField = pFields + nIdx;
HWND hwnd = pField->hwnd;
switch (pField->nType) {
case FIELD_BROWSEBUTTON:
if (g_NotifyField > CurrField)
--g_NotifyField;
--CurrField;
default:
continue;
case FIELD_CHECKBOX:
case FIELD_RADIOBUTTON:
wsprintf(pszBuffer, _T("%d"), !!mySendMessage(hwnd, BM_GETCHECK, 0, 0));
break;
case FIELD_LISTBOX:
{
// Ok, this one requires a bit of work.
// First, we allocate a buffer long enough to hold every item.
// Then, we loop through every item and if it's selected we add it to our buffer.
// If there is already an item in the list, then we prepend a | character before the new item.
// We could simplify for single-select boxes, but using one piece of code saves some space.
int nLength = lstrlen(pField->pszListItems) + 10;
if (nLength > nBufLen) {
FREE(pszBuffer);
nBufLen = nLength;
pszBuffer = (TCHAR*)MALLOC(nBufLen*sizeof(TCHAR));
if (!pszBuffer) return false;
}
TCHAR *pszItem = (TCHAR*)MALLOC(nBufLen*sizeof(TCHAR));
if (!pszItem) return false;
*pszBuffer = _T('\0');
int nNumItems = mySendMessage(hwnd, LB_GETCOUNT, 0, 0);
for (int nIdx2 = 0; nIdx2 < nNumItems; nIdx2++) {
if (mySendMessage(hwnd, LB_GETSEL, nIdx2, 0) > 0) {
if (*pszBuffer) lstrcat(pszBuffer, _T("|"));
mySendMessage(hwnd, LB_GETTEXT, (WPARAM)nIdx2, (LPARAM)pszItem);
lstrcat(pszBuffer, pszItem);
}
}
FREE(pszItem);
break;
}
case FIELD_TEXT:
case FIELD_FILEREQUEST:
case FIELD_DIRREQUEST:
case FIELD_COMBOBOX:
{
int nLength = mySendMessage(pField->hwnd, WM_GETTEXTLENGTH, 0, 0);
if (nLength > nBufLen) {
FREE(pszBuffer);
// add a bit extra so we do this less often
nBufLen = nLength + 20;
pszBuffer = (TCHAR*)MALLOC(nBufLen*sizeof(TCHAR));
if (!pszBuffer) return false;
}
*pszBuffer=_T('"');
GetWindowText(hwnd, pszBuffer+1, nBufLen-1);
pszBuffer[nLength+1]=_T('"');
pszBuffer[nLength+2]=_T('\0');
if (pField->nType == FIELD_TEXT && (pField->nFlags & FLAG_MULTILINE))
{
TCHAR *pszBuf2 = (TCHAR*)MALLOC(nBufLen*2*sizeof(TCHAR)); // double the size, consider the worst case, all chars are \r\n
TCHAR *p1, *p2;
for (p1 = pszBuffer, p2 = pszBuf2; *p1; p1 = CharNext(p1), p2 = CharNext(p2))
{
switch (*p1) {
case _T('\t'):
*p2++ = _T('\\');
*p2 = _T('t');
break;
case _T('\n'):
*p2++ = _T('\\');
*p2 = _T('n');
break;
case _T('\r'):
*p2++ = _T('\\');
*p2 = _T('n');
break;
case _T('\\'):
*p2 = _T('\\');
// Jim Park: used to be p2++ but that's a bug that works because
// CharNext()'s behavior at terminating null char. But still
// definitely, unsafe.
default:
lstrcpyn(p2, p1, CharNext(p1) - p1 + 1);
break;
}
}
*p2 = 0;
nBufLen = nBufLen*2;
FREE(pszBuffer);
pszBuffer=pszBuf2;
}
break;
}
}
wsprintf(szField, _T("Field %d"), CurrField);
WritePrivateProfileString(szField, _T("State"), pszBuffer, pszFilename);
}
// Tell NSIS which control was activated, if any
wsprintf(pszBuffer, _T("%d"), g_NotifyField);
WritePrivateProfileString(_T("Settings"), _T("State"), pszBuffer, pszFilename);
FREE(pszBuffer);
return true;
}
#define BROWSE_WIDTH 15
static TCHAR szResult[BUFFER_SIZE];
TCHAR *pszAppName;
DWORD WINAPI myGetProfileString(LPCTSTR lpKeyName)
{
*szResult = _T('\0');
return GetPrivateProfileString(pszAppName, lpKeyName, _T(""), szResult, BUFFER_SIZE, pszFilename);
}
TCHAR * WINAPI myGetProfileStringDup(LPCTSTR lpKeyName)
{
int nSize = myGetProfileString(lpKeyName);
if (nSize)
return strdup(szResult); // uses STRDUP
else
return NULL;
}
UINT WINAPI myGetProfileInt(LPCTSTR lpKeyName, INT nDefault)
{
return GetPrivateProfileInt(pszAppName, lpKeyName, nDefault, pszFilename);
}
int WINAPI ReadSettings(void) {
static TCHAR szField[25];
int nIdx, nCtrlIdx;
pszAppName = _T("Settings");
pszTitle = myGetProfileStringDup(_T("Title"));
pszCancelButtonText = myGetProfileStringDup(_T("CancelButtonText"));
pszNextButtonText = myGetProfileStringDup(_T("NextButtonText"));
pszBackButtonText = myGetProfileStringDup(_T("BackButtonText"));
nNumFields = myGetProfileInt(_T("NumFields"), 0);
nRectId = myGetProfileInt(_T("Rect"), DEFAULT_RECT);
bBackEnabled = myGetProfileInt(_T("BackEnabled"), -1);
// by ORTIM: 13-August-2002
bCancelEnabled = myGetProfileInt(_T("CancelEnabled"), -1);
bCancelShow = myGetProfileInt(_T("CancelShow"), -1);
bRTL = myGetProfileInt(_T("RTL"), 0);
if (nNumFields > 0) {
// make this twice as large for the worst case that every control is a browse button.
// the structure is small enough that this won't waste much memory.
// if the structure gets much larger, we should switch to a linked list.
pFields = (FieldType *)MALLOC(sizeof(FieldType)*2*nNumFields);
}
for (nIdx = 0, nCtrlIdx = 0; nCtrlIdx < nNumFields; nCtrlIdx++, nIdx++) {
// Control types
static TableEntry TypeTable[] = {
{ _T("LABEL"), FIELD_LABEL },
{ _T("TEXT"), FIELD_TEXT },
{ _T("PASSWORD"), FIELD_TEXT },
{ _T("LISTBOX"), FIELD_LISTBOX },
{ _T("COMBOBOX"), FIELD_COMBOBOX },
{ _T("DROPLIST"), FIELD_COMBOBOX },
{ _T("FILEREQUEST"), FIELD_FILEREQUEST },
{ _T("DIRREQUEST"), FIELD_DIRREQUEST },
{ _T("CHECKBOX"), FIELD_CHECKBOX },
{ _T("RADIOBUTTON"), FIELD_RADIOBUTTON },
{ _T("ICON"), FIELD_ICON },
{ _T("BITMAP"), FIELD_BITMAP },
{ _T("GROUPBOX"), FIELD_GROUPBOX },
#ifdef IO_ENABLE_LINK
{ _T("LINK"), FIELD_LINK },
#else
{ _T("LINK"), FIELD_LABEL },
#endif
{ _T("BUTTON"), FIELD_BUTTON },
{ _T("HLINE"), FIELD_HLINE },
{ _T("VLINE"), FIELD_VLINE },
{ NULL, 0 }
};
// Control flags
static TableEntry FlagTable[] = {
{ _T("NOTIFY"), LBS_NOTIFY },
{ _T("WARN_IF_EXIST"), OFN_OVERWRITEPROMPT },
{ _T("FILE_HIDEREADONLY"), OFN_HIDEREADONLY },
{ _T("MULTISELECT"), LBS_MULTIPLESEL },
{ _T("READONLY"), FLAG_READONLY },
{ _T("RIGHT"), BS_LEFTTEXT },
{ _T("PASSWORD"), FLAG_PASSWORD },
{ _T("ONLY_NUMBERS"), FLAG_ONLYNUMBERS },
{ _T("MULTILINE"), FLAG_MULTILINE },
{ _T("NOWORDWRAP"), FLAG_NOWORDWRAP },
{ _T("WANTRETURN"), FLAG_WANTRETURN },
{ _T("EXTENDEDSELCT"), LBS_EXTENDEDSEL },
{ _T("PATH_MUST_EXIST"), OFN_PATHMUSTEXIST },
{ _T("FILE_MUST_EXIST"), OFN_FILEMUSTEXIST },
{ _T("PROMPT_CREATE"), OFN_CREATEPROMPT },
{ _T("DROPLIST"), FLAG_DROPLIST },
{ _T("RESIZETOFIT"), FLAG_RESIZETOFIT },
{ _T("NOTABSTOP"), WS_TABSTOP },
{ _T("GROUP"), WS_GROUP },
{ _T("REQ_SAVE"), FLAG_SAVEAS },
{ _T("FILE_EXPLORER"), OFN_EXPLORER },
{ _T("HSCROLL"), WS_HSCROLL },
{ _T("VSCROLL"), WS_VSCROLL },
{ _T("DISABLED"), WS_DISABLED },
{ _T("TRANSPARENT"), TRANSPARENT_BMP },
{ _T("FOCUS"), FLAG_FOCUS },
{ NULL, 0 }
};
FieldType *pField = pFields + nIdx;
pField->nField = nCtrlIdx + 1;
pField->pszHwndEntry = _T("HWND");
wsprintf(szField, _T("Field %d"), nCtrlIdx + 1);
pszAppName = szField;
// Get the control type
myGetProfileString(_T("TYPE"));
pField->nType = LookupToken(TypeTable, szResult);
if (pField->nType == FIELD_INVALID)
continue;
// Lookup flags associated with the control type
pField->nFlags = LookupToken(FlagTable, szResult);
myGetProfileString(_T("Flags"));
pField->nFlags |= LookupTokens(FlagTable, szResult);
// pszState must not be NULL!
myGetProfileString(_T("State"));
pField->pszState = strdup(szResult); // uses STRDUP
// ListBox items list
{
int nResult = myGetProfileString(_T("ListItems"));
if (nResult) {
// add an extra | character to the end to simplify the loop where we add the items.
pField->pszListItems = (TCHAR*)MALLOC((nResult + 2)*sizeof(TCHAR));
lstrcpy(pField->pszListItems, szResult);
pField->pszListItems[nResult] = _T('|');
pField->pszListItems[nResult + 1] = _T('\0');
}
}
// Label Text - convert newline
pField->pszText = myGetProfileStringDup(_T("TEXT"));
if (pField->nType == FIELD_LABEL || pField->nType == FIELD_LINK)
ConvertNewLines(pField->pszText);
// Dir request - root folder
pField->pszRoot = myGetProfileStringDup(_T("ROOT"));
// ValidateText - convert newline
pField->pszValidateText = myGetProfileStringDup(_T("ValidateText"));
ConvertNewLines(pField->pszValidateText);
{
int nResult = GetPrivateProfileString(szField, _T("Filter"), _T("All Files|*.*"), szResult, COUNTOF(szResult), pszFilename);
if (nResult) {
// Convert the filter to the format required by Windows: NULL after each
// item followed by a terminating NULL
pField->pszFilter = (TCHAR*)MALLOC((nResult + 2)*sizeof(TCHAR));
lstrcpy(pField->pszFilter, szResult);
TCHAR *pszPos = pField->pszFilter;
while (*pszPos)
{
if (*pszPos == _T('|'))
*pszPos++ = 0;
else
pszPos = CharNext(pszPos);
}
}
}
pField->rect.left = myGetProfileInt(_T("LEFT"), 0);
pField->rect.top = myGetProfileInt(_T("TOP"), 0);
pField->rect.right = myGetProfileInt(_T("RIGHT"), 0);
pField->rect.bottom = myGetProfileInt(_T("BOTTOM"), 0);
pField->nMinLength = myGetProfileInt(_T("MinLen"), 0);
pField->nMaxLength = myGetProfileInt(_T("MaxLen"), 0);
// Text color for LINK control, default is pure blue
pField->hImage = (HANDLE)myGetProfileInt(_T("TxtColor"), RGB(0,0,255));
pField->nControlID = 1200 + nIdx;
if (pField->nType == FIELD_FILEREQUEST || pField->nType == FIELD_DIRREQUEST)
{
FieldType *pNewField = &pFields[nIdx+1];
pNewField->nControlID = 1200 + nIdx + 1;
pNewField->nType = FIELD_BROWSEBUTTON;
pNewField->nFlags = pField->nFlags & (WS_DISABLED | WS_TABSTOP);
pNewField->pszText = STRDUP(szBrowseButtonCaption); // needed for generic FREE
pNewField->rect.right = pField->rect.right;
pNewField->rect.left = pNewField->rect.right - BROWSE_WIDTH;
pNewField->rect.bottom = pField->rect.bottom;
pNewField->rect.top = pField->rect.top;
pField->rect.right = pNewField->rect.left - 3;
pNewField->nField = nCtrlIdx + 1;
pNewField->pszHwndEntry = _T("HWND2");
nNumFields++;
nIdx++;
}
}
return nNumFields;
}
LRESULT WINAPI WMCommandProc(HWND hWnd, UINT id, HWND hwndCtl, UINT codeNotify) {
int nIdx = FindControlIdx(id);
// Ignore if the dialog is in the process of being created
if (g_done || nIdx < 0)
return 0;
switch (pFields[nIdx].nType)
{
case FIELD_BROWSEBUTTON:
--nIdx;
case FIELD_LINK:
case FIELD_BUTTON:
case FIELD_CHECKBOX:
case FIELD_RADIOBUTTON:
if (codeNotify != BN_CLICKED)
return 0;
break;
case FIELD_COMBOBOX:
case FIELD_LISTBOX:
if (codeNotify != LBN_SELCHANGE) // LBN_SELCHANGE == CBN_SELCHANGE
return 0;
break;
default:
return 0;
}
FieldType *pField = pFields + nIdx;
TCHAR szBrowsePath[MAX_PATH];
switch (pField->nType) {
case FIELD_FILEREQUEST: {
OPENFILENAME ofn={0,};
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hConfigWindow;
ofn.lpstrFilter = pField->pszFilter;
ofn.lpstrFile = szBrowsePath;
ofn.nMaxFile = COUNTOF(szBrowsePath);
ofn.Flags = pField->nFlags & (OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_CREATEPROMPT | OFN_EXPLORER);
GetWindowText(pField->hwnd, szBrowsePath, COUNTOF(szBrowsePath));
tryagain:
GetCurrentDirectory(BUFFER_SIZE, szResult); // save working dir
if ((pField->nFlags & FLAG_SAVEAS) ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn)) {
mySetWindowText(pField->hwnd, szBrowsePath);
SetCurrentDirectory(szResult); // restore working dir
// OFN_NOCHANGEDIR doesn't always work (see MSDN)
break;
}
else if (szBrowsePath[0] && CommDlgExtendedError() == FNERR_INVALIDFILENAME) {
szBrowsePath[0] = _T('\0');
goto tryagain;
}
break;
}
case FIELD_DIRREQUEST: {
BROWSEINFO bi;
bi.hwndOwner = hConfigWindow;
bi.pidlRoot = NULL;
bi.pszDisplayName = szBrowsePath;
bi.lpszTitle = pField->pszText;
#ifndef BIF_NEWDIALOGSTYLE
#define BIF_NEWDIALOGSTYLE 0x0040
#endif
bi.ulFlags = BIF_STATUSTEXT | BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
bi.lpfn = BrowseCallbackProc;
bi.lParam = nIdx;
bi.iImage = 0;
if (pField->pszRoot) {
LPSHELLFOLDER sf;
ULONG eaten;
LPITEMIDLIST root;
int ccRoot = (lstrlen(pField->pszRoot) * 2) + 2;
SHGetDesktopFolder(&sf);
#ifdef _UNICODE
sf->ParseDisplayName(hConfigWindow, NULL, pField->pszRoot, &eaten, &root, NULL);
#else
LPWSTR pwszRoot = (LPWSTR) MALLOC(ccRoot);
MultiByteToWideChar(CP_ACP, 0, pField->pszRoot, -1, pwszRoot, ccRoot);
sf->ParseDisplayName(hConfigWindow, NULL, pwszRoot, &eaten, &root, NULL);
FREE(pwszRoot);
#endif
bi.pidlRoot = root;
sf->Release();
}
//CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
LPITEMIDLIST pResult = SHBrowseForFolder(&bi);
if (!pResult)
break;
if (SHGetPathFromIDList(pResult, szBrowsePath)) {
mySetWindowText(pField->hwnd, szBrowsePath);
}
CoTaskMemFree(pResult);
break;
}
case FIELD_LINK:
case FIELD_BUTTON:
// Allow the state to be empty - this might be useful in conjunction
// with the NOTIFY flag
if (*pField->pszState)
ShellExecute(hMainWindow, NULL, pField->pszState, NULL, NULL, SW_SHOWDEFAULT);
break;
}
if (pField->nFlags & LBS_NOTIFY) {
// Remember which control was activated then pretend the user clicked Next
g_NotifyField = nIdx + 1;
mySendMessage(hMainWindow, WM_NOTIFY_OUTER_NEXT, 1, 0);
}
return 0;
}
static void *lpWndProcOld;
int g_is_cancel,g_is_back;
BOOL CALLBACK ParentWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
BOOL bRes;
if (message == WM_NOTIFY_OUTER_NEXT && wParam == 1)
{
// Don't call leave function if fields aren't valid
if (!g_NotifyField && !ValidateFields())
return 0;
// Get the settings ready for the leave function verification
SaveSettings();
// Reset the record of activated control
g_NotifyField = 0;
}
bRes = CallWindowProc((long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long))lpWndProcOld,hwnd,message,wParam,lParam);
if (message == WM_NOTIFY_OUTER_NEXT && !bRes)
{
// if leave function didn't abort (bRes != 0 in that case)
if (wParam == (WPARAM)-1)
g_is_back++;
if (wParam == NOTIFY_BYE_BYE)
g_is_cancel++;
g_done++;
PostMessage(hConfigWindow,WM_CLOSE,0,0);
}
return bRes;
}
BOOL CALLBACK cfgDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
HANDLE_MSG(hwndDlg, WM_COMMAND, WMCommandProc);
case WM_DRAWITEM:
{
DRAWITEMSTRUCT* lpdis = (DRAWITEMSTRUCT*)lParam;
int nIdx = FindControlIdx(lpdis->CtlID);
#ifdef IO_LINK_UNDERLINED
HFONT OldFont;
LOGFONT lf;
#endif
if (nIdx < 0)
break;
FieldType *pField = pFields + nIdx;
#ifdef IO_LINK_UNDERLINED
GetObject(GetCurrentObject(lpdis->hDC, OBJ_FONT), sizeof(lf), &lf);
lf.lfUnderline = TRUE;
OldFont = (HFONT)SelectObject(lpdis->hDC, CreateFontIndirect(&lf));
#endif
// We need lpdis->rcItem later
RECT rc = lpdis->rcItem;
// Calculate needed size of the control
DrawText(lpdis->hDC, pField->pszText, -1, &rc, DT_VCENTER | DT_WORDBREAK | DT_CALCRECT);
// Make some more room so the focus rect won't cut letters off
rc.right = min(rc.right + 2, lpdis->rcItem.right);
// Move rect to right if in RTL mode
if (bRTL)
{
rc.left += lpdis->rcItem.right - rc.right;
rc.right += lpdis->rcItem.right - rc.right;
}
if (lpdis->itemAction & ODA_DRAWENTIRE)
{
// Get TxtColor unless the user has set another using SetCtlColors
if (!GetWindowLong(lpdis->hwndItem, GWL_USERDATA))
SetTextColor(lpdis->hDC, (COLORREF) pField->hImage);
// Draw the text
DrawText(lpdis->hDC, pField->pszText, -1, &rc, DT_CENTER | DT_VCENTER | DT_WORDBREAK | (bRTL ? DT_RTLREADING : 0));
}
// Draw the focus rect if needed
if (((lpdis->itemState & ODS_FOCUS) && (lpdis->itemAction & ODA_DRAWENTIRE)) || (lpdis->itemAction & ODA_FOCUS))
{
// NB: when not in DRAWENTIRE mode, this will actually toggle the focus
// rectangle since it's drawn in a XOR way
DrawFocusRect(lpdis->hDC, &rc);
}
pField->rect = rc;
#ifdef IO_LINK_UNDERLINED
DeleteObject(SelectObject(lpdis->hDC, OldFont));
#endif
break;
}
case WM_CTLCOLORSTATIC:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORDLG:
case WM_CTLCOLORBTN:
case WM_CTLCOLORLISTBOX:
// let the NSIS window handle colors, it knows best
return mySendMessage(hMainWindow, uMsg, wParam, lParam);
}
return 0;
}
#ifdef IO_ENABLE_LINK
#ifndef IDC_HAND
#define IDC_HAND MAKEINTRESOURCE(32649)
#endif
#ifndef BS_TYPEMASK
#define BS_TYPEMASK 0x0000000FL
#endif
// pFields[nIdx].nParentIdx is used to store original windowproc
int WINAPI StaticLINKWindowProc(HWND hWin, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int StaticField = FindControlIdx(GetDlgCtrlID(hWin));
if (StaticField < 0)
return 0;
FieldType *pField = pFields + StaticField;
switch(uMsg)
{
case WM_GETDLGCODE:
// Pretend we are a normal button/default button as appropriate
return DLGC_BUTTON | ((pField->nFlags & FLAG_WANTRETURN) ? DLGC_DEFPUSHBUTTON : DLGC_UNDEFPUSHBUTTON);
case BM_SETSTYLE:
// Detect when we are becoming the default button but don't lose the owner-draw style
if ((wParam & BS_TYPEMASK) == BS_DEFPUSHBUTTON)
pField->nFlags |= FLAG_WANTRETURN; // Hijack this flag to indicate default button status
else
pField->nFlags &= ~FLAG_WANTRETURN;
wParam = (wParam & ~BS_TYPEMASK) | BS_OWNERDRAW;
break;
case WM_NCHITTEST:
{
POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
MapWindowPoints(0, hWin, &pt, 1);
if (PtInRect(&pField->rect, pt))
return HTCLIENT;
else
return HTNOWHERE;
}
case WM_SETCURSOR:
{
if ((HWND)wParam == hWin && LOWORD(lParam) == HTCLIENT)
{
HCURSOR hCur = LoadCursor(NULL, IDC_HAND);
if (hCur)
{
SetCursor(hCur);
return 1; // halt further processing
}
}
}
}
return CallWindowProc((WNDPROC)pField->nParentIdx, hWin, uMsg, wParam, lParam);
}
#endif
int WINAPI NumbersOnlyPasteWndProc(HWND hWin, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int nIdx = FindControlIdx(GetDlgCtrlID(hWin));
if (nIdx < 0)
return 0;
FieldType *pField = pFields + nIdx;
if (uMsg == WM_PASTE)
{
if (OpenClipboard(hWin))
{
#ifdef _UNICODE
HGLOBAL hData = GetClipboardData(CF_UNICODETEXT);
#else
HGLOBAL hData = GetClipboardData(CF_TEXT);
#endif
if (hData)
{
TCHAR *lpData = (TCHAR *) GlobalLock(hData);
if (lpData)
{
int iLen = lstrlen(lpData);
TCHAR *lpFilteredData = (TCHAR *) MALLOC((iLen + 1)*sizeof(TCHAR));
if (lpFilteredData)
{
for (int i = 0, j = 0; i < iLen; i++)
{
if (lpData[i] >= _T('0') && lpData[i] <= _T('9'))
{
lpFilteredData[j] = lpData[i];
j++;
}
lpFilteredData[j] = 0;
}
SendMessage(hWin, EM_REPLACESEL, TRUE, (LPARAM) lpFilteredData);
FREE(lpFilteredData);
}
GlobalUnlock(hData);
}
}
CloseClipboard();
return 0;
}
}
return CallWindowProc((WNDPROC) pField->wndProc, hWin, uMsg, wParam, lParam);
}
int old_cancel_visible;
int WINAPI createCfgDlg()
{
g_is_back=0;
g_is_cancel=0;
HWND mainwnd = hMainWindow;
if (!mainwnd)
{
popstring(NULL);
pushstring(_T("error finding mainwnd"));
return 1; // cannot be used in silent mode unfortunately.
}
if (!g_stacktop || !*g_stacktop || !(pszFilename = (*g_stacktop)->text) || !pszFilename[0] || !ReadSettings())
{
popstring(NULL);
pushstring(_T("error finding config"));
return 1;
}
HWND childwnd=GetDlgItem(mainwnd,nRectId);
if (!childwnd)
{
popstring(NULL);
pushstring(_T("error finding childwnd"));
return 1;
}
hCancelButton = GetDlgItem(mainwnd,IDCANCEL);
hNextButton = GetDlgItem(mainwnd,IDOK);
hBackButton = GetDlgItem(mainwnd,3);
mySetWindowText(hCancelButton,pszCancelButtonText);
mySetWindowText(hNextButton,pszNextButtonText);
mySetWindowText(hBackButton,pszBackButtonText);
if (bBackEnabled!=-1) EnableWindow(hBackButton,bBackEnabled);
if (bCancelEnabled!=-1)
{
EnableWindow(hCancelButton,bCancelEnabled);
if (bCancelEnabled)
EnableMenuItem(GetSystemMenu(mainwnd, FALSE), SC_CLOSE, MF_BYCOMMAND | MF_ENABLED);
else
EnableMenuItem(GetSystemMenu(mainwnd, FALSE), SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
}
if (bCancelShow!=-1) old_cancel_visible=ShowWindow(hCancelButton,bCancelShow?SW_SHOWNA:SW_HIDE);
HFONT hFont = (HFONT)mySendMessage(mainwnd, WM_GETFONT, 0, 0);
// Prevent WM_COMMANDs from being processed while we are building
g_done = 1;
int mainWndWidth, mainWndHeight;
hConfigWindow=CreateDialog(m_hInstance,MAKEINTRESOURCE(IDD_DIALOG1),mainwnd,cfgDlgProc);
if (hConfigWindow)
{
RECT dialog_r;
GetWindowRect(childwnd,&dialog_r);
MapWindowPoints(0, mainwnd, (LPPOINT) &dialog_r, 2);
mainWndWidth = dialog_r.right - dialog_r.left;
mainWndHeight = dialog_r.bottom - dialog_r.top;
SetWindowPos(
hConfigWindow,
0,
dialog_r.left,
dialog_r.top,
mainWndWidth,
mainWndHeight,
SWP_NOZORDER|SWP_NOACTIVATE
);
// Sets the font of IO window to be the same as the main window
mySendMessage(hConfigWindow, WM_SETFONT, (WPARAM)hFont, TRUE);
}
else
{
popstring(NULL);
pushstring(_T("error creating dialog"));
return 1;
}
BOOL fFocused = FALSE;
BOOL fFocusedByFlag = FALSE;
#define DEFAULT_STYLES (WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
#define RTL_EX_STYLES (WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR)
for (int nIdx = 0; nIdx < nNumFields; nIdx++) {
static struct {
TCHAR* pszClass;
DWORD dwStyle;
DWORD dwRTLStyle;
DWORD dwExStyle;
DWORD dwRTLExStyle;
} ClassTable[] = {
{ _T("STATIC"), // FIELD_HLINE
DEFAULT_STYLES | SS_ETCHEDHORZ | SS_SUNKEN,
DEFAULT_STYLES | SS_ETCHEDHORZ | SS_SUNKEN,
WS_EX_TRANSPARENT,
WS_EX_TRANSPARENT | RTL_EX_STYLES },
{ _T("STATIC"), // FIELD_VLINE
DEFAULT_STYLES | SS_ETCHEDVERT | SS_SUNKEN,
DEFAULT_STYLES | SS_ETCHEDVERT | SS_SUNKEN,
WS_EX_TRANSPARENT,
WS_EX_TRANSPARENT | RTL_EX_STYLES },
{ _T("STATIC"), // FIELD_LABEL
DEFAULT_STYLES,
DEFAULT_STYLES | SS_RIGHT,
WS_EX_TRANSPARENT,
WS_EX_TRANSPARENT | RTL_EX_STYLES },
{ _T("STATIC"), // FIELD_ICON
DEFAULT_STYLES | SS_ICON,
DEFAULT_STYLES | SS_ICON,
0,
RTL_EX_STYLES },
{ _T("STATIC"), // FIELD_BITMAP
DEFAULT_STYLES | SS_BITMAP,
DEFAULT_STYLES | SS_BITMAP,
0,
RTL_EX_STYLES },
{ _T("BUTTON"), // FIELD_BROWSEBUTTON
DEFAULT_STYLES | WS_TABSTOP,
DEFAULT_STYLES | WS_TABSTOP,
0,
RTL_EX_STYLES },
{ _T("BUTTON"), // FIELD_LINK
DEFAULT_STYLES | WS_TABSTOP | BS_OWNERDRAW,
DEFAULT_STYLES | WS_TABSTOP | BS_OWNERDRAW | BS_RIGHT,
0,
RTL_EX_STYLES },
{ _T("BUTTON"), // FIELD_BUTTON
DEFAULT_STYLES | WS_TABSTOP,
DEFAULT_STYLES | WS_TABSTOP,
0,
RTL_EX_STYLES },
{ _T("BUTTON"), // FIELD_GROUPBOX
DEFAULT_STYLES | BS_GROUPBOX,
DEFAULT_STYLES | BS_GROUPBOX | BS_RIGHT,
WS_EX_TRANSPARENT,
WS_EX_TRANSPARENT | RTL_EX_STYLES },
{ _T("BUTTON"), // FIELD_CHECKBOX
DEFAULT_STYLES | WS_TABSTOP | BS_TEXT | BS_VCENTER | BS_AUTOCHECKBOX | BS_MULTILINE,
DEFAULT_STYLES | WS_TABSTOP | BS_TEXT | BS_VCENTER | BS_AUTOCHECKBOX | BS_MULTILINE | BS_RIGHT | BS_LEFTTEXT,
0,
RTL_EX_STYLES },
{ _T("BUTTON"), // FIELD_RADIOBUTTON
DEFAULT_STYLES | WS_TABSTOP | BS_TEXT | BS_VCENTER | BS_AUTORADIOBUTTON | BS_MULTILINE,
DEFAULT_STYLES | WS_TABSTOP | BS_TEXT | BS_VCENTER | BS_AUTORADIOBUTTON | BS_MULTILINE | BS_RIGHT | BS_LEFTTEXT,
0,
RTL_EX_STYLES },
{ _T("EDIT"), // FIELD_TEXT
DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL,
DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL | ES_RIGHT,
WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | RTL_EX_STYLES },
{ _T("EDIT"), // FIELD_FILEREQUEST
DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL,
DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL | ES_RIGHT,
WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | RTL_EX_STYLES },
{ _T("EDIT"), // FIELD_DIRREQUEST
DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL,
DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL | ES_RIGHT,
WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | RTL_EX_STYLES },
{ _T("COMBOBOX"), // FIELD_COMBOBOX
DEFAULT_STYLES | WS_TABSTOP | WS_VSCROLL | WS_CLIPCHILDREN | CBS_AUTOHSCROLL | CBS_HASSTRINGS,
DEFAULT_STYLES | WS_TABSTOP | WS_VSCROLL | WS_CLIPCHILDREN | CBS_AUTOHSCROLL | CBS_HASSTRINGS,
WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_RIGHT | RTL_EX_STYLES },
{ _T("LISTBOX"), // FIELD_LISTBOX
DEFAULT_STYLES | WS_TABSTOP | WS_VSCROLL | LBS_DISABLENOSCROLL | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT,
DEFAULT_STYLES | WS_TABSTOP | WS_VSCROLL | LBS_DISABLENOSCROLL | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT,
WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_RIGHT | RTL_EX_STYLES }
};
FieldType *pField = pFields + nIdx;
#undef DEFAULT_STYLES
if (pField->nType < 1 || pField->nType > (int)(COUNTOF(ClassTable)))
continue;
DWORD dwStyle, dwExStyle;
if (bRTL) {
dwStyle = ClassTable[pField->nType - 1].dwRTLStyle;
dwExStyle = ClassTable[pField->nType - 1].dwRTLExStyle;
}
else {
dwStyle = ClassTable[pField->nType - 1].dwStyle;
dwExStyle = ClassTable[pField->nType - 1].dwExStyle;
}
// Convert from dialog units
RECT rect = pField->rect;
// MapDialogRect uses the font used when a dialog is created, and ignores
// any subsequent WM_SETFONT messages (like we used above); so use the main
// NSIS window for the conversion, instead of this one.
MapDialogRect(mainwnd, &rect);
if (pField->rect.left < 0)
rect.left += mainWndWidth;
if (pField->rect.right < 0)
rect.right += mainWndWidth;
if (pField->rect.top < 0)
rect.top += mainWndHeight;
if (pField->rect.bottom < 0)
rect.bottom += mainWndHeight;
if (bRTL) {
int right = rect.right;
rect.right = mainWndWidth - rect.left;
rect.left = mainWndWidth - right;
}
TCHAR *title = pField->pszText;
switch (pField->nType) {
case FIELD_ICON:
case FIELD_BITMAP:
title = NULL; // otherwise it is treated as the name of a resource
break;
case FIELD_CHECKBOX:
case FIELD_RADIOBUTTON:
dwStyle ^= pField->nFlags & BS_LEFTTEXT;
break;
case FIELD_TEXT:
case FIELD_FILEREQUEST:
case FIELD_DIRREQUEST:
if (pField->nFlags & FLAG_PASSWORD)
dwStyle |= ES_PASSWORD;
if (pField->nFlags & FLAG_ONLYNUMBERS)
dwStyle |= ES_NUMBER;
if (pField->nFlags & FLAG_WANTRETURN)
dwStyle |= ES_WANTRETURN;
if (pField->nFlags & FLAG_READONLY)
dwStyle |= ES_READONLY;
title = pField->pszState;
if (pField->nFlags & FLAG_MULTILINE)
{
dwStyle |= ES_MULTILINE | ES_AUTOVSCROLL;
// Enable word-wrap unless we have a horizontal scroll bar
// or it has been explicitly disallowed
if (!(pField->nFlags & (WS_HSCROLL | FLAG_NOWORDWRAP)))
dwStyle &= ~ES_AUTOHSCROLL;
ConvertNewLines(pField->pszState);
// If multiline-readonly then hold the text back until after the
// initial focus has been set. This is so the text is not initially
// selected - useful for License Page look-a-likes.
if (pField->nFlags & FLAG_READONLY)
title = NULL;
}
break;
case FIELD_COMBOBOX:
dwStyle |= (pField->nFlags & FLAG_DROPLIST) ? CBS_DROPDOWNLIST : CBS_DROPDOWN;
title = pField->pszState;
break;
case FIELD_LISTBOX:
dwStyle |= pField->nFlags & (LBS_NOTIFY | LBS_MULTIPLESEL | LBS_EXTENDEDSEL);
break;
}
dwStyle |= pField->nFlags & (WS_GROUP | WS_HSCROLL | WS_VSCROLL | WS_DISABLED);
if (pField->nFlags & WS_TABSTOP) dwStyle &= ~WS_TABSTOP;
HWND hwCtrl = pField->hwnd = CreateWindowEx(
dwExStyle,
ClassTable[pField->nType - 1].pszClass,
title,
dwStyle,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
hConfigWindow,
(HMENU)pField->nControlID,
m_hInstance,
NULL
);
{
TCHAR szField[64];
TCHAR szHwnd[64];
wsprintf(szField, _T("Field %d"), pField->nField);
wsprintf(szHwnd, _T("%d"), hwCtrl);
WritePrivateProfileString(szField, pField->pszHwndEntry, szHwnd, pszFilename);
}
if (hwCtrl) {
// Sets the font of IO window to be the same as the main window
mySendMessage(hwCtrl, WM_SETFONT, (WPARAM)hFont, TRUE);
// make sure we created the window, then set additional attributes
switch (pField->nType) {
case FIELD_TEXT:
case FIELD_FILEREQUEST:
case FIELD_DIRREQUEST:
mySendMessage(hwCtrl, EM_LIMITTEXT, (WPARAM)pField->nMaxLength, (LPARAM)0);
if (dwStyle & ES_NUMBER)
{
pField->wndProc = GetWindowLong(hwCtrl, GWL_WNDPROC);
SetWindowLong(hwCtrl, GWL_WNDPROC, (long) NumbersOnlyPasteWndProc);
}
break;
case FIELD_CHECKBOX:
case FIELD_RADIOBUTTON:
if (pField->pszState[0] == _T('1'))
mySendMessage(hwCtrl, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
break;
case FIELD_COMBOBOX:
case FIELD_LISTBOX:
// if this is a listbox or combobox, we need to add the list items.
if (pField->pszListItems) {
UINT nAddMsg, nFindMsg, nSetSelMsg;
if (pField->nType == FIELD_COMBOBOX) {
nAddMsg = CB_ADDSTRING;
nFindMsg = CB_FINDSTRINGEXACT;
nSetSelMsg = CB_SETCURSEL;
}
else {
nAddMsg = LB_ADDSTRING;
nFindMsg = LB_FINDSTRINGEXACT;
nSetSelMsg = LB_SETCURSEL;
}
TCHAR *pszStart, *pszEnd, *pszList;
pszStart = pszEnd = pszList = STRDUP(pField->pszListItems);
// pszListItems has a trailing pipe
while (*pszEnd) {
if (*pszEnd == _T('|')) {
*pszEnd = _T('\0');
if (*pszStart)
mySendMessage(hwCtrl, nAddMsg, 0, (LPARAM) pszStart);
pszStart = ++pszEnd;
}
else
pszEnd = CharNext(pszEnd);
}
FREE(pszList);
if (pField->pszState) {
if (pField->nFlags & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && nFindMsg == LB_FINDSTRINGEXACT) {
mySendMessage(hwCtrl, LB_SETSEL, FALSE, (LPARAM)-1);
pszStart = pszEnd = pField->pszState;
for (;;) {
TCHAR c = *pszEnd;
if (c == _T('|') || c == _T('\0')) {
*pszEnd = _T('\0');
if (*pszStart)
{
int nItem = mySendMessage(hwCtrl, LB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)pszStart);
if (nItem != LB_ERR)
mySendMessage(hwCtrl, LB_SETSEL, TRUE, nItem);
}
if (!c)
break;
pszStart = ++pszEnd;
}
else
pszEnd = CharNext(pszEnd);
}
}
else {
int nItem = mySendMessage(hwCtrl, nFindMsg, (WPARAM)-1, (LPARAM)pField->pszState);
if (nItem != CB_ERR) { // CB_ERR == LB_ERR == -1
mySendMessage(hwCtrl, nSetSelMsg, nItem, 0);
}
}
}
}
break;
case FIELD_ICON:
case FIELD_BITMAP:
{
WPARAM nImageType = pField->nType == FIELD_BITMAP ? IMAGE_BITMAP : IMAGE_ICON;
LPARAM nImage = 0;
if (pField->pszText) {
pField->hImage = LoadImage(
m_hInstance,
pField->pszText,
nImageType,
(pField->nFlags & FLAG_RESIZETOFIT)
? (rect.right - rect.left)
: 0,
(pField->nFlags & FLAG_RESIZETOFIT)
? (rect.bottom - rect.top)
: 0,
LR_LOADFROMFILE
);
nImage = (LPARAM)pField->hImage;
}
else
nImage = (LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(103));
if ((pField->nFlags & TRANSPARENT_BMP) && nImageType == IMAGE_BITMAP)
{
// based on AdvSplash's SetTransparentRegion
BITMAP bm;
HBITMAP hBitmap = (HBITMAP) nImage;
if (GetObject(hBitmap, sizeof(bm), &bm))
{
HDC dc;
int x, y;
HRGN region, cutrgn;
BITMAPINFO bmi;
int size = bm.bmWidth * bm.bmHeight * sizeof(int);
int *bmp = (int *) MALLOC(size);
if (bmp)
{
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biHeight = bm.bmHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = bm.bmWidth;
bmi.bmiHeader.biClrUsed = 0;
bmi.bmiHeader.biClrImportant = 0;
dc = CreateCompatibleDC(NULL);
SelectObject(dc, hBitmap);
x = GetDIBits(dc, hBitmap, 0, bm.bmHeight, bmp, &bmi, DIB_RGB_COLORS);
region = CreateRectRgn(0, 0, bm.bmWidth, bm.bmHeight);
int keycolor = *bmp & 0xFFFFFF;
// Search for transparent pixels
for (y = bm.bmHeight - 1; y >= 0; y--) {
for (x = 0; x < bm.bmWidth;) {
if ((*bmp & 0xFFFFFF) == keycolor) {
int j = x;
while ((x < bm.bmWidth) && ((*bmp & 0xFFFFFF) == keycolor)) {
bmp++, x++;
}
// Cut transparent pixels from the original region
cutrgn = CreateRectRgn(j, y, x, y + 1);
CombineRgn(region, region, cutrgn, RGN_XOR);
DeleteObject(cutrgn);
} else {
bmp++, x++;
}
}
}
// Set resulting region.
SetWindowRgn(hwCtrl, region, TRUE);
DeleteObject(region);
DeleteObject(dc);
FREE(bmp);
}
}
}
mySendMessage(
hwCtrl,
STM_SETIMAGE,
nImageType,
nImage
);
if (pField->nType == FIELD_BITMAP)
{
// Centre the image in the requested space.
// Cannot use SS_CENTERIMAGE because it behaves differently on XP to
// everything else. (Thank you Microsoft.)
RECT bmp_rect;
GetClientRect(hwCtrl, &bmp_rect);
bmp_rect.left = (rect.left + rect.right - bmp_rect.right) / 2;
bmp_rect.top = (rect.top + rect.bottom - bmp_rect.bottom) / 2;
SetWindowPos(hwCtrl, NULL, bmp_rect.left, bmp_rect.top, 0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
break;
}
#ifdef IO_ENABLE_LINK
case FIELD_LINK:
pField->nParentIdx = SetWindowLong(hwCtrl, GWL_WNDPROC, (long)StaticLINKWindowProc);
break;
#endif
}
// Set initial focus to the first appropriate field ( with FOCUS flag)
if (!fFocusedByFlag && (dwStyle & (WS_TABSTOP | WS_DISABLED)) == WS_TABSTOP && pField->nType >= FIELD_SETFOCUS) {
if (pField->nFlags & FLAG_FOCUS) {
fFocusedByFlag = TRUE;
}
if (!fFocused || fFocusedByFlag) {
fFocused = TRUE;
mySetFocus(hwCtrl);
}
}
// If multiline-readonly then hold the text back until after the
// initial focus has been set. This is so the text is not initially
// selected - useful for License Page look-a-likes.
if ((pField->nFlags & (FLAG_MULTILINE | FLAG_READONLY)) == (FLAG_MULTILINE | FLAG_READONLY))
mySetWindowText(hwCtrl, pField->pszState);
}
}
if (!fFocused)
mySetFocus(hNextButton);
mySetWindowText(mainwnd,pszTitle);
pFilenameStackEntry = *g_stacktop;
*g_stacktop = (*g_stacktop)->next;
static TCHAR tmp[32];
wsprintf(tmp,_T("%d"),hConfigWindow);
pushstring(tmp);
return 0;
}
void WINAPI showCfgDlg()
{
lpWndProcOld = (void *) SetWindowLong(hMainWindow,DWL_DLGPROC,(long)ParentWndProc);
// Tell NSIS to remove old inner dialog and pass handle of the new inner dialog
mySendMessage(hMainWindow, WM_NOTIFY_CUSTOM_READY, (WPARAM)hConfigWindow, 0);
ShowWindow(hConfigWindow, SW_SHOWNA);
g_done = g_NotifyField = 0;
while (!g_done) {
MSG msg;
GetMessage(&msg, NULL, 0, 0);
if (!IsDialogMessage(hConfigWindow,&msg) && !IsDialogMessage(hMainWindow,&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// we don't save settings on cancel since that means your installer will likely
// quit soon, which means the ini might get flushed late and cause crap. :) anwyay.
if (!g_is_cancel) SaveSettings();
SetWindowLong(hMainWindow,DWL_DLGPROC,(long)lpWndProcOld);
DestroyWindow(hConfigWindow);
// by ORTIM: 13-August-2002
if (bCancelShow!=-1) ShowWindow(hCancelButton,old_cancel_visible?SW_SHOWNA:SW_HIDE);
FREE(pFilenameStackEntry);
FREE(pszTitle);
FREE(pszCancelButtonText);
FREE(pszNextButtonText);
FREE(pszBackButtonText);
int i = nNumFields;
while (i--) {
FieldType *pField = pFields + i;
int j = FIELD_BUFFERS;
while (j--)
FREE(((TCHAR **) pField)[j]);
if (pField->nType == FIELD_BITMAP) {
DeleteObject(pField->hImage);
}
if (pField->nType == FIELD_ICON) {
DestroyIcon((HICON)pField->hImage);
}
}
FREE(pFields);
pushstring(g_is_cancel?_T("cancel"):g_is_back?_T("back"):_T("success"));
}
int initCalled;
extern "C" void __declspec(dllexport) dialog(HWND hwndParent, int string_size,
TCHAR *variables, stack_t **stacktop)
{
hMainWindow=hwndParent;
EXDLL_INIT();
if (initCalled) {
pushstring(_T("error"));
return;
}
if (createCfgDlg())
return;
popstring(NULL);
showCfgDlg();
}
static UINT_PTR PluginCallback(enum NSPIM msg)
{
return 0;
}
extern "C" void __declspec(dllexport) initDialog(HWND hwndParent, int string_size,
TCHAR *variables, stack_t **stacktop,
extra_parameters *extra)
{
hMainWindow=hwndParent;
EXDLL_INIT();
extra->RegisterPluginCallback(m_hInstance, PluginCallback);
if (initCalled) {
pushstring(_T("error"));
return;
}
if (createCfgDlg())
return;
initCalled++;
}
extern "C" void __declspec(dllexport) show(HWND hwndParent, int string_size,
TCHAR *variables, stack_t **stacktop)
{
EXDLL_INIT();
if (!initCalled) {
pushstring(_T("error"));
return;
}
initCalled--;
showCfgDlg();
}
extern "C" BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
m_hInstance=(HINSTANCE) hInst;
return TRUE;
}
/**
* Looks up a single token in the psTable_ and returns its mapped numerical value.
*
* @param psTable_ The lookup table.
* @param pszToken_ The token to lookup.
* @return The integer value related to the token, otherwise 0.
*/
int WINAPI LookupToken(TableEntry* psTable_, TCHAR* pszToken_)
{
for (int i = 0; psTable_[i].pszName; i++)
if (!lstrcmpi(pszToken_, psTable_[i].pszName))
return psTable_[i].nValue;
return 0;
}
/**
* In a string of tokens separated by vertical bars '|', look them up in the
* Lookup Table psTable and return their logical OR of their subsequent
* integer values.
*
* @param psTable_ The lookup table to search in.
* @param pszToken String of tokens separated by '|' whose values are to be
* ORed together.
* @return The ORed value of the token values. If no tokens were found, it
* will return 0.
*/
int WINAPI LookupTokens(TableEntry* psTable_, TCHAR* pszTokens_)
{
int n = 0;
TCHAR *pszStart = pszTokens_;
TCHAR *pszEnd = pszTokens_;
for (;;) {
TCHAR c = *pszEnd;
if (c == _T('|') || c == _T('\0')) {
*pszEnd = _T('\0');
n |= LookupToken(psTable_, pszStart);
*pszEnd = c;
if (!c)
break;
pszStart = ++pszEnd;
}
else
pszEnd = CharNext(pszEnd);
}
return n;
}
/**
* ConvertNewLines takes a string and turns escape sequences written
* as separate chars e.g. "\\t" into the special char they represent
* '\t'. The transformation is done in place.
*
* @param str [in/out] The string to convert.
*/
void WINAPI ConvertNewLines(TCHAR *str) {
TCHAR *p1, *p2, *p3;
TCHAR tch0, tch1, nch;
if (!str)
return;
p1 = p2 = str;
while ((tch0 = *p1) != 0)
{
nch = 0; // new translated char
if (tch0 == _T('\\'))
{
tch1 = *(p1+1);
if (tch1 == _T('t')) nch = _T('\t');
else if (tch1 == _T('n')) nch = _T('\n');
else if (tch1 == _T('r')) nch = _T('\r');
else if (tch1 == _T('\\')) nch = _T('\\');
}
// Was it a special char?
if (nch)
{
*p2++ = nch;
p1 += 2;
}
else
{
// For MBCS
p3 = CharNext(p1);
while (p1 < p3)
*p2++ = *p1++;
}
}
*p2 = 0;
}