Allow grouping of engines in engine list
authorH.G. Muller <h.g.muller@hccnet.nl>
Wed, 8 Feb 2012 22:25:31 +0000 (23:25 +0100)
committerH.G. Muller <h.g.muller@hccnet.nl>
Wed, 8 Feb 2012 22:28:06 +0000 (23:28 +0100)
Lines "# GROUPNAME" and "# end" enclose a group in -firstChessProgramNames.

backend.c
backend.h
winboard/wsettings.c
xoptions.c

index 92a228d..a4564d8 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -9808,7 +9808,7 @@ Substitute (char *participants, int expunge)
        q = r; while(*q) nPlayers += (*q++ == '\n');
        p = buf; while(*r && (*p = *r++) != '\n') p++;
        *p = NULLCHAR;
        q = r; while(*q) nPlayers += (*q++ == '\n');
        p = buf; while(*r && (*p = *r++) != '\n') p++;
        *p = NULLCHAR;
-       NamesToList(firstChessProgramNames, command, mnemonic);
+       NamesToList(firstChessProgramNames, command, mnemonic, "all");
        for(i=1; mnemonic[i]; i++) if(!strcmp(buf, mnemonic[i])) break;
        if(mnemonic[i]) { // The substitute is valid
            FILE *f;
        for(i=1; mnemonic[i]; i++) if(!strcmp(buf, mnemonic[i])) break;
        if(mnemonic[i]) { // The substitute is valid
            FILE *f;
@@ -9877,19 +9877,28 @@ CreateTourney (char *name)
        return 1;
 }
 
        return 1;
 }
 
-void
-NamesToList (char *names, char **engineList, char **engineMnemonic)
+int
+NamesToList (char *names, char **engineList, char **engineMnemonic, char *group)
 {
     char buf[MSG_SIZ], *p, *q;
 {
     char buf[MSG_SIZ], *p, *q;
-    int i=1;
-    while(*names) {
-       p = names; q = buf;
+    int i=1, header, skip, all = !strcmp(group, "all"), depth = 0;
+    skip = !all && group[0]; // if group requested, we start in skip mode
+    for(;*names && depth >= 0 && i < MAXENGINES-1; names = p) {
+       p = names; q = buf; header = 0;
        while(*p && *p != '\n') *q++ = *p++;
        *q = 0;
        while(*p && *p != '\n') *q++ = *p++;
        *q = 0;
+       if(*p == '\n') p++;
+       if(buf[0] == '#') {
+           if(strstr(buf, "# end") == buf) { depth--; continue; } // leave group, and suppress printing label
+           depth++; // we must be entering a new group
+           if(all) continue; // suppress printing group headers when complete list requested
+           header = 1;
+           if(skip && !strcmp(group, buf)) { depth = 0; skip = FALSE; } // start when we reach requested group
+       }
+       if(depth != header && !all || skip) continue; // skip contents of group (but print first-level header)
        if(engineList[i]) free(engineList[i]);
        engineList[i] = strdup(buf);
        if(engineList[i]) free(engineList[i]);
        engineList[i] = strdup(buf);
-       if(*p == '\n') p++;
-       TidyProgramName(engineList[i], "localhost", buf);
+       if(buf[0] != '#') TidyProgramName(engineList[i], "localhost", buf); // group headers not tidied
        if(engineMnemonic[i]) free(engineMnemonic[i]);
        if((q = strstr(engineList[i]+2, "variant")) && q[-2]== ' ' && (q[-1]=='/' || q[-1]=='-') && (q[7]==' ' || q[7]=='=')) {
            strcat(buf, " (");
        if(engineMnemonic[i]) free(engineMnemonic[i]);
        if((q = strstr(engineList[i]+2, "variant")) && q[-2]== ' ' && (q[-1]=='/' || q[-1]=='-') && (q[7]==' ' || q[7]=='=')) {
            strcat(buf, " (");
@@ -9897,10 +9906,10 @@ NamesToList (char *names, char **engineList, char **engineMnemonic)
            strcat(buf, ")");
        }
        engineMnemonic[i] = strdup(buf);
            strcat(buf, ")");
        }
        engineMnemonic[i] = strdup(buf);
-       names = p; i++;
-      if(i > MAXENGINES - 2) break;
+       i++;
     }
     engineList[i] = engineMnemonic[i] = NULL;
     }
     engineList[i] = engineMnemonic[i] = NULL;
+    return i;
 }
 
 // following implemented as macro to avoid type limitations
 }
 
 // following implemented as macro to avoid type limitations
