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