home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 March / CMCD0304.ISO / Software / Freeware / Programare / nullsoft / nsis20.exe / Contrib / InstallOptions / InstallerOptions.cpp < prev    next >
C/C++ Source or Header  |  2004-01-31  |  47KB  |  1,447 lines

  1. /*********************************************************
  2.  *
  3.  *  InstallOptions version 2.0 - Plugin for custom pages
  4.  *
  5.  *  See Readme.html for documentation and license
  6.  *
  7.  *********************************************************/
  8.  
  9. #include <windows.h>
  10. #include <windowsx.h>
  11. #include <shlobj.h>
  12. #include <commdlg.h>
  13. #include <cderr.h>
  14. #include "resource.h"
  15. #include "Shellapi.h"
  16.  
  17. #define popstring dontuseme
  18. #include "../exdll/exdll.h"
  19. #undef popstring
  20.  
  21. // Use for functions only called from one place to possibly reduce some code
  22. // size.  Allows the source code to remain readable by leaving the function
  23. // intact.
  24. #ifdef _MSC_VER
  25. #define INLINE __forceinline
  26. #else
  27. #define INLINE inline
  28. #endif
  29.  
  30. void *WINAPI MALLOC(int len) { return (void*)GlobalAlloc(GPTR,len); }
  31. void WINAPI FREE(void *d) { if (d) GlobalFree((HGLOBAL)d); }
  32.  
  33. void WINAPI popstring(char *str)
  34. {
  35.   if (g_stacktop && *g_stacktop)
  36.   {
  37.     stack_t *th = *g_stacktop;
  38.     *g_stacktop = th->next;
  39.     if (str)
  40.       lstrcpy(str, th->text);
  41.     FREE(th);
  42.   }
  43. }
  44.  
  45. #define strcpy(x,y) lstrcpy(x,y)
  46. //#define strncpy(x,y,z) lstrcpyn(x,y,z)
  47. #define strdup(x) STRDUP(x)
  48. #define stricmp(x,y) lstrcmpi(x,y)
  49. //#define abs(x) ((x) < 0 ? -(x) : (x))
  50.  
  51. char *WINAPI STRDUP(const char *c)
  52. {
  53.   char *t=(char*)MALLOC(lstrlen(c)+1);
  54.   return lstrcpy(t,c);
  55. }
  56.  
  57. // Turn a pair of chars into a word
  58. // Turn four chars into a dword
  59. #ifdef __BIG_ENDIAN__ // Not very likely, but, still...
  60. #define CHAR2_TO_WORD(a,b) (((WORD)(b))|((a)<<8))
  61. #define CHAR4_TO_DWORD(a,b,c,d) (((DWORD)CHAR2_TO_WORD(c,d))|(CHAR2_TO_WORD(a,b)<<16))
  62. #else
  63. #define CHAR2_TO_WORD(a,b) (((WORD)(a))|((b)<<8))
  64. #define CHAR4_TO_DWORD(a,b,c,d) (((DWORD)CHAR2_TO_WORD(a,b))|(CHAR2_TO_WORD(c,d)<<16))
  65. #endif
  66.  
  67. // Field types
  68. // NB - the order of this list is important - see below
  69.  
  70. #define FIELD_INVALID      (0)
  71. #define FIELD_LABEL        (1)
  72. #define FIELD_ICON         (2)
  73. #define FIELD_BITMAP       (3)
  74. #define FIELD_BROWSEBUTTON (4)
  75. #define FIELD_LINK         (5)
  76. #define FIELD_BUTTON       (6)
  77. #define FIELD_GROUPBOX     (7)
  78. #define FIELD_CHECKBOX     (8)
  79. #define FIELD_RADIOBUTTON  (9)
  80. #define FIELD_TEXT         (10)
  81. #define FIELD_FILEREQUEST  (11)
  82. #define FIELD_DIRREQUEST   (12)
  83. #define FIELD_COMBOBOX     (13)
  84. #define FIELD_LISTBOX      (14)
  85.  
  86. #define FIELD_SETFOCUS     FIELD_CHECKBOX // First field that qualifies for having the initial keyboard focus
  87. #define FIELD_CHECKLEN     FIELD_TEXT     // First field to have length of state value checked against MinLen/MaxLen
  88.  
  89. //---------------------------------------------------------------------
  90. // settings
  91. #define IO_ENABLE_LINK
  92.  
  93. //#define IO_LINK_UNDERLINED // Uncomment to show links text underlined
  94. //---------------------------------------------------------------------
  95.  
  96. // Flags
  97.  
  98. // LBS_NOTIFY              0x00000001 // LISTBOX/CHECKBOX/RADIOBUTTON/BUTTON/LINK - Notify NSIS script when control is "activated" (exact meaning depends on the type of control)
  99. // OFN_OVERWRITEPROMPT     0x00000002 // FILEREQUEST
  100. // OFN_HIDEREADONLY        0x00000004 // FILEREQUEST
  101. // LBS_MULTIPLESEL         0x00000008 // LISTBOX
  102. #define FLAG_READONLY      0x00000010 // TEXT/FILEREQUEST/DIRREQUEST
  103. // BS_LEFTTEXT             0x00000020 // CHECKBOX/RADIOBUTTON
  104. #define FLAG_PASSWORD      0x00000040 // TEXT/FILEREQUEST/DIRREQUEST
  105. #define FLAG_ONLYNUMBERS   0x00000080 // TEXT/FILEREQUEST/DIRREQUEST
  106. #define FLAG_MULTILINE     0x00000100 // TEXT/FILEREQUEST/DIRREQUEST
  107. #define FLAG_NOWORDWRAP    0x00000200 // TEXT/FILEREQUEST/DIRREQUEST - Disable word-wrap in multi-line text boxes
  108. #define FLAG_WANTRETURN    0x00000400 // TEXT/FILEREQUEST/DIRREQUEST
  109. // LBS_EXTENDEDSEL         0x00000800 // LISTBOX
  110. // OFN_PATHMUSTEXIST       0x00000800 // FILEREQUEST
  111. // OFN_FILEMUSTEXIST       0x00001000 // FILEREQUEST
  112. // OFN_CREATEPROMPT        0x00002000 // FILEREQUEST
  113. #define FLAG_DROPLIST      0x00004000 // COMBOBOX
  114. #define FLAG_RESIZETOFIT   0x00008000 // BITMAP
  115. // WS_TABSTOP              0x00010000 // *ALL*
  116. // WS_GROUP                0x00020000 // *ALL*
  117. #define FLAG_SAVEAS        0x00040000 // FILEREQUEST - Show "Save As" instead of "Open" for FileRequest field
  118. // OFN_EXPLORER            0x00080000 // FILEREQUEST
  119. // WS_HSCROLL              0x00100000 // *ALL*
  120. // WS_VSCROLL              0x00200000 // *ALL*
  121. // WS_DISABLED             0x08000000 // *ALL*
  122.  
  123. struct TableEntry {
  124.   char *pszName;
  125.   int   nValue;
  126. };
  127.  
  128. int WINAPI LookupToken(TableEntry*, char*);
  129. int WINAPI LookupTokens(TableEntry*, char*);
  130.  
  131. void WINAPI ConvertNewLines(char *str);
  132.  
  133. // all allocated buffers must be first in the struct
  134. // when adding more allocated buffers to FieldType, don't forget to change this define
  135. #define FIELD_BUFFERS 6
  136. struct FieldType {
  137.   char  *pszText;
  138.   char  *pszState;
  139.   char  *pszRoot;
  140.  
  141.   char  *pszListItems;
  142.   char  *pszFilter;
  143.  
  144.   char   *pszValidateText;
  145.   int    nMinLength;
  146.   int    nMaxLength;
  147.  
  148.   int    nType;
  149.   RECT   rect;
  150.  
  151.   int    nFlags;
  152.  
  153.   HWND   hwnd;
  154.   UINT   nControlID;
  155.  
  156.   int    nParentIdx;  // this is used to store original windowproc for LINK
  157.   HANDLE hImage; // this is used by image/icon field to save the handle to the image
  158. };
  159.  
  160. // initial buffer size.  buffers will grow as required.
  161. // use a value larger than MAX_PATH to prevent need for excessive growing.
  162. #define BUFFER_SIZE 8192 // 8kb of mem is max char count in multiedit
  163.  
  164. char szBrowseButtonCaption[] = "...";
  165.  
  166. HWND hConfigWindow    = NULL;
  167. HWND hMainWindow      = NULL;
  168. HWND hCancelButton    = NULL;
  169. HWND hNextButton      = NULL;
  170. HWND hBackButton      = NULL;
  171.  
  172. HINSTANCE m_hInstance = NULL;
  173.  
  174. struct _stack_t *pFilenameStackEntry = NULL;
  175.  
  176. char *pszFilename         = NULL;
  177. char *pszTitle            = NULL;
  178. char *pszCancelButtonText = NULL;
  179. char *pszNextButtonText   = NULL;
  180. char *pszBackButtonText   = NULL;
  181.  
  182. int bBackEnabled   = FALSE;
  183. int bCancelEnabled = FALSE;   // by ORTIM: 13-August-2002
  184. int bCancelShow    = FALSE;   // by ORTIM: 13-August-2002
  185.  
  186. int bRTL = FALSE;
  187.  
  188. FieldType *pFields   = NULL;
  189. #define DEFAULT_RECT 1018
  190. int nRectId          = 0;
  191. int nNumFields       = 0;
  192. int g_done;
  193. int g_NotifyField;  // Field number of notifying control
  194.  
  195. int WINAPI FindControlIdx(UINT id)
  196. {
  197.   for (int nIdx = 0; nIdx < nNumFields; nIdx++)
  198.     if (id == pFields[nIdx].nControlID)
  199.       return nIdx;
  200.   return -1;
  201. }
  202.  
  203. LRESULT WINAPI mySendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
  204. {
  205.   return SendMessage(hWnd, Msg, wParam, lParam);
  206. }
  207.  
  208. void WINAPI mySetFocus(HWND hWnd)
  209. {
  210.   mySendMessage(hMainWindow, WM_NEXTDLGCTL, (WPARAM)hWnd, TRUE);
  211. }
  212.  
  213. void WINAPI mySetWindowText(HWND hWnd, LPCTSTR pszText)
  214. {
  215.   if (pszText)
  216.     SetWindowText(hWnd, pszText);
  217. }
  218.  
  219. int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) {
  220.   static TCHAR szDir[MAX_PATH];
  221.  
  222.   if (uMsg == BFFM_INITIALIZED &&
  223.       GetWindowText(pFields[(int)pData].hwnd, szDir, MAX_PATH) > 0)
  224.     mySendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)szDir);
  225.   return 0;
  226. }
  227.  
  228.  
  229. bool INLINE ValidateFields() {
  230.   int nIdx;
  231.   int nLength;
  232.  
  233.   // In the unlikely event we can't allocate memory, go ahead and return true so we can get out of here.
  234.   // May cause problems for the install script, but no memory is problems for us.
  235.   for (nIdx = 0; nIdx < nNumFields; nIdx++) {
  236.     FieldType *pField = pFields + nIdx;
  237.     // this if statement prevents a stupid bug where a min/max length is assigned to a label control
  238.     // where the user obviously has no way of changing what is displayed. (can you say, "infinite loop"?)
  239.     if (pField->nType >= FIELD_CHECKLEN) {
  240.       nLength = mySendMessage(pField->hwnd, WM_GETTEXTLENGTH, 0, 0);
  241.  
  242.       if (((pField->nMaxLength > 0) && (nLength > pField->nMaxLength)) ||
  243.          ((pField->nMinLength > 0) && (nLength < pField->nMinLength))) {
  244.         if (pField->pszValidateText) {
  245.           MessageBox(hConfigWindow, pField->pszValidateText, NULL, MB_OK|MB_ICONWARNING);
  246.         }
  247.         mySetFocus(pField->hwnd);
  248.         return false;
  249.       }
  250.  
  251.     }
  252.   }
  253.   return true;
  254. }
  255.  
  256. bool WINAPI SaveSettings(void) {
  257.   static char szField[25];
  258.   int nBufLen = BUFFER_SIZE;
  259.   char *pszBuffer = (char*)MALLOC(nBufLen);
  260.   if (!pszBuffer) return false;
  261.  
  262.   int nIdx;
  263.   int CurrField;
  264.   for (nIdx = 0, CurrField = 1; nIdx < nNumFields; nIdx++, CurrField++) {
  265.     FieldType *pField = pFields + nIdx;
  266.     HWND hwnd = pField->hwnd;
  267.     switch (pField->nType) {
  268.       case FIELD_BROWSEBUTTON:
  269.         if (g_NotifyField > CurrField)
  270.           --g_NotifyField;
  271.         --CurrField;
  272.       default:
  273.         continue;
  274.  
  275.       case FIELD_CHECKBOX:
  276.       case FIELD_RADIOBUTTON:
  277.         wsprintf(pszBuffer, "%d", !!mySendMessage(hwnd, BM_GETCHECK, 0, 0));
  278.         break;
  279.  
  280.       case FIELD_LISTBOX:
  281.       {
  282.         // Ok, this one requires a bit of work.
  283.         // First, we allocate a buffer long enough to hold every item.
  284.         // Then, we loop through every item and if it's selected we add it to our buffer.
  285.         // If there is already an item in the list, then we prepend a | character before the new item.
  286.         // We could simplify for single-select boxes, but using one piece of code saves some space.
  287.         int nLength = lstrlen(pField->pszListItems) + 10;
  288.         if (nLength > nBufLen) {
  289.           FREE(pszBuffer);
  290.           nBufLen = nLength;
  291.           pszBuffer = (char*)MALLOC(nBufLen);
  292.           if (!pszBuffer) return false;
  293.         }
  294.         char *pszItem = (char*)MALLOC(nBufLen);
  295.         if (!pszItem) return false;
  296.  
  297.         *pszBuffer = '\0';
  298.         int nNumItems = mySendMessage(hwnd, LB_GETCOUNT, 0, 0);
  299.         for (int nIdx2 = 0; nIdx2 < nNumItems; nIdx2++) {
  300.           if (mySendMessage(hwnd, LB_GETSEL, nIdx2, 0) > 0) {
  301.             if (*pszBuffer) lstrcat(pszBuffer, "|");
  302.             mySendMessage(hwnd, LB_GETTEXT, (WPARAM)nIdx2, (LPARAM)pszItem);
  303.             lstrcat(pszBuffer, pszItem);
  304.           }
  305.         }
  306.  
  307.         FREE(pszItem);
  308.         break;
  309.       }
  310.  
  311.       case FIELD_TEXT:
  312.       case FIELD_FILEREQUEST:
  313.       case FIELD_DIRREQUEST:
  314.       case FIELD_COMBOBOX:
  315.       {
  316.         int nLength = mySendMessage(pField->hwnd, WM_GETTEXTLENGTH, 0, 0);
  317.         if (nLength > nBufLen) {
  318.           FREE(pszBuffer);
  319.           // add a bit extra so we do this less often
  320.           nBufLen = nLength + 20;
  321.           pszBuffer = (char*)MALLOC(nBufLen);
  322.           if (!pszBuffer) return false;
  323.         }
  324.         *pszBuffer='"';
  325.         GetWindowText(hwnd, pszBuffer+1, nBufLen-1);
  326.         pszBuffer[nLength+1]='"';
  327.         pszBuffer[nLength+2]='\0';
  328.  
  329.         if (pField->nType == FIELD_TEXT && (pField->nFlags & FLAG_MULTILINE))
  330.         {
  331.           char *pszBuf2 = (char*)MALLOC(nBufLen*2); // double the size, consider the worst case, all chars are \r\n
  332.           char *p1, *p2;
  333.           for (p1 = pszBuffer, p2 = pszBuf2; *p1; p1 = CharNext(p1), p2 = CharNext(p2))
  334.           {
  335.             switch (*p1) {
  336.               case '\t':
  337.                 *(LPWORD)p2 = CHAR2_TO_WORD('\\', 't');
  338.                 p2++;
  339.                 break;
  340.               case '\n':
  341.                 *(LPWORD)p2 = CHAR2_TO_WORD('\\', 'n');
  342.                 p2++;
  343.                 break;
  344.               case '\r':
  345.                 *(LPWORD)p2 = CHAR2_TO_WORD('\\', 'r');
  346.                 p2++;
  347.                 break;
  348.               case '\\':
  349.                 *p2++ = '\\';
  350.               default:
  351.                 lstrcpyn(p2, p1, CharNext(p1) - p1 + 1);
  352.                 break;
  353.             }
  354.           }
  355.           *p2 = 0;
  356.           nBufLen = nBufLen*2;
  357.           FREE(pszBuffer);
  358.           pszBuffer=pszBuf2;
  359.         }
  360.         break;
  361.       }
  362.     }
  363.     wsprintf(szField, "Field %d", CurrField);
  364.     WritePrivateProfileString(szField, "State", pszBuffer, pszFilename);
  365.   }
  366.  
  367.   // Tell NSIS which control was activated, if any
  368.   wsprintf(pszBuffer, "%d", g_NotifyField);
  369.   WritePrivateProfileString("Settings", "State", pszBuffer, pszFilename);
  370.  
  371.   FREE(pszBuffer);
  372.  
  373.   return true;
  374. }
  375.  
  376. #define BROWSE_WIDTH 15
  377.  
  378. static char szResult[BUFFER_SIZE];
  379. char *pszAppName;
  380.  
  381. DWORD WINAPI myGetProfileString(LPCTSTR lpKeyName)
  382. {
  383.   *szResult = '\0';
  384.   return GetPrivateProfileString(pszAppName, lpKeyName, "", szResult, BUFFER_SIZE, pszFilename);
  385. }
  386.  
  387. char * WINAPI myGetProfileStringDup(LPCTSTR lpKeyName)
  388. {
  389.   int nSize = myGetProfileString(lpKeyName);
  390.   if (nSize)
  391.     return strdup(szResult);
  392.   else
  393.     return NULL;
  394. }
  395.  
  396. UINT WINAPI myGetProfileInt(LPCTSTR lpKeyName, INT nDefault)
  397. {
  398.   return GetPrivateProfileInt(pszAppName, lpKeyName, nDefault, pszFilename);
  399. }
  400.  
  401. int WINAPI ReadSettings(void) {
  402.   static char szField[25];
  403.   int nIdx, nCtrlIdx;
  404.  
  405.   pszAppName = "Settings";
  406.   pszTitle = myGetProfileStringDup("Title");
  407.   pszCancelButtonText = myGetProfileStringDup("CancelButtonText");
  408.   pszNextButtonText = myGetProfileStringDup("NextButtonText");
  409.   pszBackButtonText = myGetProfileStringDup("BackButtonText");
  410.  
  411.   nNumFields = myGetProfileInt("NumFields", 0);
  412.  
  413.   nRectId = myGetProfileInt("Rect", DEFAULT_RECT);
  414.  
  415.   bBackEnabled = myGetProfileInt("BackEnabled", -1);
  416.   // by ORTIM: 13-August-2002
  417.   bCancelEnabled = myGetProfileInt("CancelEnabled", -1);
  418.   bCancelShow = myGetProfileInt("CancelShow", -1);
  419.  
  420.   bRTL = myGetProfileInt("RTL", 0);
  421.  
  422.   if (nNumFields > 0) {
  423.     // make this twice as large for the worst case that every control is a browse button.
  424.     // the structure is small enough that this won't waste much memory.
  425.     // if the structure gets much larger, we should switch to a linked list.
  426.     pFields = (FieldType *)MALLOC(sizeof(FieldType)*2*nNumFields);
  427.   }
  428.  
  429.   for (nIdx = 0, nCtrlIdx = 0; nCtrlIdx < nNumFields; nCtrlIdx++, nIdx++) {
  430.     // Control types
  431.     static TableEntry TypeTable[] = {
  432.       { "LABEL",       FIELD_LABEL       },
  433.       { "TEXT",        FIELD_TEXT        },
  434.       { "PASSWORD",    FIELD_TEXT        },
  435.       { "LISTBOX",     FIELD_LISTBOX     },
  436.       { "COMBOBOX",    FIELD_COMBOBOX    },
  437.       { "DROPLIST",    FIELD_COMBOBOX    },
  438.       { "FILEREQUEST", FIELD_FILEREQUEST },
  439.       { "DIRREQUEST",  FIELD_DIRREQUEST  },
  440.       { "CHECKBOX",    FIELD_CHECKBOX    },
  441.       { "RADIOBUTTON", FIELD_RADIOBUTTON },
  442.       { "ICON",        FIELD_ICON        },
  443.       { "BITMAP",      FIELD_BITMAP      },
  444.       { "GROUPBOX",    FIELD_GROUPBOX    },
  445. #ifdef IO_ENABLE_LINK
  446.       { "LINK",        FIELD_LINK        },
  447. #else
  448.       { "LINK",        FIELD_LABEL       },
  449. #endif
  450.       { "BUTTON",      FIELD_BUTTON      },
  451.       { NULL,          0                 }
  452.     };
  453.     // Control flags
  454.     static TableEntry FlagTable[] = {
  455.       { "NOTIFY",            LBS_NOTIFY          },
  456.       { "WARN_IF_EXIST",     OFN_OVERWRITEPROMPT },
  457.       { "FILE_HIDEREADONLY", OFN_HIDEREADONLY    },
  458.       { "MULTISELECT",       LBS_MULTIPLESEL     },
  459.       { "READONLY",          FLAG_READONLY       },
  460.       { "RIGHT",             BS_LEFTTEXT         },
  461.       { "PASSWORD",          FLAG_PASSWORD       },
  462.       { "ONLY_NUMBERS",      FLAG_ONLYNUMBERS    },
  463.       { "MULTILINE",         FLAG_MULTILINE      },
  464.       { "NOWORDWRAP",        FLAG_NOWORDWRAP     },
  465.       { "WANTRETURN",        FLAG_WANTRETURN     },
  466.       { "EXTENDEDSELCT",     LBS_EXTENDEDSEL     },
  467.       { "PATH_MUST_EXIST",   OFN_PATHMUSTEXIST   },
  468.       { "FILE_MUST_EXIST",   OFN_FILEMUSTEXIST   },
  469.       { "PROMPT_CREATE",     OFN_CREATEPROMPT    },
  470.       { "DROPLIST",          FLAG_DROPLIST       },
  471.       { "RESIZETOFIT",       FLAG_RESIZETOFIT    },
  472.       { "NOTABSTOP",         WS_TABSTOP          },
  473.       { "GROUP",             WS_GROUP            },
  474.       { "REQ_SAVE",          FLAG_SAVEAS         },
  475.       { "FILE_EXPLORER",     OFN_EXPLORER        },
  476.       { "HSCROLL",           WS_HSCROLL          },
  477.       { "VSCROLL",           WS_VSCROLL          },
  478.       { "DISABLED",          WS_DISABLED         },
  479.       { NULL,                0                   }
  480.     };
  481.     FieldType *pField = pFields + nIdx;
  482.  
  483.     wsprintf(szField, "Field %d", nCtrlIdx + 1);
  484.     pszAppName = szField;
  485.  
  486.     // Get the control type
  487.     myGetProfileString("TYPE");
  488.     pField->nType = LookupToken(TypeTable, szResult);
  489.     if (pField->nType == FIELD_INVALID)
  490.       continue;
  491.  
  492.     // Lookup flags associated with the control type
  493.     pField->nFlags = LookupToken(FlagTable, szResult);
  494.     myGetProfileString("Flags");
  495.     pField->nFlags |= LookupTokens(FlagTable, szResult);
  496.  
  497.     // pszState must not be NULL!
  498.     myGetProfileString("State");
  499.     pField->pszState = strdup(szResult);
  500.  
  501.     // ListBox items list
  502.     {
  503.       int nResult = myGetProfileString("ListItems");
  504.       if (nResult) {
  505.         // add an extra | character to the end to simplify the loop where we add the items.
  506.         pField->pszListItems = (char*)MALLOC(nResult + 2);
  507.         strcpy(pField->pszListItems, szResult);
  508.         pField->pszListItems[nResult] = '|';
  509.         pField->pszListItems[nResult + 1] = '\0';
  510.       }
  511.     }
  512.  
  513.     // Label Text - convert newline
  514.     pField->pszText = myGetProfileStringDup("TEXT");
  515.     if (pField->nType == FIELD_LABEL)
  516.       ConvertNewLines(pField->pszText);
  517.  
  518.     // Dir request - root folder
  519.     pField->pszRoot = myGetProfileStringDup("ROOT");
  520.  
  521.     // ValidateText - convert newline
  522.     pField->pszValidateText = myGetProfileStringDup("ValidateText");
  523.     ConvertNewLines(pField->pszValidateText);
  524.  
  525.     {
  526.       int nResult = GetPrivateProfileString(szField, "Filter", "All Files|*.*", szResult, sizeof(szResult), pszFilename);
  527.       if (nResult) {
  528.         // Convert the filter to the format required by Windows: NULL after each
  529.         // item followed by a terminating NULL
  530.         pField->pszFilter = (char*)MALLOC(nResult + 2);
  531.         strcpy(pField->pszFilter, szResult);
  532.         char *pszPos = pField->pszFilter;
  533.         while (*pszPos)
  534.         {
  535.           if (*pszPos == '|')
  536.             *pszPos++ = 0;
  537.           else
  538.             pszPos = CharNext(pszPos);
  539.         }
  540.       }
  541.     }
  542.  
  543.     pField->rect.left = myGetProfileInt("LEFT", 0);
  544.     pField->rect.top = myGetProfileInt("TOP", 0);
  545.     pField->rect.right = myGetProfileInt("RIGHT", 0);
  546.     pField->rect.bottom = myGetProfileInt("BOTTOM", 0);
  547.     pField->nMinLength = myGetProfileInt("MinLen", 0);
  548.     pField->nMaxLength = myGetProfileInt("MaxLen", 0);
  549.  
  550.     // Text color for LINK control, default is pure blue
  551.     pField->hImage = (HANDLE)myGetProfileInt("TxtColor", RGB(0,0,255));
  552.  
  553.     pField->nControlID = 1200 + nIdx;
  554.     if (pField->nType == FIELD_FILEREQUEST || pField->nType == FIELD_DIRREQUEST)
  555.     {
  556.       FieldType *pNewField = &pFields[nIdx+1];
  557.       pNewField->nControlID = 1200 + nIdx + 1;
  558.       pNewField->nType = FIELD_BROWSEBUTTON;
  559.       pNewField->nFlags = pField->nFlags & (WS_DISABLED | WS_TABSTOP);
  560.       pNewField->pszText = STRDUP(szBrowseButtonCaption); // needed for generic FREE
  561.       pNewField->rect.right  = pField->rect.right;
  562.       pNewField->rect.left   = pNewField->rect.right - BROWSE_WIDTH;
  563.       pNewField->rect.bottom = pField->rect.bottom;
  564.       pNewField->rect.top    = pField->rect.top;
  565.       pField->rect.right = pNewField->rect.left - 3;
  566.       nNumFields++;
  567.       nIdx++;
  568.     }
  569.   }
  570.  
  571.   return nNumFields;
  572. }
  573.  
  574.  
  575. LRESULT WINAPI WMCommandProc(HWND hWnd, UINT id, HWND hwndCtl, UINT codeNotify) {
  576.   switch (codeNotify) {
  577.     case BN_CLICKED:    // The user pressed a button
  578.     case LBN_SELCHANGE: // The user changed the selection in a ListBox control
  579. //  case CBN_SELCHANGE: // The user changed the selection in a DropList control (same value as LBN_SELCHANGE)
  580.     {
  581.       char szBrowsePath[MAX_PATH];
  582.       int nIdx = FindControlIdx(id);
  583.       // Ignore if the dialog is in the process of being created
  584.       if (g_done || nIdx < 0)
  585.         break;
  586.       if (pFields[nIdx].nType == FIELD_BROWSEBUTTON)
  587.         --nIdx;
  588.       FieldType *pField = pFields + nIdx;
  589.       switch (pField->nType) {
  590.         case FIELD_FILEREQUEST: {
  591.           OPENFILENAME ofn={0,};
  592.  
  593.           ofn.lStructSize = sizeof(ofn);
  594.           ofn.hwndOwner = hConfigWindow;
  595.           ofn.lpstrFilter = pField->pszFilter;
  596.           ofn.lpstrFile = szBrowsePath;
  597.           ofn.nMaxFile  = sizeof(szBrowsePath);
  598.           ofn.Flags = pField->nFlags & (OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_CREATEPROMPT | OFN_EXPLORER);
  599.  
  600.           GetWindowText(pField->hwnd, szBrowsePath, sizeof(szBrowsePath));
  601.  
  602.         tryagain:
  603.           if ((pField->nFlags & FLAG_SAVEAS) ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn)) {
  604.             mySetWindowText(pField->hwnd, szBrowsePath);
  605.             break;
  606.           }
  607.           else if (szBrowsePath[0] && CommDlgExtendedError() == FNERR_INVALIDFILENAME) {
  608.             szBrowsePath[0] = '\0';
  609.             goto tryagain;
  610.           }
  611.  
  612.           break;
  613.         }
  614.  
  615.         case FIELD_DIRREQUEST: {
  616.           BROWSEINFO bi;
  617.  
  618.           bi.hwndOwner = hConfigWindow;
  619.           bi.pidlRoot = NULL;
  620.           bi.pszDisplayName = szBrowsePath;
  621.           bi.lpszTitle = pField->pszText;
  622. #ifndef BIF_NEWDIALOGSTYLE
  623. #define BIF_NEWDIALOGSTYLE 0x0040
  624. #endif
  625.           bi.ulFlags = BIF_STATUSTEXT | BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
  626.           bi.lpfn = BrowseCallbackProc;
  627.           bi.lParam = nIdx;
  628.           bi.iImage = 0;
  629.  
  630.           if (pField->pszRoot) {
  631.             LPSHELLFOLDER sf;
  632.             ULONG eaten;
  633.             LPITEMIDLIST root;
  634.             int ccRoot = (lstrlen(pField->pszRoot) * 2) + 2;
  635.             LPWSTR pwszRoot = (LPWSTR) MALLOC(ccRoot);
  636.             MultiByteToWideChar(CP_ACP, 0, pField->pszRoot, -1, pwszRoot, ccRoot);
  637.             SHGetDesktopFolder(&sf);
  638.             sf->ParseDisplayName(hConfigWindow, NULL, pwszRoot, &eaten, &root, NULL);
  639.             bi.pidlRoot = root;
  640.             sf->Release();
  641.             FREE(pwszRoot);
  642.           }
  643. //          CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  644.           LPITEMIDLIST pResult = SHBrowseForFolder(&bi);
  645.           if (!pResult)
  646.             break;
  647.  
  648.           if (SHGetPathFromIDList(pResult, szBrowsePath)) {
  649.             mySetWindowText(pField->hwnd, szBrowsePath);
  650.           }
  651.  
  652.           LPMALLOC pMalloc;
  653.           if (!SHGetMalloc(&pMalloc)) {
  654.             pMalloc->Free(pResult);
  655.           }
  656.  
  657.           break;
  658.         }
  659.  
  660.         case FIELD_LINK:
  661.         case FIELD_BUTTON:
  662.           // Allow the state to be empty - this might be useful in conjunction
  663.           // with the NOTIFY flag
  664.           if (*pField->pszState)
  665.             ShellExecute(hMainWindow, NULL, pField->pszState, NULL, NULL, SW_SHOWDEFAULT);
  666.           break;
  667.       }
  668.  
  669.       if (pField->nFlags & LBS_NOTIFY) {
  670.         // Remember which control was activated then pretend the user clicked Next
  671.         g_NotifyField = nIdx + 1;
  672.         // the next button must be enabled or nsis will ignore WM_COMMAND
  673.         BOOL bWasDisabled = EnableWindow(hNextButton, TRUE);
  674.         FORWARD_WM_COMMAND(hMainWindow, IDOK, hNextButton, BN_CLICKED, mySendMessage);
  675.         if (bWasDisabled)
  676.           EnableWindow(hNextButton, FALSE);
  677.       }
  678.     }
  679.     break;
  680.   }
  681.   return 0;
  682. }
  683.  
  684.  
  685. static void *lpWndProcOld;
  686.  
  687. int g_is_cancel,g_is_back;
  688.  
  689. BOOL CALLBACK ParentWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  690. {
  691.   BOOL bRes;
  692.   if (message == WM_NOTIFY_OUTER_NEXT && wParam == 1)
  693.   {
  694.     // Don't call leave function if fields aren't valid
  695.     if (!g_NotifyField && !ValidateFields())
  696.       return 0;
  697.     // Get the settings ready for the leave function verification
  698.     SaveSettings();
  699.     // Reset the record of activated control
  700.     g_NotifyField = 0;
  701.   }
  702.   bRes = CallWindowProc((long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long))lpWndProcOld,hwnd,message,wParam,lParam);
  703.   if (message == WM_NOTIFY_OUTER_NEXT && !bRes)
  704.   {
  705.     // if leave function didn't abort (bRes != 0 in that case)
  706.     if (wParam == -1)
  707.       g_is_back++;
  708.     if (wParam == NOTIFY_BYE_BYE)
  709.       g_is_cancel++;
  710.     g_done++;
  711.     PostMessage(hConfigWindow,WM_CLOSE,0,0);
  712.   }
  713.   return bRes;
  714. }
  715.  
  716. BOOL CALLBACK cfgDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  717. {
  718.   switch (uMsg)
  719.   {
  720.     HANDLE_MSG(hwndDlg, WM_COMMAND, WMCommandProc);
  721.     case WM_DRAWITEM:
  722.     {
  723.       DRAWITEMSTRUCT* lpdis = (DRAWITEMSTRUCT*)lParam;
  724.       int nIdx = FindControlIdx(lpdis->CtlID);
  725. #ifdef IO_LINK_UNDERLINED
  726.       HFONT OldFont;
  727.       LOGFONT lf;
  728. #endif
  729.  
  730.       if (nIdx < 0)
  731.         break;
  732.       FieldType *pField = pFields + nIdx;
  733.  
  734. #ifdef IO_LINK_UNDERLINED
  735.       GetObject(GetCurrentObject(lpdis->hDC, OBJ_FONT), sizeof(lf), &lf);
  736.       lf.lfUnderline = TRUE;
  737.       OldFont = (HFONT)SelectObject(lpdis->hDC, CreateFontIndirect(&lf));
  738. #endif
  739.  
  740.       // We need lpdis->rcItem later
  741.       RECT rc = lpdis->rcItem;
  742.  
  743.       // Calculate needed size of the control
  744.       DrawText(lpdis->hDC, pField->pszText, -1, &rc, DT_VCENTER | DT_SINGLELINE | DT_CALCRECT);
  745.  
  746.       // Make some more room so the focus rect won't cut letters off
  747.       rc.right = min(rc.right + 2, lpdis->rcItem.right);
  748.  
  749.       // Move rect to right if in RTL mode
  750.       if (bRTL)
  751.       {
  752.         rc.left += lpdis->rcItem.right - rc.right;
  753.         rc.right += lpdis->rcItem.right - rc.right;
  754.       }
  755.  
  756.       if (lpdis->itemAction & ODA_DRAWENTIRE)
  757.       {
  758.         // Get TxtColor unless the user has set another using SetCtlColors
  759.         if (!GetWindowLong(lpdis->hwndItem, GWL_USERDATA))
  760.           SetTextColor(lpdis->hDC, (COLORREF) pField->hImage);
  761.  
  762.         // Draw the text
  763.         DrawText(lpdis->hDC, pField->pszText, -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE | (bRTL ? DT_RTLREADING : 0));
  764.       }
  765.  
  766.       // Draw the focus rect if needed
  767.       if (((lpdis->itemState & ODS_FOCUS) && (lpdis->itemAction & ODA_DRAWENTIRE)) || (lpdis->itemAction & ODA_FOCUS))
  768.       {
  769.         // NB: when not in DRAWENTIRE mode, this will actually toggle the focus
  770.         // rectangle since it's drawn in a XOR way
  771.         DrawFocusRect(lpdis->hDC, &rc);
  772.       }
  773.  
  774.       pField->rect = rc;
  775.  
  776. #ifdef IO_LINK_UNDERLINED
  777.       DeleteObject(SelectObject(lpdis->hDC, OldFont));
  778. #endif
  779.       break;
  780.     }
  781.     case WM_CTLCOLORSTATIC:
  782.     case WM_CTLCOLOREDIT:
  783.     case WM_CTLCOLORDLG:
  784.     case WM_CTLCOLORBTN:
  785.     case WM_CTLCOLORLISTBOX:
  786.       // let the NSIS window handle colors, it knows best
  787.       return mySendMessage(hMainWindow, uMsg, wParam, lParam);
  788.   }
  789.   return 0;
  790. }
  791.  
  792. #ifdef IO_ENABLE_LINK
  793.  
  794. #ifndef IDC_HAND
  795. #define IDC_HAND MAKEINTRESOURCE(32649)
  796. #endif
  797.  
  798. #ifndef BS_TYPEMASK
  799. #define BS_TYPEMASK 0x0000000FL
  800. #endif
  801.  
  802. // pFields[nIdx].nParentIdx is used to store original windowproc
  803. int WINAPI StaticLINKWindowProc(HWND hWin, UINT uMsg, LPARAM wParam, WPARAM lParam)
  804. {
  805.   int StaticField = FindControlIdx(GetDlgCtrlID(hWin));
  806.   if (StaticField < 0)
  807.     return 0;
  808.   FieldType *pField = pFields + StaticField;
  809.  
  810.   switch(uMsg)
  811.   {
  812.     case WM_GETDLGCODE:
  813.       // Pretend we are a normal button/default button as appropriate
  814.       return DLGC_BUTTON | ((pField->nFlags & FLAG_WANTRETURN) ? DLGC_DEFPUSHBUTTON : DLGC_UNDEFPUSHBUTTON);
  815.  
  816.     case BM_SETSTYLE:
  817.       // Detect when we are becoming the default button but don't lose the owner-draw style
  818.       if ((wParam & BS_TYPEMASK) == BS_DEFPUSHBUTTON)
  819.         pField->nFlags |= FLAG_WANTRETURN;  // Hijack this flag to indicate default button status
  820.       else
  821.         pField->nFlags &= ~FLAG_WANTRETURN;
  822.       wParam = (wParam & ~BS_TYPEMASK) | BS_OWNERDRAW;
  823.       break;
  824.  
  825.     case WM_NCHITTEST:
  826.     {
  827.       POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
  828.       MapWindowPoints(0, hWin, &pt, 1);
  829.       if (PtInRect(&pField->rect, pt))
  830.         return HTCLIENT;
  831.       else
  832.         return HTNOWHERE;
  833.     }
  834.  
  835.     case WM_SETCURSOR:
  836.     {
  837.       if ((HWND)wParam == hWin && LOWORD(lParam) == HTCLIENT)
  838.       {
  839.         HCURSOR hCur = LoadCursor(NULL, IDC_HAND);
  840.         if (hCur)
  841.         {
  842.           SetCursor(hCur);
  843.           return 1; // halt further processing
  844.         }
  845.       }
  846.     }
  847.   }
  848.   return CallWindowProc((WNDPROC)pField->nParentIdx, hWin, uMsg, wParam, lParam);
  849. }
  850. #endif
  851.  
  852. int old_cancel_visible;
  853.  
  854. int WINAPI createCfgDlg()
  855. {
  856.   g_is_back=0;
  857.   g_is_cancel=0;
  858.  
  859.   HWND mainwnd = hMainWindow;
  860.   if (!mainwnd)
  861.   {
  862.     popstring(NULL);
  863.     pushstring("error finding mainwnd");
  864.     return 1; // cannot be used in silent mode unfortunately.
  865.   }
  866.  
  867.   if (!g_stacktop || !*g_stacktop || !(pszFilename = (*g_stacktop)->text) || !pszFilename[0] || !ReadSettings())
  868.   {
  869.     popstring(NULL);
  870.     pushstring("error finding config");
  871.     return 1;
  872.   }
  873.  
  874.   HWND childwnd=GetDlgItem(mainwnd,nRectId);
  875.   if (!childwnd)
  876.   {
  877.     popstring(NULL);
  878.     pushstring("error finding childwnd");
  879.     return 1;
  880.   }
  881.  
  882.   hCancelButton = GetDlgItem(mainwnd,IDCANCEL);
  883.   hNextButton = GetDlgItem(mainwnd,IDOK);
  884.   hBackButton = GetDlgItem(mainwnd,3);
  885.  
  886.   mySetWindowText(hCancelButton,pszCancelButtonText);
  887.   mySetWindowText(hNextButton,pszNextButtonText);
  888.   mySetWindowText(hBackButton,pszBackButtonText);
  889.  
  890.   if (bBackEnabled!=-1) EnableWindow(hBackButton,bBackEnabled);
  891.   if (bCancelEnabled!=-1) EnableWindow(hCancelButton,bCancelEnabled);
  892.   if (bCancelShow!=-1) old_cancel_visible=ShowWindow(hCancelButton,bCancelShow?SW_SHOWNA:SW_HIDE);
  893.  
  894.   HFONT hFont = (HFONT)mySendMessage(mainwnd, WM_GETFONT, 0, 0);
  895.  
  896.   // Prevent WM_COMMANDs from being processed while we are building
  897.   g_done = 1;
  898.  
  899.   RECT dialog_r;
  900.   int mainWndWidth, mainWndHeight;
  901.   hConfigWindow=CreateDialog(m_hInstance,MAKEINTRESOURCE(IDD_DIALOG1),mainwnd,cfgDlgProc);
  902.   if (hConfigWindow)
  903.   {
  904.     GetWindowRect(childwnd,&dialog_r);
  905.     MapWindowPoints(0, mainwnd, (LPPOINT) &dialog_r, 2);
  906.     mainWndWidth = dialog_r.right - dialog_r.left;
  907.     mainWndHeight = dialog_r.bottom - dialog_r.top;
  908.     SetWindowPos(
  909.       hConfigWindow,
  910.       0,
  911.       dialog_r.left,
  912.       dialog_r.top,
  913.       mainWndWidth,
  914.       mainWndHeight,
  915.       SWP_NOZORDER|SWP_NOACTIVATE
  916.     );
  917.     // Sets the font of IO window to be the same as the main window
  918.     mySendMessage(hConfigWindow, WM_SETFONT, (WPARAM)hFont, TRUE);
  919.   }
  920.   else
  921.   {
  922.     popstring(NULL);
  923.     pushstring("error creating dialog");
  924.     return 1;
  925.   }
  926.  
  927.   // Init dialog unit conversion
  928.  
  929.   HDC memDC = CreateCompatibleDC(GetDC(hConfigWindow));
  930.   SelectObject(memDC, hFont);
  931.  
  932.   TEXTMETRIC tm;
  933.   GetTextMetrics(memDC, &tm);
  934.   int baseUnitY = tm.tmHeight;
  935.  
  936.   SIZE size;
  937.   GetTextExtentPoint32(memDC,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size);
  938.   int baseUnitX = (size.cx / 26 + 1) / 2;
  939.  
  940.   DeleteDC(memDC);
  941.  
  942.   BOOL fFocused = FALSE;
  943.  
  944. #define DEFAULT_STYLES (WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
  945.  
  946.   for (int nIdx = 0; nIdx < nNumFields; nIdx++) {
  947.     static struct {
  948.       char* pszClass;
  949.       DWORD dwStyle;
  950.       DWORD dwRTLStyle;
  951.       DWORD dwExStyle;
  952.       DWORD dwRTLExStyle;
  953.     } ClassTable[] = {
  954.       { "STATIC",       // FIELD_LABEL
  955.         DEFAULT_STYLES,
  956.         DEFAULT_STYLES | SS_RIGHT,
  957.         WS_EX_TRANSPARENT,
  958.         WS_EX_TRANSPARENT | WS_EX_RTLREADING },
  959.       { "STATIC",       // FIELD_ICON
  960.         DEFAULT_STYLES | SS_ICON,
  961.         DEFAULT_STYLES | SS_ICON,
  962.         0,
  963.         WS_EX_RTLREADING },
  964.       { "STATIC",       // FIELD_BITMAP
  965.         DEFAULT_STYLES | SS_BITMAP | SS_CENTERIMAGE,
  966.         DEFAULT_STYLES | SS_BITMAP | SS_CENTERIMAGE,
  967.         0,
  968.         WS_EX_RTLREADING },
  969.       { "BUTTON",       // FIELD_BROWSEBUTTON
  970.         DEFAULT_STYLES | WS_TABSTOP,
  971.         DEFAULT_STYLES | WS_TABSTOP,
  972.         0,
  973.         WS_EX_RTLREADING },
  974.       { "BUTTON",       // FIELD_LINK
  975.         DEFAULT_STYLES | WS_TABSTOP | BS_OWNERDRAW,
  976.         DEFAULT_STYLES | WS_TABSTOP | BS_OWNERDRAW | BS_RIGHT,
  977.         0,
  978.         WS_EX_RTLREADING },
  979.       { "BUTTON",       // FIELD_BUTTON
  980.         DEFAULT_STYLES | WS_TABSTOP,
  981.         DEFAULT_STYLES | WS_TABSTOP,
  982.         0,
  983.         WS_EX_RTLREADING },
  984.       { "BUTTON",       // FIELD_GROUPBOX
  985.         DEFAULT_STYLES | BS_GROUPBOX,
  986.         DEFAULT_STYLES | BS_GROUPBOX | BS_RIGHT,
  987.         WS_EX_TRANSPARENT,
  988.         WS_EX_TRANSPARENT | WS_EX_RTLREADING },
  989.       { "BUTTON",       // FIELD_CHECKBOX
  990.         DEFAULT_STYLES | WS_TABSTOP | BS_TEXT | BS_VCENTER | BS_AUTOCHECKBOX | BS_MULTILINE,
  991.         DEFAULT_STYLES | WS_TABSTOP | BS_TEXT | BS_VCENTER | BS_AUTOCHECKBOX | BS_MULTILINE | BS_RIGHT | BS_LEFTTEXT,
  992.         0,
  993.         WS_EX_RTLREADING },
  994.       { "BUTTON",       // FIELD_RADIOBUTTON
  995.         DEFAULT_STYLES | WS_TABSTOP | BS_TEXT | BS_VCENTER | BS_AUTORADIOBUTTON | BS_MULTILINE,
  996.         DEFAULT_STYLES | WS_TABSTOP | BS_TEXT | BS_VCENTER | BS_AUTORADIOBUTTON | BS_MULTILINE | BS_RIGHT | BS_LEFTTEXT,
  997.         0,
  998.         WS_EX_RTLREADING },
  999.       { "EDIT",         // FIELD_TEXT
  1000.         DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL,
  1001.         DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL | ES_RIGHT,
  1002.         WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
  1003.         WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_RTLREADING },
  1004.       { "EDIT",         // FIELD_FILEREQUEST
  1005.         DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL,
  1006.         DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL | ES_RIGHT,
  1007.         WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
  1008.         WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_RTLREADING },
  1009.       { "EDIT",         // FIELD_DIRREQUEST
  1010.         DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL,
  1011.         DEFAULT_STYLES | WS_TABSTOP | ES_AUTOHSCROLL | ES_RIGHT,
  1012.         WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
  1013.         WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_RTLREADING },
  1014.       { "COMBOBOX",     // FIELD_COMBOBOX
  1015.         DEFAULT_STYLES | WS_TABSTOP | WS_VSCROLL | WS_CLIPCHILDREN | CBS_AUTOHSCROLL | CBS_HASSTRINGS,
  1016.         DEFAULT_STYLES | WS_TABSTOP | WS_VSCROLL | WS_CLIPCHILDREN | CBS_AUTOHSCROLL | CBS_HASSTRINGS,
  1017.         WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
  1018.         WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_RIGHT | WS_EX_RTLREADING },
  1019.       { "LISTBOX",      // FIELD_LISTBOX
  1020.         DEFAULT_STYLES | WS_TABSTOP | WS_VSCROLL | LBS_DISABLENOSCROLL | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT,
  1021.         DEFAULT_STYLES | WS_TABSTOP | WS_VSCROLL | LBS_DISABLENOSCROLL | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT,
  1022.         WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
  1023.         WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_RIGHT | WS_EX_RTLREADING }
  1024.     };
  1025.  
  1026.     FieldType *pField = pFields + nIdx;
  1027.  
  1028. #undef DEFAULT_STYLES
  1029.  
  1030.     if (pField->nType < 1 || pField->nType > (sizeof(ClassTable) / sizeof(ClassTable[0])))
  1031.       continue;
  1032.  
  1033.     DWORD dwStyle, dwExStyle;
  1034.     if (bRTL) {
  1035.       dwStyle = ClassTable[pField->nType - 1].dwRTLStyle;
  1036.       dwExStyle = ClassTable[pField->nType - 1].dwRTLExStyle;
  1037.     }
  1038.     else {
  1039.       dwStyle = ClassTable[pField->nType - 1].dwStyle;
  1040.       dwExStyle = ClassTable[pField->nType - 1].dwExStyle;
  1041.     }
  1042.  
  1043.     // Convert from dialog units
  1044.  
  1045.     RECT rect;
  1046.  
  1047.     rect.left = MulDiv(pField->rect.left, baseUnitX, 4);
  1048.     rect.right = MulDiv(pField->rect.right, baseUnitX, 4);
  1049.     rect.top = MulDiv(pField->rect.top, baseUnitY, 8);
  1050.     rect.bottom = MulDiv(pField->rect.bottom, baseUnitY, 8);
  1051.  
  1052.     if (pField->rect.left < 0)
  1053.       rect.left += mainWndWidth;
  1054.     if (pField->rect.right < 0)
  1055.       rect.right += mainWndWidth;
  1056.     if (pField->rect.top < 0)
  1057.       rect.top += mainWndHeight;
  1058.     if (pField->rect.bottom < 0)
  1059.       rect.bottom += mainWndHeight;
  1060.  
  1061.     if (bRTL) {
  1062.       int right = rect.right;
  1063.       rect.right = mainWndWidth - rect.left;
  1064.       rect.left = mainWndWidth - right;
  1065.     }
  1066.  
  1067.     char *title = pField->pszText;
  1068.     switch (pField->nType) {
  1069.       case FIELD_ICON:
  1070.       case FIELD_BITMAP:
  1071.         title = NULL; // otherwise it is treated as the name of a resource
  1072.         break;
  1073.       case FIELD_CHECKBOX:
  1074.       case FIELD_RADIOBUTTON:
  1075.         dwStyle ^= pField->nFlags & BS_LEFTTEXT;
  1076.         break;
  1077.       case FIELD_TEXT:
  1078.       case FIELD_FILEREQUEST:
  1079.       case FIELD_DIRREQUEST:
  1080.         if (pField->nFlags & FLAG_PASSWORD)
  1081.           dwStyle |= ES_PASSWORD;
  1082.         if (pField->nFlags & FLAG_ONLYNUMBERS)
  1083.           dwStyle |= ES_NUMBER;
  1084.         if (pField->nFlags & FLAG_WANTRETURN)
  1085.           dwStyle |= ES_WANTRETURN;
  1086.         if (pField->nFlags & FLAG_READONLY)
  1087.           dwStyle |= ES_READONLY;
  1088.         title = pField->pszState;
  1089.         if (pField->nFlags & FLAG_MULTILINE)
  1090.         {
  1091.           dwStyle |= ES_MULTILINE | ES_AUTOVSCROLL;
  1092.           // Enable word-wrap unless we have a horizontal scroll bar
  1093.           // or it has been explicitly disallowed
  1094.           if (!(pField->nFlags & (WS_HSCROLL | FLAG_NOWORDWRAP)))
  1095.             dwStyle &= ~ES_AUTOHSCROLL;
  1096.           ConvertNewLines(pField->pszState);
  1097.           // If multiline-readonly then hold the text back until after the
  1098.           // initial focus has been set. This is so the text is not initially
  1099.           // selected - useful for License Page look-a-likes.
  1100.           if (pField->nFlags & FLAG_READONLY)
  1101.             title = NULL;
  1102.         }
  1103.         break;
  1104.       case FIELD_COMBOBOX:
  1105.         dwStyle |= (pField->nFlags & FLAG_DROPLIST) ? CBS_DROPDOWNLIST : CBS_DROPDOWN;
  1106.         title = pField->pszState;
  1107.         break;
  1108.       case FIELD_LISTBOX:
  1109.         dwStyle |= pField->nFlags & (LBS_NOTIFY | LBS_MULTIPLESEL | LBS_EXTENDEDSEL);
  1110.         break;
  1111.     }
  1112.  
  1113.     dwStyle |= pField->nFlags & (WS_GROUP | WS_HSCROLL | WS_VSCROLL | WS_DISABLED);
  1114.     if (pField->nFlags & WS_TABSTOP) dwStyle &= ~WS_TABSTOP;
  1115.  
  1116.     HWND hwCtrl = pField->hwnd = CreateWindowEx(
  1117.       dwExStyle,
  1118.       ClassTable[pField->nType - 1].pszClass,
  1119.       title,
  1120.       dwStyle,
  1121.       rect.left,
  1122.       rect.top,
  1123.       rect.right - rect.left,
  1124.       rect.bottom - rect.top,
  1125.       hConfigWindow,
  1126.       (HMENU)pField->nControlID,
  1127.       m_hInstance,
  1128.       NULL
  1129.     );
  1130.  
  1131.     if (hwCtrl) {
  1132.       // Sets the font of IO window to be the same as the main window
  1133.       mySendMessage(hwCtrl, WM_SETFONT, (WPARAM)hFont, TRUE);
  1134.       // make sure we created the window, then set additional attributes
  1135.       switch (pField->nType) {
  1136.         case FIELD_TEXT:
  1137.         case FIELD_FILEREQUEST:
  1138.         case FIELD_DIRREQUEST:
  1139.           mySendMessage(hwCtrl, EM_LIMITTEXT, (WPARAM)pField->nMaxLength, (LPARAM)0);
  1140.           break;
  1141.  
  1142.         case FIELD_CHECKBOX:
  1143.         case FIELD_RADIOBUTTON:
  1144.           if (pField->pszState[0] == '1')
  1145.             mySendMessage(hwCtrl, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
  1146.           break;
  1147.  
  1148.         case FIELD_COMBOBOX:
  1149.         case FIELD_LISTBOX:
  1150.           // if this is a listbox or combobox, we need to add the list items.
  1151.           if (pField->pszListItems) {
  1152.             UINT nAddMsg, nFindMsg, nSetSelMsg;
  1153.             if (pField->nType == FIELD_COMBOBOX) {
  1154.               nAddMsg = CB_ADDSTRING;
  1155.               nFindMsg = CB_FINDSTRINGEXACT;
  1156.               nSetSelMsg = CB_SETCURSEL;
  1157.             }
  1158.             else {
  1159.               nAddMsg = LB_ADDSTRING;
  1160.               nFindMsg = LB_FINDSTRINGEXACT;
  1161.               nSetSelMsg = LB_SETCURSEL;
  1162.             }
  1163.             char *pszStart, *pszEnd, *pszList;
  1164.             pszStart = pszEnd = pszList = STRDUP(pField->pszListItems);
  1165.             // pszListItems has a trailing pipe
  1166.             while (*pszEnd) {
  1167.               if (*pszEnd == '|') {
  1168.                 *pszEnd = '\0';
  1169.                 if (*pszStart)
  1170.                   mySendMessage(hwCtrl, nAddMsg, 0, (LPARAM) pszStart);
  1171.                 pszStart = ++pszEnd;
  1172.               }
  1173.               else
  1174.                 pszEnd = CharNext(pszEnd);
  1175.             }
  1176.             FREE(pszList);
  1177.             if (pField->pszState) {
  1178.               if (pField->nFlags & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && nFindMsg == LB_FINDSTRINGEXACT) {
  1179.                 mySendMessage(hwCtrl, LB_SETSEL, FALSE, -1);
  1180.                 pszStart = pszEnd = pField->pszState;
  1181.                 for (;;) {
  1182.                   char c = *pszEnd;
  1183.                   if (c == '|' || c == '\0') {
  1184.                     *pszEnd = '\0';
  1185.                     if (*pszStart)
  1186.                     {
  1187.                       int nItem = mySendMessage(hwCtrl, LB_FINDSTRINGEXACT, -1, (LPARAM)pszStart);
  1188.                       if (nItem != LB_ERR)
  1189.                         mySendMessage(hwCtrl, LB_SETSEL, TRUE, nItem);
  1190.                     }
  1191.                     if (!c)
  1192.                       break;
  1193.                     pszStart = ++pszEnd;
  1194.                   }
  1195.                   else
  1196.                     pszEnd = CharNext(pszEnd);
  1197.                 }
  1198.               }
  1199.               else {
  1200.                 int nItem = mySendMessage(hwCtrl, nFindMsg, -1, (LPARAM)pField->pszState);
  1201.                 if (nItem != CB_ERR) { // CB_ERR == LB_ERR == -1
  1202.                   mySendMessage(hwCtrl, nSetSelMsg, nItem, 0);
  1203.                 }
  1204.               }
  1205.             }
  1206.           }
  1207.           break;
  1208.  
  1209.         case FIELD_ICON:
  1210.         case FIELD_BITMAP:
  1211.         {
  1212.           WPARAM nImageType = pField->nType == FIELD_BITMAP ? IMAGE_BITMAP : IMAGE_ICON;
  1213.           LPARAM nImage = 0;
  1214.           if (pField->pszText) {
  1215.             pField->hImage = LoadImage(
  1216.               m_hInstance,
  1217.               pField->pszText,
  1218.               nImageType,
  1219.               (pField->nFlags & FLAG_RESIZETOFIT)
  1220.                 ? (rect.right - rect.left)
  1221.                 : 0,
  1222.               (pField->nFlags & FLAG_RESIZETOFIT)
  1223.                 ? (rect.bottom - rect.top)
  1224.                 : 0,
  1225.               LR_LOADFROMFILE
  1226.             );
  1227.             nImage = (LPARAM)pField->hImage;
  1228.           }
  1229.           else
  1230.             nImage = (LPARAM)LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(103));
  1231.           mySendMessage(
  1232.             hwCtrl,
  1233.             STM_SETIMAGE,
  1234.             nImageType,
  1235.             nImage
  1236.           );
  1237.           break;
  1238.         }
  1239.  
  1240. #ifdef IO_ENABLE_LINK
  1241.         case FIELD_LINK:
  1242.           pField->nParentIdx = SetWindowLong(hwCtrl, GWL_WNDPROC, (long)StaticLINKWindowProc);
  1243.           break;
  1244. #endif
  1245.       }
  1246.  
  1247.       // Set initial focus to the first appropriate field
  1248.       if (!fFocused && (dwStyle & (WS_TABSTOP | WS_DISABLED)) == WS_TABSTOP && pField->nType >= FIELD_SETFOCUS) {
  1249.         fFocused = TRUE;
  1250.         mySetFocus(hwCtrl);
  1251.       }
  1252.  
  1253.       // If multiline-readonly then hold the text back until after the
  1254.       // initial focus has been set. This is so the text is not initially
  1255.       // selected - useful for License Page look-a-likes.
  1256.       if ((pField->nFlags & (FLAG_MULTILINE | FLAG_READONLY)) == (FLAG_MULTILINE | FLAG_READONLY))
  1257.         mySetWindowText(hwCtrl, pField->pszState);
  1258.     }
  1259.   }
  1260.  
  1261.   if (!fFocused)
  1262.     mySetFocus(hNextButton);
  1263.  
  1264.   mySetWindowText(mainwnd,pszTitle);
  1265.   pFilenameStackEntry = *g_stacktop;
  1266.   *g_stacktop = (*g_stacktop)->next;
  1267.   static char tmp[32];
  1268.   wsprintf(tmp,"%d",hConfigWindow);
  1269.   pushstring(tmp);
  1270.   return 0;
  1271. }
  1272.  
  1273. void WINAPI showCfgDlg()
  1274. {
  1275.   lpWndProcOld = (void *) SetWindowLong(hMainWindow,DWL_DLGPROC,(long)ParentWndProc);
  1276.  
  1277.   // Tell NSIS to remove old inner dialog and pass handle of the new inner dialog
  1278.   mySendMessage(hMainWindow, WM_NOTIFY_CUSTOM_READY, (WPARAM)hConfigWindow, 0);
  1279.   ShowWindow(hConfigWindow, SW_SHOWNA);
  1280.  
  1281.   g_done = g_NotifyField = 0;
  1282.  
  1283.   while (!g_done) {
  1284.     MSG msg;
  1285.     int nResult = GetMessage(&msg, NULL, 0, 0);
  1286.     if (!IsDialogMessage(hConfigWindow,&msg) && !IsDialogMessage(hMainWindow,&msg) && !TranslateMessage(&msg))
  1287.       DispatchMessage(&msg);
  1288.   }
  1289.  
  1290.   // we don't save settings on cancel since that means your installer will likely
  1291.   // quit soon, which means the ini might get flushed late and cause crap. :) anwyay.
  1292.   if (!g_is_cancel) SaveSettings();
  1293.  
  1294.   SetWindowLong(hMainWindow,DWL_DLGPROC,(long)lpWndProcOld);
  1295.   DestroyWindow(hConfigWindow);
  1296.  
  1297.   // by ORTIM: 13-August-2002
  1298.   if (bCancelShow!=-1) ShowWindow(hCancelButton,old_cancel_visible?SW_SHOWNA:SW_HIDE);
  1299.  
  1300.   FREE(pFilenameStackEntry);
  1301.   FREE(pszTitle);
  1302.   FREE(pszCancelButtonText);
  1303.   FREE(pszNextButtonText);
  1304.   FREE(pszBackButtonText);
  1305.  
  1306.   int i = nNumFields;
  1307.   while (i--) {
  1308.     FieldType *pField = pFields + i;
  1309.     
  1310.     int j = FIELD_BUFFERS;
  1311.     while (j--)
  1312.       FREE(((char **) pField)[j]);
  1313.  
  1314.     if (pField->nType == FIELD_BITMAP) {
  1315.       DeleteObject(pField->hImage);
  1316.     }
  1317.     if (pField->nType == FIELD_ICON) {
  1318.       DestroyIcon((HICON)pField->hImage);
  1319.     }
  1320.   }
  1321.   FREE(pFields);
  1322.  
  1323.   pushstring(g_is_cancel?"cancel":g_is_back?"back":"success");
  1324. }
  1325.  
  1326. int initCalled;
  1327.  
  1328. extern "C" void __declspec(dllexport) dialog(HWND hwndParent, int string_size,
  1329.                                       char *variables, stack_t **stacktop)
  1330. {
  1331.   hMainWindow=hwndParent;
  1332.   EXDLL_INIT();
  1333.   if (initCalled) {
  1334.     pushstring("error");
  1335.     return;
  1336.   }
  1337.   if (createCfgDlg()) {
  1338.     return;
  1339.   }
  1340.   popstring(NULL);
  1341.   showCfgDlg();
  1342. }
  1343.  
  1344. extern "C" void __declspec(dllexport) initDialog(HWND hwndParent, int string_size,
  1345.                                       char *variables, stack_t **stacktop)
  1346. {
  1347.   hMainWindow=hwndParent;
  1348.   EXDLL_INIT();
  1349.   if (initCalled) {
  1350.     pushstring("error");
  1351.     return;
  1352.   }
  1353.   initCalled++;
  1354.   createCfgDlg();
  1355. }
  1356.  
  1357. extern "C" void __declspec(dllexport) show(HWND hwndParent, int string_size,
  1358.                                       char *variables, stack_t **stacktop)
  1359. {
  1360.   EXDLL_INIT();
  1361.   if (!initCalled) {
  1362.     pushstring("error");
  1363.     return;
  1364.   }
  1365.   initCalled--;
  1366.   showCfgDlg();
  1367. }
  1368.  
  1369. extern "C" BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
  1370. {
  1371.   m_hInstance=(HINSTANCE) hInst;
  1372.   return TRUE;
  1373. }
  1374.  
  1375.  
  1376. int WINAPI LookupToken(TableEntry* psTable_, char* pszToken_)
  1377. {
  1378.   for (int i = 0; psTable_[i].pszName; i++)
  1379.     if (!stricmp(pszToken_, psTable_[i].pszName))
  1380.       return psTable_[i].nValue;
  1381.   return 0;
  1382. }
  1383.  
  1384. int WINAPI LookupTokens(TableEntry* psTable_, char* pszTokens_)
  1385. {
  1386.   int n = 0;
  1387.   char *pszStart = pszTokens_;
  1388.   char *pszEnd = pszTokens_;
  1389.   for (;;) {
  1390.     char c = *pszEnd;
  1391.     if (c == '|' || c == '\0') {
  1392.       *pszEnd = '\0';
  1393.       n |= LookupToken(psTable_, pszStart);
  1394.       *pszEnd = c;
  1395.       if (!c)
  1396.         break;
  1397.       pszStart = ++pszEnd;
  1398.     }
  1399.     else
  1400.       pszEnd = CharNext(pszEnd);
  1401.   }
  1402.   return n;
  1403. }
  1404.  
  1405. void WINAPI ConvertNewLines(char *str) {
  1406.   char *p1, *p2, *p3;
  1407.  
  1408.   if (!str)
  1409.     return;
  1410.  
  1411.   p1 = p2 = str;
  1412.  
  1413.   while (*p1)
  1414.   {
  1415.     switch (*(LPWORD)p1)
  1416.     {
  1417.     case CHAR2_TO_WORD('\\', 't'):
  1418.       *p2 = '\t';
  1419.       p1 += 2;
  1420.       p2++;
  1421.       break;
  1422.     case CHAR2_TO_WORD('\\', 'n'):
  1423.       *p2 = '\n';
  1424.       p1 += 2;
  1425.       p2++;
  1426.       break;
  1427.     case CHAR2_TO_WORD('\\', 'r'):
  1428.       *p2 = '\r';
  1429.       p1 += 2;
  1430.       p2++;
  1431.       break;
  1432.     case CHAR2_TO_WORD('\\', '\\'):
  1433.       *p2 = '\\';
  1434.       p1 += 2;
  1435.       p2++;
  1436.       break;
  1437.     default:
  1438.       p3 = CharNext(p1);
  1439.       while (p1 < p3)
  1440.         *p2++ = *p1++;
  1441.       break;
  1442.     }
  1443.   }
  1444.  
  1445.   *p2 = 0;
  1446. }
  1447.