
Compiler output is identical before & after this step git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/branches/wizou@6036 212acab6-be3b-0410-9dea-997c60f758d6
600 lines
15 KiB
C
600 lines
15 KiB
C
// Unicode support by Jim Park -- 08/10/2007
|
|
|
|
#include <windows.h>
|
|
|
|
#include <nsis/pluginapi.h> // nsis plugin
|
|
|
|
#include "defs.h"
|
|
#include "input.h"
|
|
#include "rtl.h"
|
|
|
|
#ifndef ODS_NOACCEL
|
|
#define ODS_NOACCEL 0x0100
|
|
#define ODS_NOFOCUSRECT 0x0200
|
|
#endif
|
|
#ifndef DT_HIDEPREFIX
|
|
#define DT_HIDEPREFIX 0x00100000
|
|
#endif
|
|
|
|
HINSTANCE g_hInstance;
|
|
struct nsDialog g_dialog;
|
|
extra_parameters* g_pluginParms;
|
|
|
|
struct nsControl* NSDFUNC GetControl(HWND hwCtl)
|
|
{
|
|
unsigned id = (unsigned) GetProp(hwCtl, NSCONTROL_ID_PROP);
|
|
|
|
if (id == 0 || id > g_dialog.controlCount)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return &g_dialog.controls[id - 1];
|
|
}
|
|
|
|
BOOL CALLBACK ParentProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
BOOL res;
|
|
|
|
if (message == WM_NOTIFY_OUTER_NEXT)
|
|
{
|
|
if (wParam == (WPARAM)-1)
|
|
{
|
|
if (g_pluginParms->ExecuteCodeSegment(g_dialog.callbacks.onBack - 1, 0))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
res = CallWindowProc(g_dialog.parentOriginalWndproc, hwnd, message, wParam, lParam);
|
|
|
|
if (message == WM_NOTIFY_OUTER_NEXT && !res)
|
|
{
|
|
DestroyWindow(g_dialog.hwDialog);
|
|
HeapFree(GetProcessHeap(), 0, g_dialog.controls);
|
|
g_dialog.hwDialog = NULL;
|
|
g_dialog.controls = NULL;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
LRESULT CALLBACK LinkWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
struct nsControl* ctl = GetControl(hwnd);
|
|
|
|
if(ctl == NULL)
|
|
return 0;
|
|
|
|
if(message == WM_SETCURSOR)
|
|
{
|
|
SetCursor(LoadCursor(NULL, IDC_HAND));
|
|
return TRUE;
|
|
}
|
|
|
|
return CallWindowProc(ctl->oldWndProc, hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
// handle notifications
|
|
case WM_COMMAND:
|
|
{
|
|
HWND hwCtl = GetDlgItem(hwndDlg, LOWORD(wParam));
|
|
struct nsControl* ctl = GetControl(hwCtl);
|
|
|
|
if (ctl == NULL)
|
|
break;
|
|
|
|
if (HIWORD(wParam) == BN_CLICKED && (ctl->type == NSCTL_BUTTON || ctl->type == NSCTL_LINK))
|
|
{
|
|
if (ctl->callbacks.onClick)
|
|
{
|
|
pushint((int) hwCtl);
|
|
g_pluginParms->ExecuteCodeSegment(ctl->callbacks.onClick - 1, 0);
|
|
}
|
|
}
|
|
else if (HIWORD(wParam) == EN_CHANGE && ctl->type == NSCTL_EDIT)
|
|
{
|
|
if (ctl->callbacks.onChange)
|
|
{
|
|
pushint((int) hwCtl);
|
|
g_pluginParms->ExecuteCodeSegment(ctl->callbacks.onChange - 1, 0);
|
|
}
|
|
}
|
|
else if (HIWORD(wParam) == LBN_SELCHANGE && ctl->type == NSCTL_LISTBOX)
|
|
{
|
|
if (ctl->callbacks.onChange)
|
|
{
|
|
pushint((int) hwCtl);
|
|
g_pluginParms->ExecuteCodeSegment(ctl->callbacks.onChange - 1, 0);
|
|
}
|
|
}
|
|
else if ((HIWORD(wParam) == CBN_EDITUPDATE || HIWORD(wParam) == CBN_SELCHANGE)
|
|
&& ctl->type == NSCTL_COMBOBOX)
|
|
{
|
|
if (ctl->callbacks.onChange)
|
|
{
|
|
pushint((int) hwCtl);
|
|
g_pluginParms->ExecuteCodeSegment(ctl->callbacks.onChange - 1, 0);
|
|
}
|
|
}
|
|
else if (HIWORD(wParam) == STN_CLICKED && ctl->type == NSCTL_STATIC)
|
|
{
|
|
if (ctl->callbacks.onClick)
|
|
{
|
|
pushint((int) hwCtl);
|
|
g_pluginParms->ExecuteCodeSegment(ctl->callbacks.onClick - 1, 0);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
LPNMHDR nmhdr = (LPNMHDR) lParam;
|
|
struct nsControl* ctl = GetControl(nmhdr->hwndFrom);
|
|
|
|
if (ctl == NULL)
|
|
break;
|
|
|
|
if (!ctl->callbacks.onNotify)
|
|
break;
|
|
|
|
pushint((int) nmhdr);
|
|
pushint(nmhdr->code);
|
|
pushint((int) nmhdr->hwndFrom);
|
|
g_pluginParms->ExecuteCodeSegment(ctl->callbacks.onNotify - 1, 0);
|
|
}
|
|
|
|
// handle links
|
|
case WM_DRAWITEM:
|
|
{
|
|
DRAWITEMSTRUCT* lpdis = (DRAWITEMSTRUCT*)lParam;
|
|
RECT rc;
|
|
TCHAR text[1024];
|
|
|
|
// http://blogs.msdn.com/oldnewthing/archive/2005/05/03/414317.aspx#414357
|
|
// says we should call SystemParametersInfo(SPI_GETKEYBOARDCUES,...) to make
|
|
// sure, does not seem to be required, might be a win2k bug, or it might
|
|
// only apply to menus
|
|
BOOL hideFocus = (lpdis->itemState & ODS_NOFOCUSRECT);
|
|
BOOL hideAccel = (lpdis->itemState & ODS_NOACCEL);
|
|
|
|
struct nsControl* ctl = GetControl(lpdis->hwndItem);
|
|
if (ctl == NULL)
|
|
break;
|
|
|
|
// We need lpdis->rcItem later
|
|
rc = lpdis->rcItem;
|
|
|
|
// Get button's text
|
|
text[0] = _T('\0');
|
|
GetWindowText(lpdis->hwndItem, text, 1024);
|
|
|
|
// Calculate needed size of the control
|
|
DrawText(lpdis->hDC, text, -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 (g_dialog.rtl)
|
|
{
|
|
rc.left += lpdis->rcItem.right - rc.right;
|
|
rc.right += lpdis->rcItem.right - rc.right;
|
|
}
|
|
|
|
if (lpdis->itemAction & ODA_DRAWENTIRE)
|
|
{
|
|
DWORD xtraDrawStyle = (g_dialog.rtl ? DT_RTLREADING : 0);
|
|
if (hideAccel)
|
|
xtraDrawStyle |= DT_HIDEPREFIX;
|
|
|
|
// Use blue unless the user has set another using SetCtlColors
|
|
if (!GetWindowLong(lpdis->hwndItem, GWL_USERDATA))
|
|
SetTextColor(lpdis->hDC, RGB(0,0,255));
|
|
|
|
// Draw the text
|
|
DrawText(lpdis->hDC, text, -1, &rc, xtraDrawStyle | DT_CENTER | DT_VCENTER | DT_WORDBREAK);
|
|
}
|
|
|
|
// 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
|
|
if (!hideFocus)
|
|
DrawFocusRect(lpdis->hDC, &rc);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// handle colors
|
|
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 SendMessage(g_dialog.hwParent, uMsg, wParam, lParam);
|
|
|
|
// bye bye
|
|
case WM_DESTROY:
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < g_dialog.controlCount; i++)
|
|
{
|
|
RemoveProp(g_dialog.controls[i].window, NSCONTROL_ID_PROP);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static UINT_PTR PluginCallback(enum NSPIM msg)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void __declspec(dllexport) Create(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
HWND hwPlacementRect;
|
|
RECT rcPlacement;
|
|
|
|
EXDLL_INIT();
|
|
|
|
extra->RegisterPluginCallback(g_hInstance, PluginCallback);
|
|
|
|
g_dialog.hwParent = hwndParent;
|
|
g_pluginParms = extra;
|
|
|
|
hwPlacementRect = GetDlgItem(hwndParent, popint());
|
|
GetWindowRect(hwPlacementRect, &rcPlacement);
|
|
MapWindowPoints(NULL, hwndParent, (LPPOINT) &rcPlacement, 2);
|
|
|
|
g_dialog.hwDialog = CreateDialog(g_hInstance, MAKEINTRESOURCE(1), hwndParent, DialogProc);
|
|
|
|
if (g_dialog.hwDialog == NULL)
|
|
{
|
|
pushstring(_T("error"));
|
|
return;
|
|
}
|
|
|
|
SetWindowPos(
|
|
g_dialog.hwDialog,
|
|
0,
|
|
rcPlacement.left,
|
|
rcPlacement.top,
|
|
rcPlacement.right - rcPlacement.left,
|
|
rcPlacement.bottom - rcPlacement.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE
|
|
);
|
|
|
|
g_dialog.parentOriginalWndproc = (WNDPROC) SetWindowLong(hwndParent, DWL_DLGPROC, (long) ParentProc);
|
|
|
|
g_dialog.rtl = FALSE;
|
|
|
|
g_dialog.controlCount = 0;
|
|
g_dialog.controls = (struct nsControl*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0);
|
|
|
|
g_dialog.callbacks.onBack = 0;
|
|
|
|
pushint((int) g_dialog.hwDialog);
|
|
}
|
|
|
|
void __declspec(dllexport) CreateControl(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
TCHAR *className;
|
|
TCHAR *text;
|
|
|
|
HWND hwItem;
|
|
int x, y, width, height;
|
|
DWORD style, exStyle;
|
|
size_t id;
|
|
|
|
// get info from stack
|
|
|
|
className = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (g_stringsize * 2)*sizeof(TCHAR));
|
|
text = &className[g_stringsize];
|
|
|
|
if (!className)
|
|
{
|
|
pushstring(_T("error"));
|
|
return;
|
|
}
|
|
|
|
if (popstringn(className, 0))
|
|
{
|
|
pushstring(_T("error"));
|
|
HeapFree(GetProcessHeap(), 0, className);
|
|
return;
|
|
}
|
|
|
|
style = (DWORD) popint_or();
|
|
exStyle = (DWORD) popint_or();
|
|
|
|
PopPlacement(&x, &y, &width, &height);
|
|
|
|
if (popstringn(text, 0))
|
|
{
|
|
pushstring(_T("error"));
|
|
HeapFree(GetProcessHeap(), 0, className);
|
|
return;
|
|
}
|
|
|
|
// create item descriptor
|
|
|
|
id = g_dialog.controlCount;
|
|
g_dialog.controlCount++;
|
|
g_dialog.controls = (struct nsControl*) HeapReAlloc(
|
|
GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
g_dialog.controls,
|
|
g_dialog.controlCount * sizeof(struct nsControl));
|
|
|
|
if (!lstrcmpi(className, _T("BUTTON")))
|
|
g_dialog.controls[id].type = NSCTL_BUTTON;
|
|
else if (!lstrcmpi(className, _T("EDIT")))
|
|
g_dialog.controls[id].type = NSCTL_EDIT;
|
|
else if (!lstrcmpi(className, _T("COMBOBOX")))
|
|
g_dialog.controls[id].type = NSCTL_COMBOBOX;
|
|
else if (!lstrcmpi(className, _T("LISTBOX")))
|
|
g_dialog.controls[id].type = NSCTL_LISTBOX;
|
|
else if (!lstrcmpi(className, _T("RichEdit")))
|
|
g_dialog.controls[id].type = NSCTL_RICHEDIT;
|
|
else if (!lstrcmpi(className, _T("RICHEDIT_CLASS")))
|
|
g_dialog.controls[id].type = NSCTL_RICHEDIT2;
|
|
else if (!lstrcmpi(className, _T("STATIC")))
|
|
g_dialog.controls[id].type = NSCTL_STATIC;
|
|
else if (!lstrcmpi(className, _T("LINK")))
|
|
g_dialog.controls[id].type = NSCTL_LINK;
|
|
else
|
|
g_dialog.controls[id].type = NSCTL_UNKNOWN;
|
|
|
|
// apply rtl to style
|
|
|
|
ConvertStyleToRTL(g_dialog.controls[id].type, &style, &exStyle);
|
|
|
|
// create item's window
|
|
|
|
hwItem = CreateWindowEx(
|
|
exStyle,
|
|
lstrcmpi(className, _T("LINK")) ? className : _T("BUTTON"),
|
|
text,
|
|
style,
|
|
x, y, width, height,
|
|
g_dialog.hwDialog,
|
|
(HMENU) (1200 + id),
|
|
g_hInstance,
|
|
NULL);
|
|
|
|
g_dialog.controls[id].window = hwItem;
|
|
|
|
// remember id
|
|
|
|
SetProp(hwItem, NSCONTROL_ID_PROP, (HANDLE) (id + 1));
|
|
|
|
// set font
|
|
|
|
SendMessage(hwItem, WM_SETFONT, SendMessage(g_dialog.hwParent, WM_GETFONT, 0, 0), TRUE);
|
|
|
|
// set the WndProc for the link control
|
|
|
|
if(g_dialog.controls[id].type == NSCTL_LINK)
|
|
g_dialog.controls[id].oldWndProc = (WNDPROC) SetWindowLong(hwItem, GWL_WNDPROC, (long) LinkWndProc);
|
|
|
|
// push back result
|
|
|
|
pushint((int) hwItem);
|
|
|
|
// done
|
|
|
|
HeapFree(GetProcessHeap(), 0, className);
|
|
}
|
|
|
|
// for backward compatibility (2.29 had CreateItem)
|
|
void __declspec(dllexport) CreateItem(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
CreateControl(hwndParent, string_size, variables, stacktop, extra);
|
|
}
|
|
|
|
void __declspec(dllexport) SetUserData(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
HWND hwCtl;
|
|
struct nsControl* ctl;
|
|
|
|
// get info from stack
|
|
|
|
hwCtl = (HWND) popint();
|
|
|
|
if (!IsWindow(hwCtl))
|
|
{
|
|
popint(); // remove user data from stack
|
|
return;
|
|
}
|
|
|
|
// get descriptor
|
|
|
|
ctl = GetControl(hwCtl);
|
|
|
|
if (ctl == NULL)
|
|
return;
|
|
|
|
// set user data
|
|
|
|
popstringn(ctl->userData, USERDATA_SIZE);
|
|
}
|
|
|
|
void __declspec(dllexport) GetUserData(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
HWND hwCtl;
|
|
struct nsControl* ctl;
|
|
|
|
// get info from stack
|
|
|
|
hwCtl = (HWND) popint();
|
|
|
|
if (!IsWindow(hwCtl))
|
|
{
|
|
pushstring(_T(""));
|
|
return;
|
|
}
|
|
|
|
// get descriptor
|
|
|
|
ctl = GetControl(hwCtl);
|
|
|
|
if (ctl == NULL)
|
|
{
|
|
pushstring(_T(""));
|
|
return;
|
|
}
|
|
|
|
// return user data
|
|
|
|
pushstring(ctl->userData);
|
|
}
|
|
|
|
void CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
|
|
{
|
|
// we use a timer proc instead of WM_TIMER to make sure no one messes with the ids but us
|
|
g_pluginParms->ExecuteCodeSegment(idEvent - 1, 0);
|
|
}
|
|
|
|
void __declspec(dllexport) CreateTimer(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
UINT callback;
|
|
UINT interval;
|
|
|
|
// get info from stack
|
|
|
|
callback = popint();
|
|
interval = popint();
|
|
|
|
if (!callback || !interval)
|
|
return;
|
|
|
|
// create timer
|
|
|
|
SetTimer(
|
|
g_dialog.hwDialog,
|
|
callback,
|
|
interval,
|
|
TimerProc);
|
|
}
|
|
|
|
void nsdKillTimer(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
UINT id;
|
|
|
|
// get timer id from stack
|
|
|
|
id = popint();
|
|
|
|
// kill timer
|
|
|
|
KillTimer(g_dialog.hwDialog, id);
|
|
}
|
|
|
|
void NSDFUNC SetControlCallback(size_t callbackIdx)
|
|
{
|
|
HWND hwCtl;
|
|
nsFunction callback;
|
|
nsFunction* callbacks;
|
|
struct nsControl* ctl;
|
|
|
|
// get info from stack
|
|
|
|
hwCtl = (HWND) popint();
|
|
callback = (nsFunction) popint();
|
|
|
|
if (!IsWindow(hwCtl))
|
|
return;
|
|
|
|
// get descriptor
|
|
|
|
ctl = GetControl(hwCtl);
|
|
|
|
if (ctl == NULL)
|
|
return;
|
|
|
|
// set callback
|
|
|
|
callbacks = (nsFunction*) &ctl->callbacks;
|
|
callbacks[callbackIdx] = callback;
|
|
}
|
|
|
|
void NSDFUNC SetDialogCallback(size_t callbackIdx)
|
|
{
|
|
nsFunction callback;
|
|
nsFunction* callbacks;
|
|
|
|
// get info from stack
|
|
|
|
callback = (nsFunction) popint();
|
|
|
|
// set callback
|
|
|
|
callbacks = (nsFunction*) &g_dialog.callbacks;
|
|
callbacks[callbackIdx] = callback;
|
|
}
|
|
|
|
|
|
void __declspec(dllexport) OnClick(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
SetControlCallback(CTL_CALLBACK_IDX(onClick));
|
|
}
|
|
|
|
void __declspec(dllexport) OnChange(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
SetControlCallback(CTL_CALLBACK_IDX(onChange));
|
|
}
|
|
|
|
void __declspec(dllexport) OnNotify(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
SetControlCallback(CTL_CALLBACK_IDX(onNotify));
|
|
}
|
|
|
|
void __declspec(dllexport) OnBack(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
SetDialogCallback(DLG_CALLBACK_IDX(onBack));
|
|
}
|
|
|
|
void __declspec(dllexport) Show(HWND hwndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra)
|
|
{
|
|
// tell NSIS to remove old inner dialog and pass handle of the new inner dialog
|
|
|
|
SendMessage(hwndParent, WM_NOTIFY_CUSTOM_READY, (WPARAM) g_dialog.hwDialog, 0);
|
|
ShowWindow(g_dialog.hwDialog, SW_SHOWNA);
|
|
|
|
// message loop
|
|
|
|
while (g_dialog.hwDialog)
|
|
{
|
|
MSG msg;
|
|
GetMessage(&msg, NULL, 0, 0);
|
|
if (!IsDialogMessage(g_dialog.hwDialog, &msg) && !IsDialogMessage(g_dialog.hwParent, &msg))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
// reset wndproc
|
|
|
|
SetWindowLong(hwndParent, DWL_DLGPROC, (long) g_dialog.parentOriginalWndproc);
|
|
}
|
|
|
|
BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
|
|
{
|
|
g_hInstance = (HINSTANCE) hInst;
|
|
return TRUE;
|
|
}
|