@@ -9952,7 +9961,7 @@ RecentEngineEvent (int nr)
     int n;
 //    SwapEngines(1); // bump first to second
 //    ReplaceEngine(&second, 1); // and load it there
     int n;
 //    SwapEngines(1); // bump first to second
 //    ReplaceEngine(&second, 1); // and load it there
-    NamesToList(firstChessProgramNames, command, mnemonic); // get mnemonics of installed engines
+    NamesToList(firstChessProgramNames, command, mnemonic, "all"); // get mnemonics of installed engines
     n = SetPlayer(nr, recentEngines); // select new (using original menu order!)
     if(mnemonic[n]) { // if somehow the engine with the selected nickname is no longer found in the list, we skip
        ReplaceEngine(&first, 0);
     n = SetPlayer(nr, recentEngines); // select new (using original menu order!)
     if(mnemonic[n]) { // if somehow the engine with the selected nickname is no longer found in the list, we skip
        ReplaceEngine(&first, 0);
@@ -10063,7 +10072,7 @@ NextTourneyGame (int nr, int *swapColors)
     if(first.pr != NoProc && second.pr != NoProc) return 1; // engines already loaded
 
     // redefine engines, engine dir, etc.
     if(first.pr != NoProc && second.pr != NoProc) return 1; // engines already loaded
 
     // redefine engines, engine dir, etc.
-    NamesToList(firstChessProgramNames, command, mnemonic); // get mnemonics of installed engines
+    NamesToList(firstChessProgramNames, command, mnemonic, "all"); // get mnemonics of installed engines
     if(first.pr == NoProc || nr < 0) {
       SetPlayer(whitePlayer, appData.participants); // find white player amongst it, and parse its engine line
       InitEngine(&first, 0);  // initialize ChessProgramStates based on new settings.
     if(first.pr == NoProc || nr < 0) {
       SetPlayer(whitePlayer, appData.participants); // find white player amongst it, and parse its engine line
       InitEngine(&first, 0);  // initialize ChessProgramStates based on new settings.
index ce09c98..561a0b3 100644 (file)
--- a/backend.h
+++ b/backend.h
@@ -304,7 +304,7 @@ void InitSearch P((void));
 int GameContainsPosition P((FILE *f, ListGame *lg));
 void GLT_TagsToList P(( char * tags ));
 void GLT_ParseList P((void));
 int GameContainsPosition P((FILE *f, ListGame *lg));
 void GLT_TagsToList P(( char * tags ));
 void GLT_ParseList P((void));
-void NamesToList P((char *name, char **engines, char **mnemonics));
+int NamesToList P((char *name, char **engines, char **mnemonics, char *group));
 int CreateTourney P((char *name));
 char *MakeName P((char *templ));
 void SwapEngines P((int n));
 int CreateTourney P((char *name));
 char *MakeName P((char *templ));
 void SwapEngines P((int n));
index 83472f5..10bb427 100644 (file)
@@ -366,7 +366,7 @@ GetOptionValues(HWND hDlg, ChessProgramState *cps, Option *optionList)
                if(!cps) {\r
                    char *p;\r
                    p = (optionList[j].type != FileName ? strdup(text) : InterpretFileName(text, homeDir)); // all files relative to homeDir!\r
                if(!cps) {\r
                    char *p;\r
                    p = (optionList[j].type != FileName ? strdup(text) : InterpretFileName(text, homeDir)); // all files relative to homeDir!\r
-                   FREE(*(char**)optionList[j].target); *(char**)optionList[j].target = p;
+                   FREE(*(char**)optionList[j].target); *(char**)optionList[j].target = p;\r
                    free(text); text = p;\r
                    while(*p++ = *text++) if(p[-1] == '\r') p--; // crush CR\r
                    break;\r
                    free(text); text = p;\r
                    while(*p++ = *text++) if(p[-1] == '\r') p--; // crush CR\r
                    break;\r
@@ -480,6 +480,7 @@ LRESULT CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa
                }\r
                if(j < 0) break;\r
                if(comboCallback && activeList[j].type == ComboBox && HIWORD(wParam) == CBN_SELCHANGE) {\r
                }\r
                if(j < 0) break;\r
                if(comboCallback && activeList[j].type == ComboBox && HIWORD(wParam) == CBN_SELCHANGE) {\r
+                   if(j > 5) break; // Yegh! Must solve problem with more than one ombobox in dialog\r
                    (*comboCallback)(hDlg);\r
                    break;\r
                } else\r
                    (*comboCallback)(hDlg);\r
                    break;\r
                } else\r
@@ -628,6 +629,7 @@ EngineOptionsPopup(HWND hwnd, ChessProgramState *cps)
 \r
 int InstallOK()\r
 {\r
 \r
 int InstallOK()\r
 {\r
+    if(engineLine[0] == '#') { DisplayError(_("Select single engine from the group"), 0); return 0; }\r
     if(isUCCI) isUCI = 2;\r
     if(engineChoice[0] == engineNr[0][0])  Load(&first, 0); else Load(&second, 1);\r
     return 1;\r
     if(isUCCI) isUCI = 2;\r
     if(engineChoice[0] == engineNr[0][0])  Load(&first, 0); else Load(&second, 1);\r
     return 1;\r
@@ -670,10 +672,27 @@ GenericPopup(HWND hwnd, Option *optionList)
     return;\r
 }\r
 \r
     return;\r
 }\r
 \r
-void LoadEnginePopUp(HWND hwnd)\r
+int\r
+EnterGroup(HWND hDlg)\r
 {\r
 {\r
-    int n=0;\r
+    char buf[MSG_SIZ];\r
+    HANDLE hwndCombo = GetDlgItem(hDlg, 2001+2*1);\r
+    int i = ComboBox_GetCurSel(hwndCombo);\r
+    if(i == 0) buf[0] = NULLCHAR; // back to top level\r
+    else if(engineList[i][0] == '#') safeStrCpy(buf, engineList[i], MSG_SIZ); // group header, open group\r
+    else return 0; // normal line, select engine\r
+    installOptions[0].max = NamesToList(firstChessProgramNames, engineList, engineMnemonic, buf); // replace list by only the group contents\r
+    SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
+    SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
+    for(i=1; i<installOptions[0].max; i++) {\r
+           SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) engineMnemonic[i]);\r
+    }\r
+    SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) 0, (LPARAM) buf);\r
+    return 0;\r
+}\r
 \r
 \r
+void LoadEnginePopUp(HWND hwnd)\r
+{\r
     isUCI = isUCCI = storeVariant = v1 = useNick = FALSE; addToList = hasBook = TRUE; // defaults\r
     if(engineDir)    free(engineDir);    engineDir = strdup("");\r
     if(params)       free(params);       params = strdup("");\r
     isUCI = isUCCI = storeVariant = v1 = useNick = FALSE; addToList = hasBook = TRUE; // defaults\r
     if(engineDir)    free(engineDir);    engineDir = strdup("");\r
     if(params)       free(params);       params = strdup("");\r
@@ -681,9 +700,9 @@ void LoadEnginePopUp(HWND hwnd)
     if(engineChoice) free(engineChoice); engineChoice = strdup(engineNr[0]);\r
     if(engineLine)   free(engineLine);   engineLine = strdup("");\r
     if(engineName)   free(engineName);   engineName = strdup("");\r
     if(engineChoice) free(engineChoice); engineChoice = strdup(engineNr[0]);\r
     if(engineLine)   free(engineLine);   engineLine = strdup("");\r
     if(engineName)   free(engineName);   engineName = strdup("");\r
-    NamesToList(firstChessProgramNames, engineList, engineMnemonic);\r
-    while(engineList[n]) n++; installOptions[0].max = n;\r
+    installOptions[0].max = NamesToList(firstChessProgramNames, engineList, engineMnemonic, ""); // only top level\r
     snprintf(title, MSG_SIZ, _("Load Engine"));\r
     snprintf(title, MSG_SIZ, _("Load Engine"));\r
+    comboCallback = &EnterGroup;\r
 \r
     GenericPopup(hwnd, installOptions);\r
 }\r
 \r
     GenericPopup(hwnd, installOptions);\r
 }\r
