/********************************************************* * * InstallOptions version 2.0 - Plugin for custom pages * * See Readme.html for documentation and license * *********************************************************/ #include #include #include #include #include #include #include #include "resource.h" #define popstring dontuseme #include "../exdll/exdll.h" #undef popstring static int popstring(char *str) { stack_t *th; if (!g_stacktop || !*g_stacktop) return 1; th=(*g_stacktop); if (str) lstrcpy(str,th->text); *g_stacktop = th->next; GlobalFree((HGLOBAL)th); return 0; } #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)) void *MALLOC(int len) { return (void*)GlobalAlloc(GPTR,len); } void FREE(void *d) { if (d) GlobalFree((HGLOBAL)d); } char *STRDUP(const char *c) { char *t=(char*)MALLOC(lstrlen(c)+1); return lstrcpy(t,c); } #define FIELD_LABEL (1) #define FIELD_ICON (2) #define FIELD_BITMAP (3) #define FIELD_BROWSEBUTTON (4) #define FIELD_CHECKBOX (5) #define FIELD_RADIOBUTTON (6) #define FIELD_TEXT (7) #define FIELD_FILEREQUEST (8) #define FIELD_DIRREQUEST (9) #define FIELD_COMBOBOX (10) #define FIELD_LISTBOX (11) #define FIELD_GROUPBOX (12) // general flags #define FLAG_RIGHT 0x00000001 // OFN_OVERWRITEPROMPT 0x00000002 // OFN_HIDEREADONLY 0x00000004 #define FLAG_DISABLED 0x00000008 #define FLAG_GROUP 0x00000010 #define FLAG_NOTABSTOP 0x00000020 // text box flags #define FLAG_PASSWORD 0x00000040 #define FLAG_ONLYNUMBERS 0x00000080 #define FLAG_MULTILINE 0x00000100 // listbox flags #define FLAG_MULTISELECT 0x00000200 #define FLAG_EXTENDEDSEL 0x00000400 // OFN_PATHMUSTEXIST 0x00000800 // OFN_FILEMUSTEXIST 0x00001000 // OFN_CREATEPROMPT 0x00002000 // combobox flags #define FLAG_DROPLIST 0x00004000 // bitmap flags #define FLAG_RESIZETOFIT 0x00008000 // OFN_EXPLORER 0x00080000 // more text box flags #define FLAG_WANTRETURN 0x00100000 #define FLAG_VSCROLL 0x00200000 #define FLAG_HSCROLL 0x00400000 #define FLAG_READONLY 0x00800000 struct TableEntry { char *pszName; int nValue; }; int LookupToken(TableEntry*, char*); int LookupTokens(TableEntry*, char*); void ConvertNewLines(char *str); struct FieldType { char *pszText; char *pszState; char *pszRoot; char *pszListItems; char *pszFilter; int nType; RECT rect; int nMinLength; int nMaxLength; char *pszValidateText; int nFlags; bool bSaveDlg; HWND hwnd; UINT nControlID; int nParentIdx; // this is used by the filerequest and dirrequest controls HANDLE hImage; // this is used by image/icon field to save the handle to the image }; // initial buffer size. buffers will grow as required. // use a value larger than MAX_PATH to prevent need for excessive growing. #define MAX_BUFFER_LENGTH (300) char szBrowseButtonCaption[] = "..."; HWND hConfigWindow = NULL; HWND hMainWindow = NULL; HWND hCancelButton = NULL; HWND hNextButton = NULL; HWND hBackButton = NULL; HINSTANCE m_hInstance = NULL; struct _stack_t *pFilenameStackEntry = NULL; char *pszFilename = NULL; char *pszTitle = NULL; char *pszCancelButtonText = NULL; char *pszNextButtonText = NULL; char *pszBackButtonText = NULL; int bBackEnabled = FALSE; int bCancelEnabled = FALSE; // by ORTIM: 13-August-2002 int bCancelShow = FALSE; // by ORTIM: 13-August-2002 FieldType *pFields = NULL; #define DEFAULT_RECT 1018 int nRectId = 0; int nNumFields = 0; int g_done; // array of HWNDs and window styles used to make the main NSIS controls invisible while this program runs. bool BrowseForFile(int nControlIdx) { OPENFILENAME ofn={0,}; HWND hControl; BOOL bResult; FieldType *pThisField = &pFields[nControlIdx]; hControl = pThisField->hwnd; ofn.Flags = pThisField->nFlags & (OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_CREATEPROMPT | OFN_EXPLORER); // ofn.hInstance = m_hInstance; // no templates so we can leave this at NULL; ofn.hwndOwner = hConfigWindow; // ofn.lCustData = NULL; // ofn.lpfnHook = NULL; // ofn.lpstrCustomFilter = NULL; // ofn.lpstrDefExt = NULL; ofn.nMaxFile = MAX_PATH; ofn.lpstrFile = (char*)MALLOC(MAX_PATH); // ofn.nMaxFileTitle = MAX_PATH; // we ignore this for simplicity, leave lpstrFileTitle at NULL // ofn.lpstrFileTitle = new char [ofn.nMaxFileTitle]; ofn.lpstrFilter = pThisField->pszFilter; // TODO: implement this // ofn.lpstrInitialDir = NULL; // for now, just use the default initial directory. // ofn.lpstrTitle = NULL; // TODO: implement this // ofn.lpTemplateName = NULL; ofn.lStructSize = sizeof(ofn); // ofn.nFileExtension // this is output variable, leave it to 0 for now. // ofn.nFileOffset // this is output variable, leave it to 0 for now. // ofn.nFilterIndex = 1; // since we use no custom filters, leaving it at 0 means use the first. // ofn.nMaxCustFilter = 0; GetWindowText(hControl, ofn.lpstrFile, MAX_PATH); tryagain: if (pThisField->bSaveDlg) { bResult = GetSaveFileName(&ofn); } else { bResult = GetOpenFileName(&ofn); } if (bResult) { SetWindowText(hControl, ofn.lpstrFile); return true; } else if (ofn.lpstrFile[0] && CommDlgExtendedError() == FNERR_INVALIDFILENAME) { ofn.lpstrFile[0] = '\0'; goto tryagain; } return false; } int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) { TCHAR szDir[MAX_PATH]; if (uMsg == BFFM_INITIALIZED) { if (GetWindowText(pFields[(int)pData].hwnd, szDir, MAX_PATH) > 0) { SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)szDir); } } return 0; } bool BrowseForFolder(int nControlIdx) { BROWSEINFO bi; bi.hwndOwner = hConfigWindow; bi.pidlRoot = NULL; bi.pszDisplayName = (char*)MALLOC(MAX_PATH); bi.lpszTitle = pFields[nControlIdx].pszText; #ifndef BIF_NEWDIALOGSTYLE #define BIF_NEWDIALOGSTYLE 0x0040 #endif bi.ulFlags = BIF_STATUSTEXT | BIF_RETURNONLYFSDIRS| BIF_NEWDIALOGSTYLE; bi.lpfn = BrowseCallbackProc; bi.lParam = nControlIdx; bi.iImage = 0; if (pFields[nControlIdx].pszRoot) { LPSHELLFOLDER sf; ULONG eaten; LPITEMIDLIST root; #define _alloca MALLOC USES_CONVERSION; LPOLESTR s = A2OLE(pFields[nControlIdx].pszRoot); #undef _alloca SHGetDesktopFolder(&sf); sf->ParseDisplayName(hConfigWindow, NULL, (unsigned short *)s, &eaten, &root, NULL); bi.pidlRoot = root; sf->Release(); FREE(s); } // CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); LPITEMIDLIST pResult = SHBrowseForFolder(&bi); if (!pResult) { FREE(bi.pszDisplayName); return false; } char *pszFolder = (char*)MALLOC(MAX_PATH); if (SHGetPathFromIDList(pResult, pszFolder)) { SetWindowText(pFields[nControlIdx].hwnd, pszFolder); } LPMALLOC pMalloc; if (!SHGetMalloc(&pMalloc)) { pMalloc->Free(pResult); } FREE(bi.pszDisplayName); FREE(pszFolder); return true; } bool 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++) { // 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 (pFields[nIdx].nType >= FIELD_TEXT) { nLength = SendMessage(pFields[nIdx].hwnd, WM_GETTEXTLENGTH, 0, 0); if (((pFields[nIdx].nMaxLength > 0) && (nLength > pFields[nIdx].nMaxLength)) || ((pFields[nIdx].nMinLength > 0) && (nLength < pFields[nIdx].nMinLength))) { if (pFields[nIdx].pszValidateText) { MessageBox(hConfigWindow, pFields[nIdx].pszValidateText, NULL, MB_OK|MB_ICONWARNING); } SetFocus(pFields[nIdx].hwnd); return false; } } } return true; } bool SaveSettings(void) { static char szField[25]; int nIdx; HWND hwnd; int nBufLen = MAX_BUFFER_LENGTH; char *pszBuffer = (char*)MALLOC(nBufLen); if (!pszBuffer) return false; int CurrField = 1; for(nIdx = 0; nIdx < nNumFields; nIdx++) { if ( pFields[nIdx].nType == FIELD_BROWSEBUTTON ) continue; hwnd = pFields[nIdx].hwnd; wsprintf(szField, "Field %d", CurrField); switch(pFields[nIdx].nType) { case FIELD_CHECKBOX: case FIELD_RADIOBUTTON: { wsprintf(pszBuffer, "%d", !!SendMessage(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(pFields[nIdx].pszListItems) + 10; if (nLength > nBufLen) { FREE(pszBuffer); nBufLen = nLength; pszBuffer = (char*)MALLOC(nBufLen); if (!pszBuffer) return false; } char *pszItem = (char*)MALLOC(nBufLen); if (!pszItem) return false; *pszBuffer = '\0'; int nNumItems = SendMessage(hwnd, LB_GETCOUNT, 0, 0); for(int nIdx2 = 0; nIdx2 < nNumItems; nIdx2++) { if (SendMessage(hwnd, LB_GETSEL, nIdx2, 0) > 0) { if (*pszBuffer) lstrcat(pszBuffer, "|"); SendMessage(hwnd, LB_GETTEXT, (WPARAM)nIdx2, (LPARAM)pszItem); lstrcat(pszBuffer, pszItem); } } FREE(pszItem); break; } case FIELD_LABEL: *pszBuffer=0; break; default: { int nLength = SendMessage(pFields[nIdx].hwnd, WM_GETTEXTLENGTH, 0, 0); if (nLength > nBufLen) { FREE(pszBuffer); // add a bit extra so we do this less often nBufLen = nLength + 20; pszBuffer = (char*)MALLOC(nBufLen); if (!pszBuffer) return false; } *pszBuffer='"'; GetWindowText(hwnd, pszBuffer+1, nBufLen-1); pszBuffer[nLength+1]='"'; pszBuffer[nLength+2]='\0'; if ( pFields[nIdx].nType == FIELD_TEXT && pFields[nIdx].nFlags & FLAG_MULTILINE ) { char *pszBuf2 = (char*)MALLOC(nBufLen*2); // double the size, consider the worst case, all chars are \r\n char *p1, *p2; for (p1=pszBuffer,p2=pszBuf2; *p1; p1++, p2++) { if (*p1 == '\r') { *p2++ = '\\'; *p2 = 'r'; } else if (*p1 == '\n') { *p2++ = '\\'; *p2 = 'n'; } else if (*p1 == '\t') { *p2++ = '\\'; *p2 = 't'; } else *p2=*p1; } *p2 = 0; nBufLen = nBufLen*2; FREE(pszBuffer); pszBuffer=pszBuf2; } break; } } WritePrivateProfileString(szField, "STATE", pszBuffer, pszFilename); CurrField++; } FREE(pszBuffer); return true; } #define BROWSE_WIDTH 15 void AddBrowseButtons() { // this function loops through all the controls and if a filerequest or dirrequest // control is found, then it adds the corresponding browse button. // NOTE: this also resizes the text box created to make room for the button. int nIdx; FieldType *pNewField; for (nIdx = nNumFields - 1; nIdx >= 0; nIdx--) { // we loop down so we don't run into the newly added fields. switch (pFields[nIdx].nType) { case FIELD_FILEREQUEST: case FIELD_DIRREQUEST: // Insert button after edit, tabstop depends in creation order, // with this trick browse button get tabstop after parent edit if ( nIdx < nNumFields - 1 ) { CopyMemory(&pFields[nIdx+2], &pFields[nIdx+1], (nNumFields-nIdx-1)*sizeof(FieldType) ); for ( int i = nIdx+2; i < nNumFields; i++ ) { if ( pFields[i].nType == FIELD_BROWSEBUTTON ) pFields[i].nParentIdx++; // Ohh! where is your dady :) } } // After moving controls 1 position, don't forget to increment nNumFields at *end* of loop pNewField = &pFields[nIdx+1]; ZeroMemory(pNewField, sizeof(FieldType)); // don't use settings from other control pNewField->nControlID = 1200 + nNumFields; pNewField->nParentIdx = nIdx; pNewField->nType = FIELD_BROWSEBUTTON; pNewField->nFlags = pFields[nIdx].nFlags & (FLAG_DISABLED | FLAG_NOTABSTOP); //pNewField->pszListItems = NULL; //pNewField->nMaxLength = 0; //pNewField->nMinLength = 0; pNewField->pszText = STRDUP(szBrowseButtonCaption); // needed for generic FREE //pNewField->pszValidateText = NULL; pNewField->rect.right = pFields[nIdx].rect.right; pNewField->rect.left = pNewField->rect.right - BROWSE_WIDTH; pNewField->rect.bottom = pFields[nIdx].rect.bottom; pNewField->rect.top = pFields[nIdx].rect.top; pFields[nIdx].rect.right = pNewField->rect.left - 3; nNumFields++; break; } } } static char szResult[4096]; DWORD WINAPI myGetProfileString(LPCTSTR lpAppName, LPCTSTR lpKeyName) { *szResult = '\0'; return GetPrivateProfileString(lpAppName, lpKeyName, "", szResult, sizeof(szResult), pszFilename); } char * WINAPI myGetProfileStringDup(LPCTSTR lpAppName, LPCTSTR lpKeyName) { if (myGetProfileString(lpAppName, lpKeyName)) return strdup(szResult); else return NULL; } bool ReadSettings(void) { static char szField[25]; int nIdx; pszTitle = myGetProfileStringDup("Settings", "Title"); pszCancelButtonText = myGetProfileStringDup("Settings", "CancelButtonText"); pszNextButtonText = myGetProfileStringDup("Settings", "NextButtonText"); pszBackButtonText = myGetProfileStringDup("Settings", "BackButtonText"); nNumFields = GetPrivateProfileInt("Settings", "NumFields", 0, pszFilename); nRectId = GetPrivateProfileInt("Settings", "Rect", DEFAULT_RECT, pszFilename); bBackEnabled = GetPrivateProfileInt("Settings", "BackEnabled", 0xFFFF0000, pszFilename); // by ORTIM: 13-August-2002 bCancelEnabled = GetPrivateProfileInt("Settings", "CancelEnabled", 0xFFFF0000, pszFilename); bCancelShow = GetPrivateProfileInt("Settings", "CancelShow", 0xFFFF0000, pszFilename); 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; nIdx < nNumFields; nIdx++) { // Control types static TableEntry TypeTable[] = { { "LABEL", FIELD_LABEL }, { "TEXT", FIELD_TEXT }, { "PASSWORD", FIELD_TEXT }, { "LISTBOX", FIELD_LISTBOX }, { "COMBOBOX", FIELD_COMBOBOX }, { "DROPLIST", FIELD_COMBOBOX }, { "FILEREQUEST", FIELD_FILEREQUEST }, { "DIRREQUEST", FIELD_DIRREQUEST }, { "CHECKBOX", FIELD_CHECKBOX }, { "RADIOBUTTON", FIELD_RADIOBUTTON }, { "ICON", FIELD_ICON }, { "BITMAP", FIELD_BITMAP }, { "GROUPBOX", FIELD_GROUPBOX }, { NULL, 0 } }; // Control flags static TableEntry FlagTable[] = { { "FILE_MUST_EXIST", OFN_FILEMUSTEXIST }, { "PATH_MUST_EXIST", OFN_PATHMUSTEXIST }, { "WARN_IF_EXIST", OFN_OVERWRITEPROMPT }, { "PROMPT_CREATE", OFN_CREATEPROMPT }, { "RIGHT", FLAG_RIGHT }, { "PASSWORD", FLAG_PASSWORD }, { "DROPLIST", FLAG_DROPLIST }, { "MULTISELECT", FLAG_MULTISELECT }, { "EXTENDEDSELCT", FLAG_EXTENDEDSEL }, { "FILE_EXPLORER", OFN_EXPLORER }, { "FILE_HIDEREADONLY", OFN_HIDEREADONLY }, { "RESIZETOFIT", FLAG_RESIZETOFIT }, { "GROUP", FLAG_GROUP }, { "DISABLED", FLAG_DISABLED }, { "NOTABSTOP", FLAG_NOTABSTOP }, { "ONLY_NUMBERS", FLAG_ONLYNUMBERS }, { "MULTILINE", FLAG_MULTILINE }, { "VSCROLL", FLAG_VSCROLL }, { "HSCROLL", FLAG_HSCROLL }, { "WANTRETURN", FLAG_WANTRETURN }, { "READONLY", FLAG_READONLY }, { NULL, 0 } }; wsprintf(szField, "Field %d", nIdx + 1); myGetProfileString(szField, "TYPE"); // Get the control type pFields[nIdx].nType = LookupToken(TypeTable, szResult); if (!pFields[nIdx].nType) continue; // Lookup flags associated with the control type pFields[nIdx].nFlags |= LookupToken(FlagTable, szResult); pFields[nIdx].pszText = myGetProfileStringDup(szField, "TEXT"); // Label Text - convert newline if (pFields[nIdx].nType == FIELD_LABEL) { ConvertNewLines(pFields[nIdx].pszText); } // pszState cannot be NULL (?) myGetProfileString(szField, "STATE"); pFields[nIdx].pszState = STRDUP(szResult); pFields[nIdx].pszRoot = myGetProfileStringDup(szField, "ROOT"); { int nResult = myGetProfileString(szField, "ListItems"); if (nResult) { // add an extra | character to the end to simplify the loop where we add the items. pFields[nIdx].pszListItems = (char*)MALLOC(nResult + 2); strcpy(pFields[nIdx].pszListItems, szResult); pFields[nIdx].pszListItems[nResult] = '|'; pFields[nIdx].pszListItems[nResult + 1] = '\0'; } } pFields[nIdx].nMaxLength = GetPrivateProfileInt(szField, "MaxLen", 0, pszFilename); pFields[nIdx].nMinLength = GetPrivateProfileInt(szField, "MinLen", 0, pszFilename); pFields[nIdx].pszValidateText = myGetProfileStringDup(szField, "ValidateText"); // ValidateText - convert newline if (pFields[nIdx].pszValidateText) { ConvertNewLines(pFields[nIdx].pszValidateText); } { int nResult = GetPrivateProfileString(szField, "Filter", "All Files|*.*", szResult, sizeof(szResult), pszFilename); if (nResult) { // add an extra | character to the end to simplify the loop where we add the items. pFields[nIdx].pszFilter = (char*)MALLOC(nResult + 2); strcpy(pFields[nIdx].pszFilter, szResult); char *pszPos = pFields[nIdx].pszFilter; while (*pszPos) { if (*pszPos == '|') *pszPos = '\0'; pszPos++; } } } pFields[nIdx].rect.left = GetPrivateProfileInt(szField, "LEFT", 0, pszFilename); pFields[nIdx].rect.right = GetPrivateProfileInt(szField, "RIGHT", 0, pszFilename); pFields[nIdx].rect.top = GetPrivateProfileInt(szField, "TOP", 0, pszFilename); pFields[nIdx].rect.bottom = GetPrivateProfileInt(szField, "BOTTOM", 0, pszFilename); if (myGetProfileString(szField, "Flags")) { // append the | to make parsing a bit easier if (lstrlen(szResult) pszStart) { if (!stricmp("REQ_SAVE", pszStart)) { pFields[nIdx].bSaveDlg = true; } else { // v1.3 converted this to a table lookup. // I think it's a bit larger now, but we can // add new flags with very little overhead. pFields[nIdx].nFlags |= LookupToken(FlagTable, pszStart); } } // jump to the next item, skip any redundant | characters do { pszEnd++; } while (*pszEnd == '|'); pszStart = pszEnd; } pszEnd++; } } pFields[nIdx].nControlID = 1200 + nIdx; } AddBrowseButtons(); return true; } LRESULT WMCommandProc(HWND hWnd, UINT id, HWND hwndCtl, UINT codeNotify) { switch (codeNotify) { case BN_CLICKED: { for (int nIdx = 0; nIdx < nNumFields; nIdx++) { if (pFields[nIdx].nType == FIELD_BROWSEBUTTON) { if (id == pFields[nIdx].nControlID) { int nParentIdx = pFields[nIdx].nParentIdx; switch(pFields[nParentIdx].nType) { case FIELD_FILEREQUEST: BrowseForFile(nParentIdx); break; case FIELD_DIRREQUEST: BrowseForFolder(nParentIdx); break; } break; } } } } break; } 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) { // Get the settings ready for the leave function verification SaveSettings(); } 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 (lRes != 0 in that case) if (wParam == NOTIFY_BYE_BYE || wParam == -1 || ValidateFields()) { if (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_CTLCOLORSTATIC: case WM_CTLCOLOREDIT: case WM_CTLCOLORDLG: case WM_CTLCOLORBTN: case WM_CTLCOLORLISTBOX: { BOOL brush = (BOOL)GetWindowLong((HWND)lParam, GWL_USERDATA); if (brush) { SetBkMode((HDC)wParam, TRANSPARENT); return brush; } } } return 0; } int nIdx; HWND childwnd; int cw_vis; int was_ok_enabled; char old_cancel[256]; char old_ok[256]; char old_back[256]; int old_cancel_enabled; int old_cancel_visible; char old_title[1024]; int createCfgDlg() { UINT nAddMsg, nFindMsg, nSetSelMsg; g_is_back=0; g_is_cancel=0; if (!hMainWindow) { popstring(NULL); pushstring("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("error finding config"); return 1; } childwnd=FindWindowEx(hMainWindow,NULL,"#32770",NULL); // find window to replace if (!childwnd || nRectId != DEFAULT_RECT) childwnd=GetDlgItem(hMainWindow,nRectId); if (!childwnd) { popstring(NULL); pushstring("error finding childwnd"); return 1; } cw_vis=IsWindowVisible(childwnd); if (cw_vis) ShowWindow(childwnd,SW_HIDE); hCancelButton = GetDlgItem(hMainWindow,IDCANCEL); hNextButton = GetDlgItem(hMainWindow,IDOK); hBackButton = GetDlgItem(hMainWindow,3); was_ok_enabled=EnableWindow(hNextButton,1); GetWindowText(hCancelButton,old_cancel,sizeof(old_cancel)); if (pszCancelButtonText) SetWindowText(hCancelButton,pszCancelButtonText); GetWindowText(hNextButton,old_ok,sizeof(old_ok)); if (pszNextButtonText) SetWindowText(hNextButton,pszNextButtonText); GetWindowText(hBackButton,old_back,sizeof(old_back)); if (pszBackButtonText) SetWindowText(hBackButton,pszBackButtonText); if (bBackEnabled!=0xFFFF0000) EnableWindow(hBackButton,bBackEnabled); if (bCancelEnabled!=0xFFFF0000) old_cancel_enabled=!EnableWindow(hCancelButton,bCancelEnabled); if (bCancelShow!=0xFFFF0000) old_cancel_visible=ShowWindow(hCancelButton,bCancelShow?SW_SHOWNA:SW_HIDE); HFONT hFont = (HFONT)SendMessage(hMainWindow, WM_GETFONT, 0, 0); RECT dialog_r; hConfigWindow=CreateDialog(m_hInstance,MAKEINTRESOURCE(IDD_DIALOG1),hMainWindow,cfgDlgProc); if (hConfigWindow) { GetWindowRect(childwnd,&dialog_r); ScreenToClient(hMainWindow,(LPPOINT)&dialog_r); ScreenToClient(hMainWindow,((LPPOINT)&dialog_r)+1); SetWindowPos(hConfigWindow,0,dialog_r.left,dialog_r.top,dialog_r.right-dialog_r.left,dialog_r.bottom-dialog_r.top,SWP_NOZORDER|SWP_NOACTIVATE); // Sets the font of IO window to be the same as the main window SendMessage(hConfigWindow, WM_SETFONT, (WPARAM)hFont, TRUE); } else { popstring(NULL); pushstring("error creating dialog"); return 1; } // Init dialog unit conversion HDC memDC = CreateCompatibleDC(GetDC(hConfigWindow)); SelectObject(memDC, hFont); TEXTMETRIC tm; GetTextMetrics(memDC, &tm); int baseUnitY = tm.tmHeight; SIZE size; GetTextExtentPoint32(memDC,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size); int baseUnitX = (size.cx / 26 + 1) / 2; DeleteDC(memDC); #define DEFAULT_STYLES (WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS) for (nIdx = 0; nIdx < nNumFields; nIdx++) { static struct { char* pszClass; DWORD dwStyle; DWORD dwExStyle; } ClassTable[] = { { "STATIC", // FIELD_LABEL DEFAULT_STYLES /*| WS_TABSTOP*/, WS_EX_TRANSPARENT }, { "STATIC", // FIELD_ICON DEFAULT_STYLES /*| WS_TABSTOP*/ | SS_ICON, 0 }, { "STATIC", // FIELD_BITMAP DEFAULT_STYLES /*| WS_TABSTOP*/ | SS_BITMAP, 0 }, { "BUTTON", // FIELD_BROWSEBUTTON DEFAULT_STYLES | WS_TABSTOP, 0 }, { "BUTTON", // FIELD_CHECKBOX DEFAULT_STYLES | WS_TABSTOP | BS_TEXT | BS_VCENTER | BS_AUTOCHECKBOX | BS_MULTILINE, 0 }, { "BUTTON", // FIELD_RADIOBUTTON DEFAULT_STYLES | WS_TABSTOP | BS_TEXT | BS_VCENTER | BS_AUTORADIOBUTTON | BS_MULTILINE, 0 }, { "EDIT", // FIELD_TEXT DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE }, { "EDIT", // FIELD_FILEREQUEST DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE }, { "EDIT", // FIELD_DIRREQUEST DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE }, { "COMBOBOX", // FIELD_COMBOBOX DEFAULT_STYLES | WS_TABSTOP | WS_VSCROLL | WS_CLIPCHILDREN | CBS_AUTOHSCROLL | CBS_HASSTRINGS, WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE }, { "LISTBOX", // FIELD_LISTBOX DEFAULT_STYLES | WS_TABSTOP | WS_VSCROLL | LBS_DISABLENOSCROLL | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT, WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE }, { "BUTTON", // FIELD_GROUPBOX DEFAULT_STYLES | BS_GROUPBOX, WS_EX_TRANSPARENT } }; int nType = pFields[nIdx].nType; #undef DEFAULT_STYLES if (nType < 1 || nType > (sizeof(ClassTable) / sizeof(ClassTable[0]))) continue; DWORD dwStyle = ClassTable[pFields[nIdx].nType - 1].dwStyle; DWORD dwExStyle = ClassTable[pFields[nIdx].nType - 1].dwExStyle; // Convert from dialog units RECT rect; rect.left = MulDiv(pFields[nIdx].rect.left, baseUnitX, 4); rect.right = MulDiv(pFields[nIdx].rect.right, baseUnitX, 4); rect.top = MulDiv(pFields[nIdx].rect.top, baseUnitY, 8); rect.bottom = MulDiv(pFields[nIdx].rect.bottom, baseUnitY, 8); if (pFields[nIdx].rect.left < 0) rect.left += dialog_r.right - dialog_r.left; if (pFields[nIdx].rect.right < 0) rect.right += dialog_r.right - dialog_r.left; if (pFields[nIdx].rect.top < 0) rect.top += dialog_r.bottom - dialog_r.top; if (pFields[nIdx].rect.bottom < 0) rect.bottom += dialog_r.bottom - dialog_r.top; char *title = pFields[nIdx].pszText; switch (nType) { case FIELD_CHECKBOX: case FIELD_RADIOBUTTON: if (pFields[nIdx].nFlags & FLAG_RIGHT) dwStyle |= BS_RIGHTBUTTON; break; case FIELD_FILEREQUEST: case FIELD_DIRREQUEST: case FIELD_TEXT: if (pFields[nIdx].nFlags & FLAG_PASSWORD) dwStyle |= ES_PASSWORD; if (pFields[nIdx].nFlags & FLAG_ONLYNUMBERS) dwStyle |= ES_NUMBER; if (pFields[nIdx].nFlags & FLAG_MULTILINE) { dwStyle |= ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL; ConvertNewLines(pFields[nIdx].pszState); } if ( pFields[nIdx].nFlags & FLAG_WANTRETURN ) dwStyle |= ES_WANTRETURN; if (pFields[nIdx].nFlags & FLAG_VSCROLL) dwStyle |= WS_VSCROLL; if (pFields[nIdx].nFlags & FLAG_HSCROLL) dwStyle |= WS_HSCROLL; if (pFields[nIdx].nFlags & FLAG_READONLY) dwStyle |= ES_READONLY ; title = pFields[nIdx].pszState; break; case FIELD_COMBOBOX: dwStyle |= (pFields[nIdx].nFlags & FLAG_DROPLIST) ? CBS_DROPDOWNLIST : CBS_DROPDOWN; title = pFields[nIdx].pszState; break; case FIELD_LISTBOX: if (pFields[nIdx].nFlags & FLAG_EXTENDEDSEL) dwStyle |= LBS_EXTENDEDSEL; if (pFields[nIdx].nFlags & FLAG_MULTISELECT) dwStyle |= LBS_MULTIPLESEL; break; } if (pFields[nIdx].nFlags & FLAG_DISABLED) dwStyle |= WS_DISABLED; if (pFields[nIdx].nFlags & FLAG_GROUP) dwStyle |= WS_GROUP; if (pFields[nIdx].nFlags & FLAG_NOTABSTOP) dwStyle &= ~WS_TABSTOP; HWND hwCtrl = pFields[nIdx].hwnd = CreateWindowEx( dwExStyle, ClassTable[pFields[nIdx].nType - 1].pszClass, title, dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hConfigWindow, (HMENU)pFields[nIdx].nControlID, m_hInstance, NULL ); if (hwCtrl) { // Sets the font of IO window to be the same as the main window SendMessage(hwCtrl, WM_SETFONT, (WPARAM)hFont, TRUE); // make sure we created the window, then set additional attributes if (pFields[nIdx].nMaxLength > 0) { switch (nType) { case FIELD_TEXT: case FIELD_DIRREQUEST: case FIELD_FILEREQUEST: SendMessage(hwCtrl, EM_LIMITTEXT, (WPARAM)pFields[nIdx].nMaxLength, (LPARAM)0); break; } } if ((nType == FIELD_CHECKBOX) || (nType == FIELD_RADIOBUTTON)) { if (pFields[nIdx].pszState[0] == '1') { SendMessage(hwCtrl, BM_SETCHECK, (WPARAM)BST_CHECKED, 0); } } else if (pFields[nIdx].pszListItems && ( ((nType == FIELD_COMBOBOX) && (nAddMsg = CB_ADDSTRING) && (nFindMsg = CB_FINDSTRINGEXACT) && (nSetSelMsg = CB_SETCURSEL)) || ((nType == FIELD_LISTBOX ) && (nAddMsg = LB_ADDSTRING) && (nFindMsg = LB_FINDSTRINGEXACT) && (nSetSelMsg = LB_SETCURSEL)) )) { // if this is a listbox or combobox, we need to add the list items. char *pszStart, *pszEnd, *pszList; pszStart = pszEnd = pszList = STRDUP(pFields[nIdx].pszListItems); while ((*pszEnd) && (*pszStart)) { if (*pszEnd == '|') { *pszEnd = '\0'; if (pszEnd > pszStart) { SendMessage(hwCtrl, nAddMsg, 0, (LPARAM)pszStart); } // jump to the next item, skip any redundant | characters do { pszEnd++; } while (*pszEnd == '|'); pszStart = pszEnd; } pszEnd++; } FREE(pszList); if (pFields[nIdx].pszState) { if (pFields[nIdx].nFlags & (FLAG_MULTISELECT|FLAG_EXTENDEDSEL) && nFindMsg == LB_FINDSTRINGEXACT) { SendMessage(hwCtrl, LB_SETSEL, FALSE, -1); pszStart = pszEnd = pFields[nIdx].pszState; while (*pszStart) { char cLast = *pszEnd; if (*pszEnd == '|') *pszEnd = '\0'; if (!*pszEnd) { if (pszEnd > pszStart) { int nItem = SendMessage(hwCtrl, nFindMsg, -1, (LPARAM)pszStart); if (nItem != CB_ERR) { // CB_ERR == LB_ERR == -1 SendMessage(hwCtrl, LB_SETSEL, TRUE, nItem); } } if (cLast) { do { pszEnd++; } while (*pszEnd == '|'); } pszStart = pszEnd; } pszEnd++; } } else { int nItem = SendMessage(hwCtrl, nFindMsg, -1, (LPARAM)pFields[nIdx].pszState); if (nItem != CB_ERR) { // CB_ERR == LB_ERR == -1 SendMessage(hwCtrl, nSetSelMsg, nItem, 0); } } } } else if (nType == FIELD_BITMAP || nType == FIELD_ICON) { WPARAM nImageType = nType == FIELD_BITMAP ? IMAGE_BITMAP : IMAGE_ICON; LPARAM nImage = 0; if (pFields[nIdx].pszText) { pFields[nIdx].hImage = LoadImage( 0, pFields[nIdx].pszText, nImageType, (pFields[nIdx].rect.right - pFields[nIdx].rect.left > 0 && !(pFields[nIdx].nFlags & FLAG_RESIZETOFIT)) ? (pFields[nIdx].rect.right - pFields[nIdx].rect.left) : (rect.right - rect.left), (pFields[nIdx].rect.right - pFields[nIdx].rect.left > 0 && !(pFields[nIdx].nFlags & FLAG_RESIZETOFIT)) ? (pFields[nIdx].rect.bottom - pFields[nIdx].rect.top) : (rect.bottom - rect.top), LR_LOADFROMFILE ); nImage = (LPARAM)pFields[nIdx].hImage; } else nImage = (LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(103)); SendMessage( hwCtrl, STM_SETIMAGE, nImageType, nImage ); } } } if (pszTitle) { GetWindowText(hMainWindow,old_title,sizeof(old_title)); SetWindowText(hMainWindow,pszTitle); } pFilenameStackEntry = *g_stacktop; *g_stacktop = (*g_stacktop)->next; char tmp[32]; wsprintf(tmp,"%d",hConfigWindow); pushstring(tmp); return 0; } void showCfgDlg() { lpWndProcOld = (void *) SetWindowLong(hMainWindow,DWL_DLGPROC,(long)ParentWndProc); SendMessage(hMainWindow, WM_NOTIFY_CUSTOM_READY, 0, 0); ShowWindow(hConfigWindow, SW_SHOWNA); SetFocus(hNextButton); g_done=0; while (!g_done) { MSG msg; int nResult = 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(); if (lpWndProcOld) SetWindowLong(hMainWindow,DWL_DLGPROC,(long)lpWndProcOld); DestroyWindow(hConfigWindow); if (was_ok_enabled) EnableWindow(hNextButton,0); SetWindowText(hCancelButton,old_cancel); SetWindowText(hNextButton,old_ok); SetWindowText(hBackButton,old_back); // by ORTIM: 13-August-2002 if (bCancelEnabled!=0xFFFF0000) EnableWindow(hCancelButton,old_cancel_enabled); if (bCancelShow!=0xFFFF0000) ShowWindow(hCancelButton,old_cancel_visible?SW_SHOWNA:SW_HIDE); if (pszTitle) SetWindowText(hMainWindow,old_title); if (cw_vis) ShowWindow(childwnd,SW_SHOWNA); FREE(pFilenameStackEntry); FREE(pszTitle); FREE(pszCancelButtonText); FREE(pszNextButtonText); FREE(pszBackButtonText); int i = nNumFields; while (i--) { FREE(pFields[i].pszText); FREE(pFields[i].pszState); FREE(pFields[i].pszListItems); FREE(pFields[i].pszFilter); FREE(pFields[i].pszRoot); if (pFields[i].nType == FIELD_BITMAP) { DeleteObject(pFields[i].hImage); } if (pFields[i].nType == FIELD_ICON) { DestroyIcon((HICON)pFields[i].hImage); } } FREE(pFields); pushstring(g_is_cancel?"cancel":g_is_back?"back":"success"); } int initCalled; extern "C" void __declspec(dllexport) dialog(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) { hMainWindow=hwndParent; EXDLL_INIT(); if (initCalled) { pushstring("error"); return; } if (createCfgDlg()) { return; } popstring(NULL); showCfgDlg(); } extern "C" void __declspec(dllexport) initDialog(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) { hMainWindow=hwndParent; EXDLL_INIT(); if (initCalled) { pushstring("error"); return; } initCalled++; createCfgDlg(); } extern "C" void __declspec(dllexport) show(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) { EXDLL_INIT(); if (!initCalled) { pushstring("error"); return; } initCalled--; showCfgDlg(); } #ifdef DEBUG extern "C" BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) #else extern "C" BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) #endif { m_hInstance=(HINSTANCE) hInst; if (ul_reason_for_call == DLL_THREAD_DETACH || ul_reason_for_call == DLL_PROCESS_DETACH) DestroyWindow(hConfigWindow); return TRUE; } int LookupToken(TableEntry* psTable_, char* pszToken_) { for (int i = 0; psTable_[i].pszName; i++) if (!stricmp(pszToken_, psTable_[i].pszName)) return psTable_[i].nValue; return 0; } int LookupTokens(TableEntry* psTable_, char* pszTokens_) { int n = 0; char *pszStart = pszTokens_; char *pszEnd = pszTokens_; for (;;) { if (*pszEnd == '\0') { n |= LookupToken(psTable_, pszStart); break; } if (*pszEnd == '|') { *pszEnd = '\0'; n |= LookupToken(psTable_, pszStart); *pszEnd = '|'; pszStart = pszEnd + 1; } pszEnd++; } return n; } void ConvertNewLines(char *str) { char *p1, *p2; if (!str) return; for (p1=p2=str; *p1; p1++, p2++) { if (*p1 == '\\') { switch (p1[1]) { case 'n': *p2 = '\n'; break; case 'r': *p2 = '\r'; break; case 't': *p2 = '\t'; break; case '\\': *p2 = '\\'; break; default: p1--; p2--; break; } p1++; } else *p2 = *p1; } *p2 = 0; }