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