257fcd2d0d491b264ff573903bb103842a02c22d
[xboard.git] / winboard / wsettings.c
1 /*\r
2  * Engine-settings dialog. The complexity come from an attempt to present the engine-defined options\r
3  * in a nicey formatted layout. To this end we first run a back-end pre-formatter, which will distribute\r
4  * the controls over two columns (the minimum required, as some are double width). It also takes care of \r
5  * grouping options that start with the same word (mainly for "Polyglot ..." options). It assigns relative\r
6  * suitability to break points between lines, and in the end decides if and where to break up the list\r
7  * for display in multiple (2*N) columns.\r
8  * The thus obtained list representing the topology of the layout is then passed to a front-end routine\r
9  * that generates the actual dialog box from it.\r
10  */\r
11 \r
12 //#include "config.h"\r
13 #include "config.h"\r
14 \r
15 #include <stdio.h>\r
16 #include <string.h>\r
17 #include "common.h"\r
18 #include "backend.h"\r
19 #include "backendz.h"\r
20 \r
21 int layoutList[2*MAX_OPTIONS];\r
22 int checkList[2*MAX_OPTIONS];\r
23 int comboList[2*MAX_OPTIONS];\r
24 int buttonList[2*MAX_OPTIONS];\r
25 int boxList[2*MAX_OPTIONS];\r
26 int groupNameList[2*MAX_OPTIONS];\r
27 int breaks[MAX_OPTIONS];\r
28 int checks, combos, buttons, layout, groups;\r
29 \r
30 void\r
31 PrintOpt(int i, int right, ChessProgramState *cps)\r
32 {\r
33     if(i<0) {\r
34         if(!right) fprintf(debugFP, "%30s", "");\r
35     } else {\r
36         Option opt = cps->option[i];\r
37         switch(opt.type) {\r
38             case Slider:\r
39             case Spin:\r
40                 fprintf(debugFP, "%20.20s [    +/-]", opt.name);\r
41                 break;\r
42             case TextBox:\r
43             case FileName:\r
44             case PathName:\r
45                 fprintf(debugFP, "%20.20s [______________________________________]", opt.name);\r
46                 break;\r
47             case CheckBox:\r
48                 fprintf(debugFP, "[x] %-26.25s", opt.name);\r
49                 break;\r
50             case ComboBox:\r
51                 fprintf(debugFP, "%20.20s [ COMBO ]", opt.name);\r
52                 break;\r
53             case Button:\r
54             case SaveButton:\r
55             case ResetButton:\r
56                 fprintf(debugFP, "[ %26.26s ]", opt.name);\r
57             case Message:\r
58                 break;\r
59         }\r
60     }\r
61     fprintf(debugFP, right ? "\n" : " ");\r
62 }\r
63 \r
64 void\r
65 CreateOptionDialogTest(int *list, int nr, ChessProgramState *cps)\r
66 {\r
67     int line;\r
68 \r
69     for(line = 0; line < nr; line+=2) {\r
70         PrintOpt(list[line+1], 0, cps);\r
71         PrintOpt(list[line], 1, cps);\r
72     }\r
73 }\r
74 \r
75 void\r
76 LayoutOptions(int firstOption, int endOption, char *groupName, Option *optionList)\r
77 {\r
78     int i, b = strlen(groupName), stop, prefix, right, nextOption, firstButton = buttons;\r
79     Control lastType, nextType;\r
80 \r
81     nextOption = firstOption;\r
82     while(nextOption < endOption) {\r
83         checks = combos = 0; stop = 0;\r
84         lastType = Button; // kludge to make sure leading Spin will not be prefixed\r
85         // first separate out buttons for later treatment, and collect consecutive checks and combos\r
86         while(nextOption < endOption && !stop) {\r
87             switch(nextType = optionList[nextOption].type) {\r
88                 case CheckBox: checkList[checks++] = nextOption; lastType = CheckBox; break;\r
89                 case ComboBox: comboList[combos++] = nextOption; lastType = ComboBox; break;\r
90                 case ResetButton:\r
91                 case SaveButton:\r
92                 case Button:  buttonList[buttons++] = nextOption; lastType = Button; break;\r
93                 case TextBox:\r
94                 case FileName:\r
95                 case PathName:\r
96                 case Slider:\r
97                 case Spin: stop++;\r
98                 case Message: ; // cannot happen\r
99             }\r
100             nextOption++;\r
101         }\r
102         // We now must be at the end, or looking at a spin or textbox (in nextType)\r
103         if(!stop) \r
104             nextType = Button; // kudge to flush remaining checks and combos undistorted\r
105         // Take a new line if a spin follows combos or checks, or when we encounter a textbox\r
106         if((combos+checks || nextType == TextBox) && layout&1) {\r
107             layoutList[layout++] = -1;\r
108         }\r
109         // The last check or combo before a spin will be put on the same line as that spin (prefix)\r
110         // and will thus not be grouped with other checks and combos\r
111         prefix = -1;\r
112         if(nextType == Spin && lastType != Button) {\r
113             if(lastType == CheckBox) prefix = checkList[--checks]; else\r
114             if(lastType == ComboBox) prefix = comboList[--combos];\r
115         }\r
116         // if a combo is followed by a textbox, it must stay at the end of the combo/checks list to appear\r
117         // immediately above the textbox, so treat it as check. (A check would automatically be and remain there.)\r
118         if(nextType == TextBox && lastType == ComboBox)\r
119             checkList[checks++] = comboList[--combos];\r
120         // Now append the checks behind the (remaining) combos to treat them as one group\r
121         for(i=0; i< checks; i++) \r
122             comboList[combos++] = checkList[i];\r
123         // emit the consecutive checks and combos in two columns\r
124         right = combos/2; // rounded down if odd!\r
125         for(i=0; i<right; i++) {\r
126             breaks[layout/2] = 2;\r
127             layoutList[layout++] = comboList[i];\r
128             layoutList[layout++] = comboList[i + right];\r
129         }\r
130         // An odd check or combo (which could belong to following textBox) will be put in the left column\r
131         // If there was an even number of checks and combos the last one will automatically be in that position\r
132         if(combos&1) {\r
133             layoutList[layout++] = -1;\r
134             layoutList[layout++] = comboList[2*right];\r
135         }\r
136         if(nextType == TextBox) {\r
137             // A textBox is double width, so must be left-adjusted, and the right column remains empty\r
138             breaks[layout/2] = lastType == Button ? 0 : 100;\r
139             layoutList[layout++] = -1;\r
140             layoutList[layout++] = nextOption - 1;\r
141         } else if(nextType == Spin) {\r
142             // A spin will go in the next available position (right to left!). If it had to be prefixed with\r
143             // a check or combo, this next position must be to the right, and the prefix goes left to it.\r
144             layoutList[layout++] = nextOption - 1;\r
145             if(prefix >= 0) layoutList[layout++] = prefix;\r
146         }\r
147     }\r
148     // take a new line if needed\r
149     if(layout&1) layoutList[layout++] = -1;\r
150     // emit the buttons belonging in this group; loose buttons are saved for last, to appear at bottom of dialog\r
151     if(b) {\r
152         while(buttons > firstButton)\r
153             layoutList[layout++] = buttonList[--buttons];\r
154         if(layout&1) layoutList[layout++] = -1;\r
155     }\r
156 }\r
157 \r
158 char *\r
159 EndMatch(char *s1, char *s2)\r
160 {\r
161         char *p, *q;\r
162         p = s1; while(*p) p++;\r
163         q = s2; while(*q) q++;\r
164         while(p > s1 && q > s2 && *p == *q) { p--; q--; }\r
165         if(p[1] == 0) return NULL;\r
166         return p+1;\r
167 }\r
168 \r
169 void\r
170 DesignOptionDialog(ChessProgramState *cps)\r
171 {\r
172     int k=0, n=0;\r
173     char buf[MSG_SIZ];\r
174 \r
175     layout = 0;\r
176     buttons = groups = 0;\r
177     while(k < cps->nrOptions) { // k steps through 'solitary' options\r
178         // look if we hit a group of options having names that start with the same word\r
179         int groupSize = 1, groupNameLength = 50;\r
180         sscanf(cps->option[k].name, "%s", buf); // get first word of option name\r
181         while(k + groupSize < cps->nrOptions &&\r
182               strstr(cps->option[k+groupSize].name, buf) == cps->option[k+groupSize].name) {\r
183                 int j;\r
184                 for(j=0; j<groupNameLength; j++) // determine common initial part of option names\r
185                     if( cps->option[k].name[j] != cps->option[k+groupSize].name[j]) break;\r
186                 groupNameLength = j;\r
187                 groupSize++;\r
188                 \r
189         }\r
190         if(groupSize > 3) {\r
191                 // We found a group to terminates the current section\r
192                 LayoutOptions(n, k, "", cps->option); // flush all solitary options appearing before the group\r
193                 groupNameList[groups] = groupNameLength;\r
194                 boxList[groups++] = layout; // group start in even entries\r
195                 LayoutOptions(k, k+groupSize, buf, cps->option); // flush the group\r
196                 boxList[groups++] = layout; // group end in odd entries\r
197                 k = n = k + groupSize;\r
198 #if 0\r
199         } else {\r
200                 // try to recognize "two-column groups" based on option suffix\r
201                 int j = 1;\r
202                 while((p = EndMatch(cps->option[k].name, EndMatch(cps->option[k+2*j].name)) &&\r
203                       (q = EndMatch(cps->option[k+1].name, EndMatch(cps->option[k+2*j+1].name)) ) j++;\r
204 #endif\r
205         } else k += groupSize; // small groups are grouped with the solitary options\r
206     }\r
207     if(n != k) LayoutOptions(n, k, "", cps->option); // flush remaining solitary options\r
208     // decide if and where we break into two column pairs\r
209     \r
210     // Emit buttons and add OK and cancel\r
211 //    for(k=0; k<buttons; k++) layoutList[layout++] = buttonList[k];\r
212     // Create the dialog window\r
213     if(appData.debugMode) CreateOptionDialogTest(layoutList, layout, cps);\r
214 //    CreateOptionDialog(layoutList, layout, cps);\r
215 }\r
216 \r
217 #include <windows.h>\r
218 \r
219 extern HINSTANCE hInst;\r
220 \r
221 typedef struct {\r
222     DLGITEMTEMPLATE item;\r
223     WORD code;\r
224     WORD controlType;\r
225     wchar_t d1, data;\r
226     WORD creationData;\r
227 } Item;\r
228 \r
229 struct {\r
230     DLGTEMPLATE header;\r
231     WORD menu;\r
232     WORD winClass;\r
233     wchar_t title[20];\r
234     WORD pointSize;\r
235     wchar_t fontName[14];\r
236     Item control[MAX_OPTIONS];\r
237 } template = {\r
238     { DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_SETFONT, 0, 0, 0, 0, 295, 300 },\r
239     0x0000, 0x0000, L"Engine #1 Settings ", 8, L"MS Sans Serif"\r
240 };\r
241 \r
242 ChessProgramState *activeCps;\r
243 \r
244 void\r
245 SetOptionValues(HWND hDlg, ChessProgramState *cps)\r
246 // Put all current option values in controls, and write option names next to them\r
247 {\r
248     HANDLE hwndCombo;\r
249     int i, k;\r
250     char **choices, title[MSG_SIZ], *name;\r
251 \r
252     for(i=0; i<layout+buttons; i++) {\r
253         int j=layoutList[i];\r
254         if(j == -2) SetDlgItemText( hDlg, 2000+2*i, ". . ." );\r
255         if(j<0) continue;\r
256         name = cps->option[j].name;\r
257         if(strstr(name, "Polyglot ") == name) name += 9;\r
258         SetDlgItemText( hDlg, 2000+2*i, name );\r
259 //if(appData.debugMode) fprintf(debugFP, "# %s = %d\n",cps->option[j].name, cps->option[j].value );\r
260         switch(cps->option[j].type) {\r
261             case Spin:\r
262                 SetDlgItemInt( hDlg, 2001+2*i, cps->option[j].value, TRUE );\r
263                 break;\r
264             case TextBox:\r
265                 SetDlgItemText( hDlg, 2001+2*i, cps->option[j].textValue );\r
266                 break;\r
267             case CheckBox:\r
268                 CheckDlgButton( hDlg, 2000+2*i, cps->option[j].value != 0);\r
269                 break;\r
270             case ComboBox:\r
271                 choices = (char**) cps->option[j].textValue;\r
272                 hwndCombo = GetDlgItem(hDlg, 2001+2*i);\r
273                 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
274                 for(k=0; k<cps->option[j].max; k++) {\r
275                     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) choices[k]);\r
276                 }\r
277                 SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) choices[cps->option[j].value]);\r
278                 break;\r
279             case Button:\r
280             case SaveButton:\r
281             default:\r
282                 break;\r
283         }\r
284     }\r
285     SetDlgItemText( hDlg, IDOK, "OK" );\r
286     SetDlgItemText( hDlg, IDCANCEL, "Cancel" );\r
287     sprintf(title, "%s Engine Settings (%s)", cps->which, cps->tidy); \r
288     title[0] &= ~32; // capitalize\r
289     SetWindowText( hDlg, title);\r
290     for(i=0; i<groups; i+=2) { \r
291         int id, p; char buf[MSG_SIZ];\r
292         id = k = boxList[i];\r
293         if(layoutList[k] < 0) k++;\r
294         if(layoutList[k] < 0) continue;\r
295         for(p=0; p<groupNameList[i]; p++) buf[p] = cps->option[layoutList[k]].name[p];\r
296         buf[p] = 0;\r
297         SetDlgItemText( hDlg, 2000+2*(id+MAX_OPTIONS), buf );\r
298     }\r
299 }\r
300 \r
301 \r
302 void\r
303 GetOptionValues(HWND hDlg, ChessProgramState *cps)\r
304 // read out all controls, and if value is altered, remember it and send it to the engine\r
305 {\r
306     HANDLE hwndCombo;\r
307     int i, k, new=0, changed=0;\r
308     char **choices, newText[MSG_SIZ], buf[MSG_SIZ];\r
309     BOOL success;\r
310 \r
311     for(i=0; i<layout; i++) {\r
312         int j=layoutList[i];\r
313         if(j<0) continue;\r
314         switch(cps->option[j].type) {\r
315             case Spin:\r
316                 new = GetDlgItemInt( hDlg, 2001+2*i, &success, TRUE );\r
317                 if(!success) break;\r
318                 if(new < cps->option[j].min) new = cps->option[j].min;\r
319                 if(new > cps->option[j].max) new = cps->option[j].max;\r
320                 changed = 2*(cps->option[j].value != new);\r
321                 cps->option[j].value = new;\r
322                 break;\r
323             case TextBox:\r
324             case FileName:\r
325             case PathName:\r
326                 success = GetDlgItemText( hDlg, 2001+2*i, newText, MSG_SIZ - strlen(cps->option[j].name) - 9 );\r
327                 if(!success) break;\r
328                 changed = strcmp(cps->option[j].textValue, newText) != 0;\r
329                 strcpy(cps->option[j].textValue, newText);\r
330                 break;\r
331             case CheckBox:\r
332                 new = IsDlgButtonChecked( hDlg, 2000+2*i );\r
333                 changed = 2*(cps->option[j].value != new);\r
334                 cps->option[j].value = new;\r
335                 break;\r
336             case ComboBox:\r
337                 choices = (char**) cps->option[j].textValue;\r
338                 hwndCombo = GetDlgItem(hDlg, 2001+2*i);\r
339                 success = GetDlgItemText( hDlg, 2001+2*i, newText, MSG_SIZ );\r
340                 if(!success) break;\r
341                 new = -1;\r
342                 for(k=0; k<cps->option[j].max; k++) {\r
343                     if(!strcmp(choices[k], newText)) new = k;\r
344                 }\r
345                 changed = new >= 0 && (cps->option[j].value != new);\r
346                 if(changed) cps->option[j].value = new;\r
347                 break;\r
348             case Button:\r
349             default:\r
350                 break; // are treated instantly, so they have been sent already\r
351         }\r
352         if(changed == 2) sprintf(buf, "option %s=%d\n", cps->option[j].name, new); else\r
353         if(changed == 1) sprintf(buf, "option %s=%s\n", cps->option[j].name, newText);\r
354         if(changed) SendToProgram(buf, cps);\r
355     }\r
356 }\r
357 \r
358 LRESULT CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
359 {\r
360     char buf[MSG_SIZ];\r
361     int i, j;\r
362 \r
363     switch( message )\r
364     {\r
365     case WM_INITDIALOG:\r
366 \r
367 //        CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
368         SetOptionValues(hDlg, activeCps);\r
369 \r
370 //        SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
371 \r
372         break;\r
373 \r
374     case WM_COMMAND:\r
375         switch( LOWORD(wParam) ) {\r
376         case IDOK:\r
377             GetOptionValues(hDlg, activeCps);\r
378             EndDialog( hDlg, 0 );\r
379             return TRUE;\r
380 \r
381         case IDCANCEL:\r
382             EndDialog( hDlg, 1 );   \r
383             return TRUE;\r
384 \r
385         default:\r
386             // program-defined push buttons\r
387             i = LOWORD(wParam);\r
388             if( i>=2000 &&  i < 2000+2*(layout+buttons)) {\r
389                 j = layoutList[(i - 2000)/2];\r
390                 if(j == -2) {\r
391                           char filter[] = \r
392                                 "All files\0*.*\0BIN Files\0*.bin\0LOG Files\0*.log\0INI Files\0*.ini\0\0";\r
393 /*\r
394\r
395                               'A','l','l',' ','F','i','l','e','s', 0,\r
396                               '*','.','*', 0,\r
397                               'B','I','N',' ','F','i','l','e','s', 0,\r
398                               '*','.','b','i','n', 0,\r
399                               0 };\r
400 */\r
401                           OPENFILENAME ofn;\r
402 \r
403                           strcpy( buf, "" );\r
404 \r
405                           ZeroMemory( &ofn, sizeof(ofn) );\r
406 \r
407                           ofn.lStructSize = sizeof(ofn);\r
408                           ofn.hwndOwner = hDlg;\r
409                           ofn.hInstance = hInst;\r
410                           ofn.lpstrFilter = filter;\r
411                           ofn.lpstrFile = buf;\r
412                           ofn.nMaxFile = sizeof(buf);\r
413                           ofn.lpstrTitle = "Choose Book";\r
414                           ofn.Flags = OFN_FILEMUSTEXIST | OFN_LONGNAMES | OFN_HIDEREADONLY;\r
415 \r
416                           if( GetOpenFileName( &ofn ) ) {\r
417                               SetDlgItemText( hDlg, i+3, buf );\r
418                           }\r
419                 }\r
420                 if(j < 0) break;\r
421                 if( activeCps->option[j].type  == SaveButton)\r
422                      GetOptionValues(hDlg, activeCps);\r
423                 else if( activeCps->option[j].type  != Button) break;\r
424                 sprintf(buf, "option %s\n", activeCps->option[j].name);\r
425                 SendToProgram(buf, activeCps);\r
426             }\r
427             break;\r
428         }\r
429 \r
430         break;\r
431     }\r
432 \r
433     return FALSE;\r
434 }\r
435 \r
436 #if 0\r
437 // example copied from MS docs\r
438 #define ID_HELP   150\r
439 #define ID_TEXT   200\r
440 \r
441 LPWORD lpwAlign(LPWORD lpIn)\r
442 {\r
443     ULONG ul;\r
444 \r
445     ul = (ULONG)lpIn;\r
446     ul ++;\r
447     ul >>=1;\r
448     ul <<=1;\r
449     return (LPWORD)ul;\r
450 }\r
451 \r
452 LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, LPSTR lpszMessage)\r
453 {\r
454     HGLOBAL hgbl;\r
455     LPDLGTEMPLATE lpdt;\r
456     LPDLGITEMTEMPLATE lpdit;\r
457     LPWORD lpw;\r
458     LPWSTR lpwsz;\r
459     LRESULT ret;\r
460     int nchar;\r
461 \r
462     hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);\r
463     if (!hgbl)\r
464         return -1;\r
465  \r
466     lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);\r
467  \r
468     // Define a dialog box.\r
469  \r
470     lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION;\r
471 //                WS_POPUP |             WS_SYSMENU | DS_MODALFRAME | WS_CAPTION | DS_SETFONT\r
472     lpdt->cdit = 3;         // Number of controls\r
473     lpdt->x  = 10;  lpdt->y  = 10;\r
474     lpdt->cx = 100; lpdt->cy = 100;\r
475 \r
476     lpw = (LPWORD)(lpdt + 1);\r
477     *lpw++ = 0;             // No menu\r
478     *lpw++ = 0;             // Predefined dialog box class (by default)\r
479 \r
480     lpwsz = (LPWSTR)lpw;\r
481     nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "My Dialog", -1, lpwsz, 50);\r
482     lpw += nchar;\r
483 \r
484     //-----------------------\r
485     // Define an OK button.\r
486     //-----------------------\r
487     lpw = lpwAlign(lpw);    // Align DLGITEMTEMPLATE on DWORD boundary\r
488     lpdit = (LPDLGITEMTEMPLATE)lpw;\r
489     lpdit->x  = 10; lpdit->y  = 70;\r
490     lpdit->cx = 80; lpdit->cy = 20;\r
491     lpdit->id = IDOK;       // OK button identifier\r
492     lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;\r
493 \r
494     lpw = (LPWORD)(lpdit + 1);\r
495     *lpw++ = 0xFFFF;\r
496     *lpw++ = 0x0080;        // Button class\r
497 \r
498     lpwsz = (LPWSTR)lpw;\r
499     nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "OK", -1, lpwsz, 50);\r
500     lpw += nchar;\r
501     lpw = lpwAlign(lpw);    // Align creation data on DWORD boundary\r
502     *lpw++ = 0;             // No creation data\r
503 \r
504     //-----------------------\r
505     // Define a Help button.\r
506     //-----------------------\r
507     lpw = lpwAlign(lpw);    // Align DLGITEMTEMPLATE on DWORD boundary\r
508     lpdit = (LPDLGITEMTEMPLATE)lpw;\r
509     lpdit->x  = 55; lpdit->y  = 10;\r
510     lpdit->cx = 40; lpdit->cy = 20;\r
511     lpdit->id = ID_HELP;    // Help button identifier\r
512     lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;\r
513 \r
514     lpw = (LPWORD)(lpdit + 1);\r
515     *lpw++ = 0xFFFF;\r
516     *lpw++ = 0x0080;        // Button class atom\r
517 \r
518     lpwsz = (LPWSTR)lpw;\r
519     nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "Help", -1, lpwsz, 50);\r
520     lpw += nchar;\r
521     lpw = lpwAlign(lpw);    // Align creation data on DWORD boundary\r
522     *lpw++ = 0;             // No creation data\r
523 \r
524     //-----------------------\r
525     // Define a static text control.\r
526     //-----------------------\r
527     lpw = lpwAlign(lpw);    // Align DLGITEMTEMPLATE on DWORD boundary\r
528     lpdit = (LPDLGITEMTEMPLATE)lpw;\r
529     lpdit->x  = 10; lpdit->y  = 10;\r
530     lpdit->cx = 40; lpdit->cy = 20;\r
531     lpdit->id = ID_TEXT;    // Text identifier\r
532     lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;\r
533 \r
534     lpw = (LPWORD)(lpdit + 1);\r
535     *lpw++ = 0xFFFF;\r
536     *lpw++ = 0x0082;        // Static class\r
537 \r
538     for (lpwsz = (LPWSTR)lpw; *lpwsz++ = (WCHAR)*lpszMessage++;);\r
539     lpw = (LPWORD)lpwsz;\r
540     lpw = lpwAlign(lpw);    // Align creation data on DWORD boundary\r
541     *lpw++ = 0;             // No creation data\r
542 \r
543     GlobalUnlock(hgbl); \r
544     ret = DialogBoxIndirect(hinst, \r
545                            (LPDLGTEMPLATE)hgbl, \r
546                            hwndOwner, \r
547                            (DLGPROC)DialogProc); \r
548     GlobalFree(hgbl); \r
549     return ret; \r
550 }\r
551 #endif\r
552 \r
553 void AddControl(int x, int y, int w, int h, int type, int style, int n)\r
554 {\r
555     int i;\r
556 \r
557     i = template.header.cdit++;\r
558     template.control[i].item.style = style;\r
559     template.control[i].item.dwExtendedStyle = 0;\r
560     template.control[i].item.x = x;\r
561     template.control[i].item.y = y;\r
562     template.control[i].item.cx = w;\r
563     template.control[i].item.cy = h;\r
564     template.control[i].item.id = 2000 + n;\r
565     template.control[i].code = 0xFFFF;\r
566     template.control[i].controlType = type;\r
567     template.control[i].d1 = ' ';\r
568     template.control[i].data = 0;\r
569     template.control[i].creationData = 0;\r
570 }\r
571 \r
572 void AddOption(int x, int y, Control type, int i)\r
573 {\r
574 \r
575     switch(type) {\r
576         case Slider:\r
577         case Spin:\r
578             AddControl(x, y+1, 95, 9, 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, i);\r
579             AddControl(x+95, y, 50, 11, 0x0081, ES_AUTOHSCROLL | ES_NUMBER | WS_BORDER | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i+1);\r
580             break;\r
581 //      case TextBox:\r
582             AddControl(x, y+1, 95, 9, 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, i);\r
583             AddControl(x+95, y, 190, 11, 0x0081, ES_AUTOHSCROLL | WS_BORDER | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i+1);\r
584             break;\r
585         case TextBox:  // For now all text edits get a browse button, as long as -file and -path options are not yet implemented\r
586         case FileName:\r
587         case PathName:\r
588             AddControl(x, y+1, 95, 9, 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, i);\r
589             AddControl(x+95, y, 180, 11, 0x0081, ES_AUTOHSCROLL | WS_BORDER | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i+1);\r
590             AddControl(x+275, y, 20, 12, 0x0080, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i-2);\r
591             layoutList[i/2-1] = -2;\r
592             break;\r
593         case CheckBox:\r
594             AddControl(x, y, 145, 11, 0x0080, BS_AUTOCHECKBOX | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i);\r
595             break;\r
596         case ComboBox:\r
597             AddControl(x, y+1, 95, 9, 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, i);\r
598             AddControl(x+95, y-1, 50, 500, 0x0085, CBS_AUTOHSCROLL | CBS_DROPDOWN | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i+1);\r
599             break;\r
600         case Button:\r
601         case ResetButton:\r
602         case SaveButton:\r
603             AddControl(x-2, y, 65, 13, 0x0080, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD, i);\r
604         case Message:\r
605             break;\r
606     }\r
607     \r
608 }\r
609 \r
610 void\r
611 CreateDialogTemplate(int *layoutList, int nr, ChessProgramState *cps)\r
612 {\r
613     int i, j, x=1, y=0, buttonRows, breakPoint = -1, k=0;\r
614 \r
615     template.header.cdit = 0;\r
616     template.header.cx = 307;\r
617     buttonRows = (buttons + 1 + 3)/4; // 4 per row, rounded up\r
618     if(nr > 50) { \r
619         breakPoint = (nr+2*buttonRows+1)/2 & ~1;\r
620         template.header.cx = 625;\r
621     }\r
622 \r
623     for(i=0; i<nr; i++) {\r
624         if(k < groups && i == boxList[k]) {\r
625             y += 10;\r
626             AddControl(x+2, y+13*(i>>1)-2, 301, 13*(boxList[k+1]-boxList[k]>>1)+8, \r
627                                                 0x0082, WS_VISIBLE | WS_CHILD | SS_BLACKFRAME, 2400);\r
628             AddControl(x+60, y+13*(i>>1)-6, 10*groupNameList[k]/3, 10, \r
629                                                 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, 2*(i+MAX_OPTIONS));\r
630         }\r
631         j = layoutList[i];\r
632         if(j >= 0)\r
633             AddOption(x+155-150*(i&1), y+13*(i>>1)+5, cps->option[j].type, 2*i);\r
634         if(k < groups && i+1 == boxList[k+1]) {\r
635             k += 2; y += 4;\r
636         }\r
637         if(i+1 == breakPoint) { x += 318; y = -13*(breakPoint>>1); }\r
638     }\r
639     // add butons at the bottom of dialog window\r
640     y += 13*(nr>>1)+5;\r
641 \r
642     AddControl(x+275, y+18*(buttonRows-1), 25, 15, 0x0080, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD, IDOK-2000);\r
643     AddControl(x+235, y+18*(buttonRows-1), 35, 15, 0x0080, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD, IDCANCEL-2000);\r
644     for(i=0; i<buttons; i++) {\r
645         AddControl(x+70*(i%4)+5, y+18*(i/4), 65, 15, 0x0080, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD, 2*(nr+i));\r
646         layoutList[nr+i] = buttonList[i];\r
647     }\r
648     template.title[8] = cps == &first ? '1' :  '2';\r
649     template.header.cy = y += 18*buttonRows+2;\r
650     template.header.style &= ~WS_VSCROLL;\r
651 #if 0\r
652     if(y > 300) {\r
653         template.header.cx = 295;\r
654         template.header.cy = 300;\r
655         template.header.style |= WS_VSCROLL;\r
656     }\r
657 #endif\r
658 }\r
659 \r
660 void \r
661 EngineOptionsPopup(HWND hwnd, ChessProgramState *cps)\r
662 {\r
663     FARPROC lpProc = MakeProcInstance( (FARPROC) SettingsProc, hInst );\r
664 \r
665     activeCps = cps;\r
666     DesignOptionDialog(cps);\r
667     CreateDialogTemplate(layoutList, layout, cps);\r
668 \r
669 \r
670     DialogBoxIndirect( hInst, &template.header, hwnd, (DLGPROC)lpProc );\r
671 \r
672     FreeProcInstance(lpProc);\r
673 \r
674     return;\r
675 }\r
676 \r
677 \r