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