NSIS/Contrib/InstallOptions/InstallerOptions.cpp
anders_k 7cc150c464 MakeNSIS can now generate Unicode or Ansi installers based on a script attribute. SCons generates both Ansi and Unicode stubs and plugins.
The official plugins are now stored in architecture specific subdirectories under NSIS\Plugins. !AddPluginDir also gained a new (optional) architecture flag because MakeNSIS now stores separate plugin information for each target architecture. Storing plugins in the root of the Plugins directory is no longer supported.

MinGW does not implement the unicode CRT startup functions so the entry point functions and linker parameters had to be changed. The unicode tools use the ansi entry point and a small helper function that calls into the real code: _tmain has full argc+argv emulation while wWinMain does not pass the command line parameters. The stubs do not use any CRT functions and have no CRT or unicode helper code, they call our entry point directly.



git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6269 212acab6-be3b-0410-9dea-997c60f758d6
2012-10-13 01:47:50 +00:00

1694 lines
53 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);
}
// 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 {
const 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_PTR 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
const TCHAR *pszHwndEntry; // "HWND" or "HWND2"
WNDPROC 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];
const 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;
SHGetDesktopFolder(&sf);
#ifdef _UNICODE
sf->ParseDisplayName(hConfigWindow, NULL, pField->pszRoot, &eaten, &root, NULL);
#else
int ccRoot = (lstrlen(pField->pszRoot) * 2) + 2;
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 WNDPROC lpWndProcOld;
int g_is_cancel,g_is_back;
INT_PTR CALLBACK ParentWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
INT_PTR 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(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;
}
INT_PTR 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 (!GetWindowLongPtr(lpdis->hwndItem, GWLP_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(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 {
const 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 = (WNDPROC) GetWindowLongPtr(hwCtrl, GWLP_WNDPROC);
SetWindowLongPtr(hwCtrl, GWLP_WNDPROC, (LONG_PTR) 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 = (INT_PTR) SetWindowLongPtr(hwCtrl, GWLP_WNDPROC, (LONG_PTR)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 = (WNDPROC) SetWindowLongPtr(hMainWindow,DWLP_DLGPROC,(LONG_PTR)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();
SetWindowLongPtr(hMainWindow,DWLP_DLGPROC,(LONG_PTR)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;
#ifdef _UNICODE
// convert ini file to Unicode so that WritePrivateProfileString can write Unicode strings in it
extern "C" void __declspec(dllexport) make_unicode(HWND hwndParent, int string_size,
TCHAR *variables, stack_t **stacktop)
{
EXDLL_INIT();
TCHAR filename[MAX_PATH];
popstring(filename);
HANDLE hFile = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD fSize = GetFileSize(hFile, NULL);
LPSTR lpBuffer = (LPSTR) MALLOC(fSize);
if (lpBuffer)
{
DWORD bytes;
ReadFile(hFile, lpBuffer, fSize, &bytes, NULL);
if ((bytes < 2) || (lpBuffer[0] != '\xFF') || (lpBuffer[1] != '\xFE')) // file is not already Unicode
{
LPWSTR lpWide = (LPWSTR) MALLOC((bytes+1)*2);
if (lpWide)
{
int cch = MultiByteToWideChar(CP_ACP, 0, lpBuffer, bytes, lpWide, bytes+1);
if (cch)
{
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
WriteFile(hFile, "\xFF\xFE", 2, &bytes, NULL); // write Unicode BOM
WriteFile(hFile, lpWide, cch*2, &bytes, NULL);
SetEndOfFile(hFile);
}
FREE(lpWide);
}
}
FREE(lpBuffer);
}
CloseHandle(hFile);
}
}
#endif
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(HINSTANCE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
m_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;
}