@@ -751,7 +770,7 @@ Option tourneyOptions[] = {
   { 0,  0,          4, NULL, (void*) &tfName, "", NULL, FileName, N_("Tournament file:") },\r
   { 30, 0,          0, NULL, NULL, NULL, NULL, Label, N_("If you specify an existing file, the rest of this dialog will be ignored.") },\r
   { 30, 0,          0, NULL, NULL, NULL, NULL, Label, N_("Otherwise, the file will be created, with the settings you specify below:") },\r
   { 0,  0,          4, NULL, (void*) &tfName, "", NULL, FileName, N_("Tournament file:") },\r
   { 30, 0,          0, NULL, NULL, NULL, NULL, Label, N_("If you specify an existing file, the rest of this dialog will be ignored.") },\r
   { 30, 0,          0, NULL, NULL, NULL, NULL, Label, N_("Otherwise, the file will be created, with the settings you specify below:") },\r
-  { 0,  1,          0, NULL, (void*) &engineChoice, (char*) (engineMnemonic+1), (engineMnemonic+1), ComboBox, N_("Select Engine:") },\r
+  { 0,  1,          0, NULL, (void*) &engineChoice, (char*) engineMnemonic, engineMnemonic, ComboBox, N_("Select Engine:") },\r
   { 0xD, 7,         0, NULL, (void*) &appData.participants, "", NULL, TextBox, N_("Tourney participants:") },\r
   { 0,  0,          0, NULL, (void*) &swiss, "", NULL, CheckBox, N_("Use Swiss pairing engine (cycles = rounds)") },\r
   { 0,  0,         10, NULL, (void*) &appData.tourneyType, "", NULL, Spin, N_("Tourney type (0=RR, 1=gauntlet):") },\r
   { 0xD, 7,         0, NULL, (void*) &appData.participants, "", NULL, TextBox, N_("Tourney participants:") },\r
   { 0,  0,          0, NULL, (void*) &swiss, "", NULL, CheckBox, N_("Use Swiss pairing engine (cycles = rounds)") },\r
   { 0,  0,         10, NULL, (void*) &appData.tourneyType, "", NULL, Spin, N_("Tourney type (0=RR, 1=gauntlet):") },\r
@@ -779,25 +798,34 @@ Option tourneyOptions[] = {
 \r
 int AddToTourney(HWND hDlg)\r
 {\r
 \r
 int AddToTourney(HWND hDlg)\r
 {\r
-    char buf[MSG_SIZ];\r
-//    GetDlgItemText( hDlg, 2001+2*7, buf, MSG_SIZ-3 ); // this gives the previous selection !!!\r
-//    strncat(buf, "\r\n", MSG_SIZ);\r
-    int i = ComboBox_GetCurSel(GetDlgItem(hDlg, 2001+2*7));\r
-    snprintf(buf, MSG_SIZ, "%s\r\n", engineMnemonic[i+1]);\r
+  char buf[MSG_SIZ];\r
+  HANDLE hwndCombo = GetDlgItem(hDlg, 2001+2*7);\r
+  int i = ComboBox_GetCurSel(hwndCombo);\r
+  if(i == 0) buf[0] = NULLCHAR; // back to top level\r
+  else if(engineList[i][0] == '#') safeStrCpy(buf, engineList[i], MSG_SIZ); // group header, open group\r
+  else { // normal line, select engine\r
+    snprintf(buf, MSG_SIZ, "%s\r\n", engineMnemonic[i]);\r
     SendMessage( GetDlgItem(hDlg, 2001+2*9), EM_SETSEL, 99999, 99999 );\r
     SendMessage( GetDlgItem(hDlg, 2001+2*9), EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) buf );\r
     return 0;\r
     SendMessage( GetDlgItem(hDlg, 2001+2*9), EM_SETSEL, 99999, 99999 );\r
     SendMessage( GetDlgItem(hDlg, 2001+2*9), EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) buf );\r
     return 0;\r
+  }\r
+  installOptions[0].max = NamesToList(firstChessProgramNames, engineList, engineMnemonic, buf); // replace list by only the group contents\r
+  SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
+  SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
+  for(i=1; i<installOptions[0].max; i++) {\r
+    SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) engineMnemonic[i]);\r
+  }\r
+  SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) 0, (LPARAM) buf);\r
+  return 0;\r
 }\r
 \r
 void TourneyPopup(HWND hwnd)\r
 {\r
 }\r
 \r
 void TourneyPopup(HWND hwnd)\r
 {\r
-    int n=0;\r
-\r
-    NamesToList(firstChessProgramNames, engineList, engineMnemonic);\r
+    int n = NamesToList(firstChessProgramNames, engineList, engineMnemonic, "");\r
     comboCallback = &AddToTourney;\r
     autoinc = appData.loadGameIndex < 0 || appData.loadPositionIndex < 0;\r
     twice = FALSE; swiss = appData.tourneyType < 0;\r
     comboCallback = &AddToTourney;\r
     autoinc = appData.loadGameIndex < 0 || appData.loadPositionIndex < 0;\r
     twice = FALSE; swiss = appData.tourneyType < 0;\r
-    while(engineList[n]) n++; tourneyOptions[3].max = n-1;\r
+    tourneyOptions[3].max = n;\r
     snprintf(title, MSG_SIZ, _("Tournament and Match Options"));\r
     ASSIGN(tfName, appData.tourneyFile[0] ? appData.tourneyFile : MakeName(appData.defName));\r
 \r
     snprintf(title, MSG_SIZ, _("Tournament and Match Options"));\r
     ASSIGN(tfName, appData.tourneyFile[0] ? appData.tourneyFile : MakeName(appData.defName));\r
 \r
index 8b9b644..d3e2462 100644 (file)
@@ -1413,7 +1413,7 @@ OptionsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
 void
 MatchOptionsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
 {
 void
 MatchOptionsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
 {
-   NamesToList(firstChessProgramNames, engineList, engineMnemonic);
+   NamesToList(firstChessProgramNames, engineList, engineMnemonic, "all");
    comboCallback = &AddToTourney;
    matchOptions[5].min = -(appData.pairingEngine[0] != NULLCHAR); // with pairing engine, allow Swiss
    ASSIGN(tfName, appData.tourneyFile[0] ? appData.tourneyFile : MakeName(appData.defName));
    comboCallback = &AddToTourney;
    matchOptions[5].min = -(appData.pairingEngine[0] != NULLCHAR); // with pairing engine, allow Swiss
    ASSIGN(tfName, appData.tourneyFile[0] ? appData.tourneyFile : MakeName(appData.defName));
@@ -1742,7 +1742,7 @@ LoadEngineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
    if(engineDir)    free(engineDir);    engineDir = strdup("");
    if(nickName)     free(nickName);     nickName = strdup("");
    if(params)       free(params);       params = strdup("");
    if(engineDir)    free(engineDir);    engineDir = strdup("");
    if(nickName)     free(nickName);     nickName = strdup("");
    if(params)       free(params);       params = strdup("");
-   NamesToList(firstChessProgramNames, engineList, engineMnemonic);
+   NamesToList(firstChessProgramNames, engineList, engineMnemonic, "all");
    GenericPopUp(installOptions, _("Load engine"), 0);
 }
 
    GenericPopUp(installOptions, _("Load engine"), 0);
 }