X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=winboard%2Fwsettings.c;h=893d91b47ae737df3e1581c4435887c8cf4ee82c;hb=HEAD;hp=0a25f9bbe786d4dab74724d3b3ce62f1a52da08a;hpb=b9c351f74daacf02bfbaa97669555d8046b27b43;p=xboard.git diff --git a/winboard/wsettings.c b/winboard/wsettings.c index 0a25f9b..893d91b 100644 --- a/winboard/wsettings.c +++ b/winboard/wsettings.c @@ -1,7 +1,31 @@ /* + * woptions.h -- Options dialog box routines for WinBoard + * + * Copyright 2003, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free + * Software Foundation, Inc. + * + * ------------------------------------------------------------------------ + * + * GNU XBoard is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * GNU XBoard is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. * + * + *------------------------------------------------------------------------ + ** See the file ChangeLog for a revision history. */ + +/* * Engine-settings dialog. The complexity come from an attempt to present the engine-defined options * in a nicey formatted layout. To this end we first run a back-end pre-formatter, which will distribute - * the controls over two columns (the minimum required, as some are double width). It also takes care of + * the controls over two columns (the minimum required, as some are double width). It also takes care of * grouping options that start with the same word (mainly for "Polyglot ..." options). It assigns relative * suitability to break points between lines, and in the end decides if and where to break up the list * for display in multiple (2*N) columns. @@ -9,12 +33,20 @@ * that generates the actual dialog box from it. */ -//#include "config.h" #include "config.h" +#include +#include #include +#include #include "common.h" +#include "frontend.h" #include "backend.h" +#include "winboard.h" +#include "backendz.h" + +#define _(s) T_(s) +#define N_(s) s int layoutList[2*MAX_OPTIONS]; int checkList[2*MAX_OPTIONS]; @@ -24,21 +56,38 @@ int boxList[2*MAX_OPTIONS]; int groupNameList[2*MAX_OPTIONS]; int breaks[MAX_OPTIONS]; int checks, combos, buttons, layout, groups; +char title[MSG_SIZ]; +char *engineName, *engineDir, *engineChoice, *engineLine, *nickName, *params; +Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick, isUCCI; +extern Option installOptions[], matchOptions[]; +char *engineList[MAXENGINES] = {""}, *engineMnemonic[MAXENGINES] = {""}; +void (*okFunc)(); +ChessProgramState *activeCps; +Option *activeList; +int InstallOK P((void)); +typedef int ButtonCallback(HWND h); +ButtonCallback *comboCallback; void -PrintOpt(int i, int right, ChessProgramState *cps) +PrintOpt(int i, int right, Option *optionList) { if(i<0) { if(!right) fprintf(debugFP, "%30s", ""); } else { - Option opt = cps->option[i]; + Option opt = optionList[i]; switch(opt.type) { + case Slider: case Spin: fprintf(debugFP, "%20.20s [ +/-]", opt.name); break; case TextBox: + case FileName: + case PathName: fprintf(debugFP, "%20.20s [______________________________________]", opt.name); break; + case Label: + fprintf(debugFP, "%41.41s", opt.name); + break; case CheckBox: fprintf(debugFP, "[x] %-26.25s", opt.name); break; @@ -47,7 +96,10 @@ PrintOpt(int i, int right, ChessProgramState *cps) break; case Button: case SaveButton: + case ResetButton: fprintf(debugFP, "[ %26.26s ]", opt.name); + case Message: + default: break; } } @@ -55,21 +107,21 @@ PrintOpt(int i, int right, ChessProgramState *cps) } void -CreateOptionDialogTest(int *list, int nr, ChessProgramState *cps) +CreateOptionDialogTest(int *list, int nr, Option *optionList) { int line; for(line = 0; line < nr; line+=2) { - PrintOpt(list[line+1], 0, cps); - PrintOpt(list[line], 1, cps); + PrintOpt(list[line+1], 0, optionList); + PrintOpt(list[line], 1, optionList); } } void LayoutOptions(int firstOption, int endOption, char *groupName, Option *optionList) { - int n, i, b = strlen(groupName), stop, prefix, right, nextOption, firstButton = buttons; - Control lastType, nextType; + int i, b = strlen(groupName), stop, prefix, right, nextOption, firstButton = buttons; + Control lastType, nextType=Label; nextOption = firstOption; while(nextOption < endOption) { @@ -80,18 +132,26 @@ LayoutOptions(int firstOption, int endOption, char *groupName, Option *optionLis switch(nextType = optionList[nextOption].type) { case CheckBox: checkList[checks++] = nextOption; lastType = CheckBox; break; case ComboBox: comboList[combos++] = nextOption; lastType = ComboBox; break; + case ResetButton: case SaveButton: case Button: buttonList[buttons++] = nextOption; lastType = Button; break; case TextBox: + case ListBox: + case FileName: + case PathName: + case Slider: + case Label: case Spin: stop++; + default: + case Message: ; // cannot happen } nextOption++; } // We now must be at the end, or looking at a spin or textbox (in nextType) - if(!stop) + if(!stop) nextType = Button; // kudge to flush remaining checks and combos undistorted // Take a new line if a spin follows combos or checks, or when we encounter a textbox - if((combos+checks || nextType == TextBox) && layout&1) { + if((combos+checks || nextType == TextBox || nextType == ListBox || nextType == FileName || nextType == PathName || nextType == Label) && layout&1) { layoutList[layout++] = -1; } // The last check or combo before a spin will be put on the same line as that spin (prefix) @@ -103,10 +163,10 @@ LayoutOptions(int firstOption, int endOption, char *groupName, Option *optionLis } // if a combo is followed by a textbox, it must stay at the end of the combo/checks list to appear // immediately above the textbox, so treat it as check. (A check would automatically be and remain there.) - if(nextType == TextBox && lastType == ComboBox) + if((nextType == TextBox || nextType == ListBox || nextType == FileName || nextType == PathName) && lastType == ComboBox) checkList[checks++] = comboList[--combos]; // Now append the checks behind the (remaining) combos to treat them as one group - for(i=0; i< checks; i++) + for(i=0; i< checks; i++) comboList[combos++] = checkList[i]; // emit the consecutive checks and combos in two columns right = combos/2; // rounded down if odd! @@ -121,11 +181,26 @@ LayoutOptions(int firstOption, int endOption, char *groupName, Option *optionLis layoutList[layout++] = -1; layoutList[layout++] = comboList[2*right]; } - if(nextType == TextBox) { + if(nextType == ListBox) { + // A listBox will be left-adjusted, and cause rearrangement of the elements before it to the right column + breaks[layout/2] = lastType == Button ? 0 : 100; + layoutList[layout++] = -1; + layoutList[layout++] = nextOption - 1; + for(i=optionList[nextOption-1].min; i>0; i--) { // extra high text edit + breaks[layout/2] = -1; + layoutList[layout++] = -1; + layoutList[layout++] = -1; + } + } else + if(nextType == TextBox || nextType == FileName || nextType == PathName || nextType == Label) { // A textBox is double width, so must be left-adjusted, and the right column remains empty breaks[layout/2] = lastType == Button ? 0 : 100; layoutList[layout++] = -1; layoutList[layout++] = nextOption - 1; + if(optionList[nextOption-1].min) { // extra high text edit: goes right of existing listbox + layout -= 2; // remove + layoutList[layout-2*optionList[nextOption-1].min-2] = nextOption - 1; + } } else if(nextType == Spin) { // A spin will go in the next available position (right to left!). If it had to be prefixed with // a check or combo, this next position must be to the right, and the prefix goes left to it. @@ -143,7 +218,7 @@ LayoutOptions(int firstOption, int endOption, char *groupName, Option *optionLis } } -char *p +char * EndMatch(char *s1, char *s2) { char *p, *q; @@ -155,49 +230,46 @@ EndMatch(char *s1, char *s2) } void -DesignOptionDialog(ChessProgramState *cps) +DesignOptionDialog(int nrOpt, Option *optionList) { int k=0, n=0; char buf[MSG_SIZ]; layout = 0; buttons = groups = 0; - while(k < cps->nrOptions) { // k steps through 'solitary' options + while(k < nrOpt) { // k steps through 'solitary' options // look if we hit a group of options having names that start with the same word int groupSize = 1, groupNameLength = 50; - sscanf(cps->option[k].name, "%s", &buf); // get first word of option name - while(k + groupSize < cps->nrOptions && - strstr(cps->option[k+groupSize].name, buf) == cps->option[k+groupSize].name) { + sscanf(optionList[k].name, "%s", buf); // get first word of option name + while(k + groupSize < nrOpt && + strstr(optionList[k+groupSize].name, buf) == optionList[k+groupSize].name) { int j; for(j=0; joption[k].name[j] != cps->option[k+groupSize].name[j]) break; + if( optionList[k].name[j] != optionList[k+groupSize].name[j]) break; groupNameLength = j; groupSize++; - + } if(groupSize > 3) { // We found a group to terminates the current section - LayoutOptions(n, k, "", cps->option); // flush all solitary options appearing before the group + LayoutOptions(n, k, "", optionList); // flush all solitary options appearing before the group groupNameList[groups] = groupNameLength; boxList[groups++] = layout; // group start in even entries - LayoutOptions(k, k+groupSize, buf, cps->option); // flush the group + LayoutOptions(k, k+groupSize, buf, optionList); // flush the group boxList[groups++] = layout; // group end in odd entries k = n = k + groupSize; - } else { - // try to recognize "two-column groups" based on option suffix - int j = 1; - while((p = EndMatch(cps->option[k].name, EndMatch(cps->option[k+2*j].name)) && - (q = EndMatch(cps->option[k+1].name, EndMatch(cps->option[k+2*j+1].name)) ) j++ } else k += groupSize; // small groups are grouped with the solitary options } - if(n != k) LayoutOptions(n, k, "", cps->option); // flush remaining solitary options + if(n != k) LayoutOptions(n, k, "", optionList); // flush remaining solitary options // decide if and where we break into two column pairs - + // Emit buttons and add OK and cancel // for(k=0; k @@ -225,148 +297,194 @@ struct { 0x0000, 0x0000, L"Engine #1 Settings ", 8, L"MS Sans Serif" }; -ChessProgramState *activeCps; +char * +AddCR(char *s) +{ + char *p=s, *q; + int n=0; + while(p = strchr(p, '\n')) p++, n++; // count linefeeds + p = q = malloc(strlen(s) + n + 1); + while(*p++ = *s++) if(p[-1] == '\n') p[-1] = '\r', *p++ = '\n'; + return q; +} void -SetOptionValues(HWND hDlg, ChessProgramState *cps) +SetOptionValues(HWND hDlg, ChessProgramState *cps, Option *optionList) // Put all current option values in controls, and write option names next to them { HANDLE hwndCombo; int i, k; - char **choices, title[MSG_SIZ], *name; + char **choices, *name; for(i=0; ioption[j].name; + name = cps ? optionList[j].name : _(optionList[j].name); if(strstr(name, "Polyglot ") == name) name += 9; SetDlgItemText( hDlg, 2000+2*i, name ); -//if(appData.debugMode) fprintf(debugFP, "# %s = %d\n",cps->option[j].name, cps->option[j].value ); - switch(cps->option[j].type) { +//if(appData.debugMode) fprintf(debugFP, "# %s = %d\n",optionList[j].name, optionList[j].value ); + switch(optionList[j].type) { case Spin: - SetDlgItemInt( hDlg, 2001+2*i, cps->option[j].value, TRUE ); + SetDlgItemInt( hDlg, 2001+2*i, cps ? optionList[j].value : *(int*)optionList[j].target, TRUE ); break; case TextBox: - SetDlgItemText( hDlg, 2001+2*i, cps->option[j].textValue ); + case FileName: + case PathName: + name = AddCR(cps ? optionList[j].textValue : *(char**)optionList[j].target); // stupid CR... + SetDlgItemText( hDlg, 2001+2*i, name); + free(name); break; case CheckBox: - CheckDlgButton( hDlg, 2000+2*i, cps->option[j].value != 0); + CheckDlgButton( hDlg, 2000+2*i, (cps ? optionList[j].value : *(Boolean*)optionList[j].target) != 0); break; case ComboBox: - choices = (char**) cps->option[j].textValue; + choices = (char**) optionList[j].textValue; hwndCombo = GetDlgItem(hDlg, 2001+2*i); SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0); - for(k=0; koption[j].max; k++) { + for(k=0; koption[j].value]); + SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) choices[optionList[j].value]); + break; + case ListBox: + choices = (char**) optionList[j].choice; + hwndCombo = GetDlgItem(hDlg, 2001+2*i); + SendMessage(hwndCombo, LB_RESETCONTENT, 0, 0); + for(k=0; kwhich, cps->tidy); + SetDlgItemText( hDlg, IDOK, _("OK") ); + SetDlgItemText( hDlg, IDCANCEL, _("Cancel") ); title[0] &= ~32; // capitalize SetWindowText( hDlg, title); - for(i=0; ioption[layoutList[k]].name[p]; + for(p=0; poption[j].name ); - switch(cps->option[j].type) { + switch(optionList[j].type) { case Spin: new = GetDlgItemInt( hDlg, 2001+2*i, &success, TRUE ); if(!success) break; - if(new < cps->option[j].min) new = cps->option[j].min; - if(new > cps->option[j].max) new = cps->option[j].max; - changed = 2*(cps->option[j].value != new); - cps->option[j].value = new; + if(new < optionList[j].min) new = optionList[j].min; + if(new > optionList[j].max) new = optionList[j].max; + if(!cps) { *(int*)optionList[j].target = new; break; } + changed = 2*(optionList[j].value != new); + optionList[j].value = new; break; case TextBox: case FileName: case PathName: - success = GetDlgItemText( hDlg, 2001+2*i, newText, MSG_SIZ - strlen(cps->option[j].name) - 9 ); - if(!success) break; - changed = strcmp(cps->option[j].textValue, newText) != 0; - strcpy(cps->option[j].textValue, newText); + if(cps) len = MSG_SIZ - strlen(optionList[j].name) - 9, text = newText; + else len = GetWindowTextLength(GetDlgItem(hDlg, 2001+2*i)) + 1, text = (char*) malloc(len); + success = GetDlgItemText( hDlg, 2001+2*i, text, len ); + if(!success) text[0] = NULLCHAR; // empty string can be valid input + if(!cps) { + char *p; + p = (optionList[j].type != FileName ? strdup(text) : InterpretFileName(text, homeDir)); // all files relative to homeDir! + FREE(*(char**)optionList[j].target); *(char**)optionList[j].target = p; + free(text); text = p; + while(*p++ = *text++) if(p[-1] == '\r') p--; // crush CR + break; + } + changed = strcmp(optionList[j].textValue, newText) != 0; + safeStrCpy(optionList[j].textValue, newText, MSG_SIZ - (optionList[j].textValue - optionList[j].name) ); break; case CheckBox: new = IsDlgButtonChecked( hDlg, 2000+2*i ); - changed = 2*(cps->option[j].value != new); - cps->option[j].value = new; + if(!cps) { *(Boolean*)optionList[j].target = new; break; } + changed = 2*(optionList[j].value != new); + optionList[j].value = new; break; case ComboBox: - choices = (char**) cps->option[j].textValue; - hwndCombo = GetDlgItem(hDlg, 2001+2*i); + choices = (char**) optionList[j].textValue; success = GetDlgItemText( hDlg, 2001+2*i, newText, MSG_SIZ ); if(!success) break; new = -1; - for(k=0; koption[j].max; k++) { - if(!strcmp(choices[k], newText)) new = k; + for(k=0; k= 0 && (cps->option[j].value != new); - if(changed) cps->option[j].value = new; + if(!cps && new > 0) { + if(*(char**)optionList[j].target) free(*(char**)optionList[j].target); + *(char**)optionList[j].target = strdup(optionList[j].choice[new]); + break; + } + changed = new >= 0 && (optionList[j].value != new); + if(changed) optionList[j].value = new; break; + case ListBox: + if(optionList[j].textValue) + *(int*) optionList[j].textValue = SendDlgItemMessage(hDlg, 2001+2*i, LB_GETCURSEL, 0, 0); case Button: + default: break; // are treated instantly, so they have been sent already } - if(changed == 2) sprintf(buf, "option %s=%d\n", cps->option[j].name, new); else - if(changed == 1) sprintf(buf, "option %s=%s\n", cps->option[j].name, newText); + if(changed == 2) + snprintf(buf, MSG_SIZ, "option %s=%d\n", optionList[j].name, new); else + if(changed == 1) + snprintf(buf, MSG_SIZ, "option %s=%s\n", optionList[j].name, newText); if(changed) SendToProgram(buf, cps); } + if(!cps && okFunc) return ((ButtonCallback*) okFunc)(0); + return 1; } +char *defaultExt[] = { NULL, "pgn", "fen", "exe", "trn", "bin", "log", "ini" }; +HWND settingsDlg; + LRESULT CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { - static int * lpIndexFRC; - BOOL index_is_ok; char buf[MSG_SIZ]; - int i, j; + int i, j, ext; switch( message ) { case WM_INITDIALOG: // CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER)); - SetOptionValues(hDlg, activeCps); - -// SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit)); + SetOptionValues(hDlg, activeCps, activeList); + settingsDlg = hDlg; + SetFocus(GetDlgItem(hDlg, IDCANCEL)); break; case WM_COMMAND: switch( LOWORD(wParam) ) { case IDOK: - GetOptionValues(hDlg, activeCps); + if(!GetOptionValues(hDlg, activeCps, activeList)) return FALSE; EndDialog( hDlg, 0 ); + comboCallback = NULL; activeCps = NULL; settingsDlg = NULL; return TRUE; case IDCANCEL: - EndDialog( hDlg, 1 ); + EndDialog( hDlg, 1 ); + comboCallback = NULL; activeCps = NULL; settingsDlg = NULL; return TRUE; default: @@ -375,19 +493,14 @@ LRESULT CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa if( i>=2000 && i < 2000+2*(layout+buttons)) { j = layoutList[(i - 2000)/2]; if(j == -2) { - char filter[] = - "All files\0*.*\0BIN Files\0*.bin\0LOG Files\0*.log\0INI Files\0*.ini\0\0"; -/* -{ - 'A','l','l',' ','F','i','l','e','s', 0, - '*','.','*', 0, - 'B','I','N',' ','F','i','l','e','s', 0, - '*','.','b','i','n', 0, - 0 }; -*/ + char filter[] = + "All files\0*.*\0Game files\0*.pgn;*.gam\0Position files\0*.fen;*.epd;*.pos\0" + "EXE files\0*.exe;*.jar\0Tournament files (*.trn)\0*.trn\0" + "BIN Files\0*.bin\0LOG Files\0*.log\0INI Files\0*.ini\0" + "Image files\0*.bmp\0\0"; OPENFILENAME ofn; - strcpy( buf, "" ); + GetDlgItemText( hDlg, i+3, buf, MSG_SIZ ); ZeroMemory( &ofn, sizeof(ofn) ); @@ -395,20 +508,40 @@ LRESULT CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa ofn.hwndOwner = hDlg; ofn.hInstance = hInst; ofn.lpstrFilter = filter; + ofn.nFilterIndex = 1L + (ext = activeCps ? 0 : activeList[layoutList[(i-2000)/2+1]].max & 31); + ofn.lpstrDefExt = defaultExt[ext]; ofn.lpstrFile = buf; ofn.nMaxFile = sizeof(buf); - ofn.lpstrTitle = "Choose Book"; + ofn.lpstrTitle = _("Choose File"); ofn.Flags = OFN_FILEMUSTEXIST | OFN_LONGNAMES | OFN_HIDEREADONLY; - if( GetOpenFileName( &ofn ) ) { + if( activeList[layoutList[(i-2000)/2+1]].max & 32 ? + GetOpenFileName( &ofn ) : + GetSaveFileName( &ofn ) ) { SetDlgItemText( hDlg, i+3, buf ); } + } else + if(j == -3) { + GetDlgItemText( hDlg, i+3, buf, MSG_SIZ ); + if( BrowseForFolder( _("Choose Folder:"), buf ) ) { + SetDlgItemText( hDlg, i+3, buf ); + } } if(j < 0) break; - if( activeCps->option[j].type == SaveButton) - GetOptionValues(hDlg, activeCps); - else if( activeCps->option[j].type != Button) break; - sprintf(buf, "option %s\n", activeCps->option[j].name); + if(comboCallback && activeList[j].type == ComboBox && HIWORD(wParam) == CBN_SELCHANGE) { + if(j > 5) break; // Yegh! Must solve problem with more than one combobox in dialog + (*comboCallback)(hDlg); + break; + } else + if(activeList[j].type == ListBox && HIWORD(wParam) == /*LBN_SELCHANGE*/ LBN_DBLCLK) { + ((ButtonCallback *) activeList[j].target)(hDlg); + break; + } else + if( activeList[j].type == SaveButton) + GetOptionValues(hDlg, activeCps, activeList); + else if( activeList[j].type != Button) break; + else if( !activeCps ) { (*(ButtonCallback*) activeList[j].target)(hDlg); break; } + snprintf(buf, MSG_SIZ, "option %s\n", activeList[j].name); SendToProgram(buf, activeCps); } break; @@ -420,123 +553,6 @@ LRESULT CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa return FALSE; } -#if 0 -// example copied from MS docs -#define ID_HELP 150 -#define ID_TEXT 200 - -LPWORD lpwAlign(LPWORD lpIn) -{ - ULONG ul; - - ul = (ULONG)lpIn; - ul ++; - ul >>=1; - ul <<=1; - return (LPWORD)ul; -} - -LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, LPSTR lpszMessage) -{ - HGLOBAL hgbl; - LPDLGTEMPLATE lpdt; - LPDLGITEMTEMPLATE lpdit; - LPWORD lpw; - LPWSTR lpwsz; - LRESULT ret; - int nchar; - - hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024); - if (!hgbl) - return -1; - - lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl); - - // Define a dialog box. - - lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION; -// WS_POPUP | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION | DS_SETFONT - lpdt->cdit = 3; // Number of controls - lpdt->x = 10; lpdt->y = 10; - lpdt->cx = 100; lpdt->cy = 100; - - lpw = (LPWORD)(lpdt + 1); - *lpw++ = 0; // No menu - *lpw++ = 0; // Predefined dialog box class (by default) - - lpwsz = (LPWSTR)lpw; - nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "My Dialog", -1, lpwsz, 50); - lpw += nchar; - - //----------------------- - // Define an OK button. - //----------------------- - lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary - lpdit = (LPDLGITEMTEMPLATE)lpw; - lpdit->x = 10; lpdit->y = 70; - lpdit->cx = 80; lpdit->cy = 20; - lpdit->id = IDOK; // OK button identifier - lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON; - - lpw = (LPWORD)(lpdit + 1); - *lpw++ = 0xFFFF; - *lpw++ = 0x0080; // Button class - - lpwsz = (LPWSTR)lpw; - nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "OK", -1, lpwsz, 50); - lpw += nchar; - lpw = lpwAlign(lpw); // Align creation data on DWORD boundary - *lpw++ = 0; // No creation data - - //----------------------- - // Define a Help button. - //----------------------- - lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary - lpdit = (LPDLGITEMTEMPLATE)lpw; - lpdit->x = 55; lpdit->y = 10; - lpdit->cx = 40; lpdit->cy = 20; - lpdit->id = ID_HELP; // Help button identifier - lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON; - - lpw = (LPWORD)(lpdit + 1); - *lpw++ = 0xFFFF; - *lpw++ = 0x0080; // Button class atom - - lpwsz = (LPWSTR)lpw; - nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "Help", -1, lpwsz, 50); - lpw += nchar; - lpw = lpwAlign(lpw); // Align creation data on DWORD boundary - *lpw++ = 0; // No creation data - - //----------------------- - // Define a static text control. - //----------------------- - lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary - lpdit = (LPDLGITEMTEMPLATE)lpw; - lpdit->x = 10; lpdit->y = 10; - lpdit->cx = 40; lpdit->cy = 20; - lpdit->id = ID_TEXT; // Text identifier - lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT; - - lpw = (LPWORD)(lpdit + 1); - *lpw++ = 0xFFFF; - *lpw++ = 0x0082; // Static class - - for (lpwsz = (LPWSTR)lpw; *lpwsz++ = (WCHAR)*lpszMessage++;); - lpw = (LPWORD)lpwsz; - lpw = lpwAlign(lpw); // Align creation data on DWORD boundary - *lpw++ = 0; // No creation data - - GlobalUnlock(hgbl); - ret = DialogBoxIndirect(hinst, - (LPDLGTEMPLATE)hgbl, - hwndOwner, - (DLGPROC)DialogProc); - GlobalFree(hgbl); - return ret; -} -#endif - void AddControl(int x, int y, int w, int h, int type, int style, int n) { int i; @@ -558,95 +574,181 @@ void AddControl(int x, int y, int w, int h, int type, int style, int n) void AddOption(int x, int y, Control type, int i) { + int extra, num = ES_NUMBER; switch(type) { + case Spin+100: + num = 0; // needs text control for accepting negative numbers + case Slider: case Spin: AddControl(x, y+1, 95, 9, 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, i); - AddControl(x+95, y, 50, 11, 0x0081, ES_AUTOHSCROLL | ES_NUMBER | WS_BORDER | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i+1); + AddControl(x+95, y, 50, 11, 0x0081, ES_AUTOHSCROLL | num | WS_BORDER | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i+1); break; -// case TextBox: + case TextBox: + extra = 13*activeList[layoutList[i/2]].min; // when extra high, left-align and put description text above it + AddControl(x+(extra?50:0), y+1, 95, 9, 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, i); + AddControl(x+(extra?50:95), y+(extra?13:0), extra?105:200, 11+(extra?extra-13:0), 0x0081, ES_AUTOHSCROLL | WS_BORDER | WS_VISIBLE | + WS_CHILD | WS_TABSTOP | (extra ? ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL :0), i+1); + break; + case ListBox: AddControl(x, y+1, 95, 9, 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, i); - AddControl(x+95, y, 190, 11, 0x0081, ES_AUTOHSCROLL | WS_BORDER | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i+1); + extra = 13*activeList[layoutList[i/2]].min; + AddControl(x, y+13, 105, 11+extra-13, 0x0083, LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_BORDER | LBS_NOTIFY | + WS_VISIBLE | WS_CHILD | WS_TABSTOP, i+1); + break; + case Label: + extra = activeList[layoutList[i/2]].value; + AddControl(x+extra, y+1, 290-extra, 9, 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i); break; - case TextBox: // For now all text edits get a browse button, as long as -file and -path options are not yet implemented case FileName: case PathName: AddControl(x, y+1, 95, 9, 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, i); AddControl(x+95, y, 180, 11, 0x0081, ES_AUTOHSCROLL | WS_BORDER | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i+1); AddControl(x+275, y, 20, 12, 0x0080, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i-2); - layoutList[i/2-1] = -2; + layoutList[i/2-1] = -2 - (type == PathName); break; case CheckBox: AddControl(x, y, 145, 11, 0x0080, BS_AUTOCHECKBOX | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i); break; case ComboBox: AddControl(x, y+1, 95, 9, 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, i); - AddControl(x+95, y-1, 50, 500, 0x0085, CBS_AUTOHSCROLL | CBS_DROPDOWN | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i+1); + AddControl(x+95, y-1, !activeCps && x<10 ? 120 : 50, 500, 0x0085, + CBS_AUTOHSCROLL | CBS_DROPDOWN | WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_VSCROLL, i+1); break; case Button: - AddControl(x, y, 40, 15, 0x0080, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD, i); + case ResetButton: + case SaveButton: + AddControl(x-2, y, 65, 13, 0x0080, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | WS_TABSTOP, i); + case Message: + default: break; } - + } void -CreateDialogTemplate(int *layoutList, int nr, ChessProgramState *cps) +CreateDialogTemplate(int *layoutList, int nr, Option *optionList) { - int i, j, x=1, y=0, buttonRows, breakPoint = -1, k=0; + int i, ii, j, x=1, y=0, maxY=0, buttonRows, breakPoint = 1000, k=0; template.header.cdit = 0; template.header.cx = 307; - buttonRows = (buttons + 1 + 3)/4; // 4 per row, ronded up - if(nr > 50) { + buttonRows = (buttons + 1 + 3)/4; // 4 per row, rounded up + if(nr > 50) { breakPoint = (nr+2*buttonRows+1)/2 & ~1; template.header.cx = 625; } - for(i=0; i>1)-2, 301, 13*(boxList[k+1]-boxList[k]>>1)+8, + AddControl(x+2, y+13*(i>>1)-2, 301, 13*(boxList[k+1]-boxList[k]>>1)+8, 0x0082, WS_VISIBLE | WS_CHILD | SS_BLACKFRAME, 2400); - AddControl(x+60, y+13*(i>>1)-6, 10*groupNameList[k]/3, 10, - 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, 2*(i+MAX_OPTIONS)); + AddControl(x+60, y+13*(i>>1)-6, 10*groupNameList[k]/3, 10, + 0x0082, SS_ENDELLIPSIS | WS_VISIBLE | WS_CHILD, 2*(ii+MAX_OPTIONS)); } j = layoutList[i]; - if(j >= 0) - AddOption(x+155-150*(i&1), y+13*(i>>1)+5, cps->option[j].type, 2*i); - if(k < groups && i+1 == boxList[k+1]) { + if(j >= 0) { + int neg = (optionList[j].type == Spin && optionList[j].min < 0 ? 100 : 0); // flags spin with negative range + AddOption(x+155-150*(i&1), y+13*(i>>1)+5, optionList[j].type + neg, 2*i); + // listboxes have the special power to adjust the width of the column they are in + if(optionList[j].type == ListBox) x -= optionList[j].value, template.header.cx -= optionList[j].value; + } + if(k < groups && ii+1 == boxList[k+1]) { k += 2; y += 4; } - if(i+1 == breakPoint) { x += 318; y = -13*(breakPoint>>1); } + if(ii+1 >= breakPoint && breaks[ii+1>>1] >= 0) { x += 318; maxY = y+13*(ii+1>>1)+5; y = -13*(ii+1>>1); breakPoint = 1000; } } // add butons at the bottom of dialog window y += 13*(nr>>1)+5; - AddControl(x+275, y+18*(buttonRows-1), 25, 15, 0x0080, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD, IDOK-2000); - AddControl(x+235, y+18*(buttonRows-1), 35, 15, 0x0080, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD, IDCANCEL-2000); for(i=0; i 300, 0) { - template.header.cx = 295; - template.header.cy = 300; - template.header.style |= WS_VSCROLL; - } } -void +void EngineOptionsPopup(HWND hwnd, ChessProgramState *cps) { FARPROC lpProc = MakeProcInstance( (FARPROC) SettingsProc, hInst ); - activeCps = cps; - DesignOptionDialog(cps); - CreateDialogTemplate(layoutList, layout, cps); + activeCps = cps; activeList = cps->option; + snprintf(title, MSG_SIZ, _("%s Engine Settings (%s)"), T_(cps->which), cps->tidy); + DesignOptionDialog(cps->nrOptions, cps->option); + CreateDialogTemplate(layoutList, layout, cps->option); + + + DialogBoxIndirect( hInst, &template.header, hwnd, (DLGPROC)lpProc ); + + FreeProcInstance(lpProc); + + return; +} + +void +RefreshSettingsDialog (ChessProgramState *cps, int val) +{ + int isUp = (settingsDlg != NULL); + if(val == 1) { + if(activeCps == cps && isUp) SetOptionValues(settingsDlg, cps, activeList); + return; + } + if(settingsDlg) EndDialog(settingsDlg, 1); + comboCallback = NULL; activeCps = NULL; settingsDlg = NULL; + if(val == 3 || isUp) EngineOptionsPopup(hwndMain, cps); +} + +int EnterGroup P((HWND hDlg)); + +static int engineNr, selected; +int InstallOK() +{ + if(selected >= 0) { ASSIGN(engineLine, engineList[selected]); } + if(engineLine[0] == '#') { DisplayError(_("Select single engine from the group"), 0); return 0; } + if(isUCCI) isUCI = 2; + if(!engineNr) Load(&first, 0); else Load(&second, 1); + return 1; +} + +Option installOptions[] = { +// { 0, 0, 0, NULL, (void*) &engineLine, (char*) engineMnemonic, engineList, ComboBox, N_("Select engine from list:") }, + { 195, 14, 0, NULL, (void*) &EnterGroup, (char*) &selected, engineMnemonic, ListBox, N_("Select engine from list:") }, + { 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_("or specify one below:") }, + { 0, 0, 0, NULL, (void*) &nickName, NULL, NULL, TextBox, N_("Nickname (optional):") }, + { 0, 0, 0, NULL, (void*) &useNick, NULL, NULL, CheckBox, N_("Use nickname in PGN tag") }, + { 0, 0, 32+3, NULL, (void*) &engineName, NULL, NULL, FileName, N_("Engine (.exe or .jar):") }, + { 0, 0, 0, NULL, (void*) ¶ms, NULL, NULL, TextBox, N_("command-line parameters:") }, + { 0, 0, 0, NULL, (void*) &wbOptions, NULL, NULL, TextBox, N_("Special WinBoard options:") }, + { 0, 0, 0, NULL, (void*) &engineDir, NULL, NULL, PathName, N_("directory:") }, + { 95, 0, 0, NULL, NULL, NULL, NULL, Label, N_("(Directory will be derived from engine path when left empty)") }, + { 0, 0, 0, NULL, (void*) &addToList, NULL, NULL, CheckBox, N_("Add this engine to the list") }, + { 0, 0, 0, NULL, (void*) &hasBook, NULL, NULL, CheckBox, N_("Must not use GUI book") }, + { 0, 0, 0, NULL, (void*) &storeVariant, NULL, NULL, CheckBox, N_("Force current variant with this engine") }, + { 0, 0, 0, NULL, (void*) &isUCI, NULL, NULL, CheckBox, N_("UCI") }, + { 0, 0, 0, NULL, (void*) &v1, NULL, NULL, CheckBox, N_("WB protocol v1 (skip waiting for features)") }, + { 0, 0, 0, NULL, (void*) &isUCCI, NULL, NULL, CheckBox, N_("UCCI/USI/Arena960 (through /uxiAdapter)") }, + { 0, 1, 0, NULL, (void*) &InstallOK, "", NULL, EndMark , "" } +}; + +void +GenericPopup(HWND hwnd, Option *optionList) +{ + FARPROC lpProc = MakeProcInstance( (FARPROC) SettingsProc, hInst ); + int n=0; + + while(optionList[n].type != EndMark) n++; + activeCps = NULL; activeList = optionList; + DesignOptionDialog(n, optionList); + CreateDialogTemplate(layoutList, layout, optionList); DialogBoxIndirect( hInst, &template.header, hwnd, (DLGPROC)lpProc ); @@ -655,4 +757,274 @@ EngineOptionsPopup(HWND hwnd, ChessProgramState *cps) return; } +int +EnterGroup(HWND hDlg) +{ + char buf[MSG_SIZ]; + HANDLE hwndCombo = GetDlgItem(hDlg, 2001+2*1); + int i = SendDlgItemMessage(hDlg, 2001+2*1, LB_GETCURSEL, 0, 0); + if(i == 0) buf[0] = NULLCHAR; // back to top level + else if(engineList[i][0] == '#') safeStrCpy(buf, engineList[i], MSG_SIZ); // group header, open group + else { + ASSIGN(engineLine, engineList[i]); + if(isUCCI) isUCI = 2; + if(!engineNr) Load(&first, 0); else Load(&second, 1); + EndDialog( hDlg, 0 ); + return 0; // normal line, select engine + } + installOptions[0].max = NamesToList(firstChessProgramNames, engineList, engineMnemonic, buf); // replace list by only the group contents + SendMessage(hwndCombo, LB_RESETCONTENT, 0, 0); + SendMessage(hwndCombo, LB_ADDSTRING, 0, (LPARAM) buf); + for(i=1; i= 0) { ASSIGN(engineLine, engineList[selected]); } + if(engineLine[0] == '#') { DisplayError(_("Select single theme from the group"), 0); return 0; } + LoadTheme(); + return 1; +} + +Option themeOptions[] = { + { 195, 14, 0, NULL, (void*) &PickTheme, (char*) &selected, engineMnemonic, ListBox, N_("Select theme from list:") }, + { 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_("or specify new theme below:") }, + { 0, 0, 0, NULL, (void*) &nickName, NULL, NULL, TextBox, N_("Theme name:") }, + { 0, 0, 0, NULL, (void*) &appData.useBitmaps, NULL, NULL, CheckBox, N_("Use board textures") }, + { 0, 0, 32+0, NULL, (void*) &appData.liteBackTextureFile, NULL, NULL, FileName, N_("Light-square texture:") }, + { 0, 0, 32+0, NULL, (void*) &appData.darkBackTextureFile, NULL, NULL, FileName, N_("Dark-square texture:") }, + { 0, 0, 3, NULL, (void*) &appData.darkBackTextureMode, "", NULL, Spin, N_("Dark reorientation mode:") }, + { 0, 0, 3, NULL, (void*) &appData.liteBackTextureMode, "", NULL, Spin, N_("Light reorientation mode:") }, + { 0, 0, 0, NULL, (void*) &appData.useBorder, NULL, NULL, CheckBox, N_("Draw border around board") }, + { 0, 0, 32+0, NULL, (void*) &appData.border, NULL, NULL, FileName, N_("Optional border bitmap:") }, + { 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_(" Beware: a specified piece font will prevail over piece bitmaps") }, + { 0, 0, 0, NULL, (void*) &appData.pieceDirectory, NULL, NULL, PathName, N_("Directory with piece bitmaps:") }, + { 0, 0, 0, NULL, (void*) &appData.useFont, NULL, NULL, CheckBox, N_("Use piece font") }, + { 0, 50, 150, NULL, (void*) &appData.fontPieceSize, "", NULL, Spin, N_("Font size (%):") }, + { 0, 0, 0, NULL, (void*) &appData.renderPiecesWithFont, NULL, NULL, TextBox, N_("Font name:") }, + { 0, 0, 0, NULL, (void*) &appData.fontToPieceTable, NULL, NULL, TextBox, N_("Font piece to char:") }, +// { 0, 0, 0, NULL, (void*) &DeleteTheme, NULL, NULL, Button, N_("Up") }, +// { 0, 0, 0, NULL, (void*) &DeleteTheme, NULL, NULL, Button, N_("Down") }, + { 0, 0, 0, NULL, (void*) &DeleteTheme, NULL, NULL, Button, N_("Delete Theme") }, + { 0, 1, 0, NULL, (void*) &ThemeOK, "", NULL, EndMark , "" } +}; + +void +DeleteTheme (HWND hDlg) +{ + char *p, *q; + int i, selected = SendDlgItemMessage(hDlg, 2001+2*1, LB_GETCURSEL, 0, 0); + HANDLE hwndCombo = GetDlgItem(hDlg, 2001+2*1); + if(selected < 0) return; + if(p = strstr(appData.themeNames, engineList[selected])) { + if(q = strchr(p, '\n')) strcpy(p, q+1); + } + themeOptions[0].max = NamesToList(appData.themeNames, engineList, engineMnemonic, ""); // replace list by only the group contents + SendMessage(hwndCombo, LB_RESETCONTENT, 0, 0); + SendMessage(hwndCombo, LB_ADDSTRING, 0, (LPARAM) ""); + for(i=1; i