1 /*
2 * dialogs.c -- platform-independent code for dialogs of XBoard
3 *
4 * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
5 * ------------------------------------------------------------------------
6 *
7 * GNU XBoard is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or (at
10 * your option) any later version.
11 *
12 * GNU XBoard is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see http://www.gnu.org/licenses/. *
19 *
20 *------------------------------------------------------------------------
21 ** See the file ChangeLog for a revision history. */
22
23 // [HGM] this file is the counterpart of woptions.c, containing xboard popup menus
24 // similar to those of WinBoard, to set the most common options interactively.
25
26 #include "config.h"
27
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <sys/types.h>
32
33 #if STDC_HEADERS
34 # include <stdlib.h>
35 # include <string.h>
36 #else /* not STDC_HEADERS */
37 extern char *getenv();
38 # if HAVE_STRING_H
39 # include <string.h>
40 # else /* not HAVE_STRING_H */
41 # include <strings.h>
42 # endif /* not HAVE_STRING_H */
43 #endif /* not STDC_HEADERS */
44
45 #if HAVE_UNISTD_H
46 # include <unistd.h>
47 #endif
48 #include <stdint.h>
49
50 #include "common.h"
51 #include "frontend.h"
52 #include "backend.h"
53 #include "xboard2.h"
54 #include "menus.h"
55 #include "dialogs.h"
56 #include "gettext.h"
57
58 #ifdef ENABLE_NLS
59 # define _(s) gettext (s)
60 # define N_(s) gettext_noop (s)
61 #else
62 # define _(s) (s)
63 # define N_(s) s
64 #endif
65
66
67 int values[MAX_OPTIONS];
68 ChessProgramState *currentCps;
69
70 //----------------------------Generic dialog --------------------------------------------
71
72 // cloned from Engine Settings dialog (and later merged with it)
73
74 char *marked[NrOfDialogs];
75 Boolean shellUp[NrOfDialogs];
76
77 void
MarkMenu(char * item,int dlgNr)78 MarkMenu (char *item, int dlgNr)
79 {
80 MarkMenuItem(marked[dlgNr] = item, True);
81 }
82
83 void
AddLine(Option * opt,char * s)84 AddLine (Option *opt, char *s)
85 {
86 AppendText(opt, s);
87 AppendText(opt, "\n");
88 }
89
90 //---------------------------------------------- Update dialog controls ------------------------------------
91
92 int
SetCurrentComboSelection(Option * opt)93 SetCurrentComboSelection (Option *opt)
94 {
95 int j;
96 if(!opt->textValue) opt->value = *(int*)opt->target; /* numeric */else {
97 for(j=0; opt->choice[j]; j++) // look up actual value in list of possible values, to get selection nr
98 if(*(char**)opt->target && !strcmp(*(char**)opt->target, ((char**)opt->textValue)[j])) break;
99 opt->value = j + (opt->choice[j] == NULL);
100 }
101 return opt->value;
102 }
103
104 void
GenericUpdate(Option * opts,int selected)105 GenericUpdate (Option *opts, int selected)
106 {
107 int i;
108 char buf[MSG_SIZ];
109
110 for(i=0; ; i++)
111 {
112 if(selected >= 0) { if(i < selected) continue; else if(i > selected) break; }
113 switch(opts[i].type)
114 {
115 case TextBox:
116 case FileName:
117 case PathName:
118 SetWidgetText(&opts[i], *(char**) opts[i].target, -1);
119 break;
120 case Spin:
121 sprintf(buf, "%d", *(int*) opts[i].target);
122 SetWidgetText(&opts[i], buf, -1);
123 break;
124 case Fractional:
125 sprintf(buf, "%4.2f", *(float*) opts[i].target);
126 SetWidgetText(&opts[i], buf, -1);
127 break;
128 case CheckBox:
129 SetWidgetState(&opts[i], *(Boolean*) opts[i].target);
130 break;
131 case ComboBox:
132 if(opts[i].min & COMBO_CALLBACK) break;
133 SetCurrentComboSelection(opts+i);
134 // TODO: actually display this (but it is never used that way...)
135 break;
136 case EndMark:
137 return;
138 default:
139 printf("GenericUpdate: unexpected case in switch.\n");
140 case ListBox:
141 case Button:
142 case SaveButton:
143 case Label:
144 case Break:
145 break;
146 }
147 }
148 }
149
150 //------------------------------------------- Read out dialog controls ------------------------------------
151
152 int
GenericReadout(Option * opts,int selected)153 GenericReadout (Option *opts, int selected)
154 {
155 int i, j, res=1;
156 char *val;
157 char buf[MSG_SIZ], **dest;
158 float x;
159 for(i=0; ; i++) { // send all options that had to be OK-ed to engine
160 if(selected >= 0) { if(i < selected) continue; else if(i > selected) break; }
161 switch(opts[i].type) {
162 case TextBox:
163 case FileName:
164 case PathName:
165 GetWidgetText(&opts[i], &val);
166 dest = currentCps ? &(opts[i].textValue) : (char**) opts[i].target;
167 if(*dest == NULL || strcmp(*dest, val)) {
168 if(currentCps) {
169 snprintf(buf, MSG_SIZ, "option %s=%s\n", opts[i].name, val);
170 SendToProgram(buf, currentCps);
171 } else {
172 if(*dest) free(*dest);
173 *dest = malloc(strlen(val)+1);
174 }
175 safeStrCpy(*dest, val, MSG_SIZ - (*dest - opts[i].name)); // copy text there
176 }
177 break;
178 case Spin:
179 case Fractional:
180 GetWidgetText(&opts[i], &val);
181 x = 0.0; // Initialise because sscanf() will fail if non-numeric text is entered
182 sscanf(val, "%f", &x);
183 if(x > opts[i].max) x = opts[i].max;
184 if(x < opts[i].min) x = opts[i].min;
185 if(opts[i].type == Fractional)
186 *(float*) opts[i].target = x; // engines never have float options!
187 else {
188 if(currentCps) {
189 if(opts[i].value != x) { // only to engine if changed
190 snprintf(buf, MSG_SIZ, "option %s=%.0f\n", opts[i].name, x);
191 SendToProgram(buf, currentCps);
192 }
193 } else *(int*) opts[i].target = x;
194 opts[i].value = x;
195 }
196 break;
197 case CheckBox:
198 j = 0;
199 GetWidgetState(&opts[i], &j);
200 if(opts[i].value != j) {
201 opts[i].value = j;
202 if(currentCps) {
203 snprintf(buf, MSG_SIZ, "option %s=%d\n", opts[i].name, j);
204 SendToProgram(buf, currentCps);
205 } else *(Boolean*) opts[i].target = j;
206 }
207 break;
208 case ComboBox:
209 if(opts[i].min & COMBO_CALLBACK) break;
210 if(!opts[i].textValue) { *(int*)opts[i].target = values[i]; break; } // numeric
211 val = ((char**)opts[i].textValue)[values[i]];
212 if(currentCps) {
213 if(opts[i].value == values[i]) break; // not changed
214 opts[i].value = values[i];
215 snprintf(buf, MSG_SIZ, "option %s=%s\n", opts[i].name, opts[i].choice[values[i]]);
216 SendToProgram(buf, currentCps);
217 } else if(val && (*(char**) opts[i].target == NULL || strcmp(*(char**) opts[i].target, val))) {
218 if(*(char**) opts[i].target) free(*(char**) opts[i].target);
219 *(char**) opts[i].target = strdup(val);
220 }
221 break;
222 case EndMark:
223 if(opts[i].target && selected != -2) // callback for implementing necessary actions on OK (like redraw)
224 res = ((OKCallback*) opts[i].target)(i);
225 break;
226 default:
227 printf("GenericReadout: unexpected case in switch.\n");
228 case ListBox:
229 case Button:
230 case SaveButton:
231 case Label:
232 case Break:
233 case Skip:
234 break;
235 }
236 if(opts[i].type == EndMark) break;
237 }
238 return res;
239 }
240
241 //------------------------------------------- Match Options ------------------------------------------------------
242
243 char *engineName, *engineChoice, *tfName;
244 char *engineList[MAXENGINES] = {" "}, *engineMnemonic[MAXENGINES];
245
246 static void AddToTourney P((int n, int sel));
247 static void CloneTourney P((void));
248 static void ReplaceParticipant P((void));
249 static void UpgradeParticipant P((void));
250 static void PseudoOK P((void));
251
252 static int
MatchOK(int n)253 MatchOK (int n)
254 {
255 ASSIGN(appData.participants, engineName);
256 if(!CreateTourney(tfName) || matchMode) return matchMode || !appData.participants[0];
257 PopDown(MasterDlg); // early popdown to prevent FreezeUI called through MatchEvent from causing XtGrab warning
258 MatchEvent(2); // start tourney
259 return FALSE; // no double PopDown!
260 }
261
262 static void
DoTimeControl(int n)263 DoTimeControl(int n)
264 {
265 TimeControlProc();
266 }
267
268 static void
DoCommonEngine(int n)269 DoCommonEngine(int n)
270 {
271 UciMenuProc();
272 }
273
274 static void
DoGeneral(int n)275 DoGeneral(int n)
276 {
277 OptionsProc();
278 }
279
280 #define PARTICIPANTS 6 /* This MUST be the number of the Option for &engineName!*/
281
282 static Option matchOptions[] = {
283 { 0, 0, 0, NULL, (void*) &tfName, ".trn", NULL, FileName, N_("Tournament file: ") },
284 { 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_("For concurrent playing of tourney with multiple XBoards:") },
285 { 0, 0, 0, NULL, (void*) &appData.roundSync, "", NULL, CheckBox, N_("Sync after round") },
286 { 0, 0, 0, NULL, (void*) &appData.cycleSync, "", NULL, CheckBox, N_("Sync after cycle") },
287 { 0, LR, 175, NULL, NULL, NULL, NULL, Label, N_("Tourney participants:") },
288 { 0, SAME_ROW|RR, 175, NULL, NULL, NULL, NULL, Label, N_("Select Engine:") },
289 { 200, T_VSCRL | T_FILL | T_WRAP,
290 175, NULL, (void*) &engineName, NULL, NULL, TextBox, "" },
291 { 200, SAME_ROW|RR,
292 175, NULL, (void*) engineMnemonic, (char*) &AddToTourney, NULL, ListBox, "" },
293 { 0, SAME_ROW, 0, NULL, NULL, NULL, NULL, Break, "" }, // to decouple alignment above and below boxes
294 //{ 0, COMBO_CALLBACK | NO_GETTEXT,
295 // 0, NULL, (void*) &AddToTourney, (char*) (engineMnemonic+1), (engineMnemonic+1), ComboBox, N_("Select Engine:") },
296 { 0, 0, 10, NULL, (void*) &appData.tourneyType, "", NULL, Spin, N_("Tourney type (0 = round-robin, 1 = gauntlet):") },
297 { 0, 1, 1000000000, NULL, (void*) &appData.tourneyCycles, "", NULL, Spin, N_("Number of tourney cycles (or Swiss rounds):") },
298 { 0, 1, 1000000000, NULL, (void*) &appData.defaultMatchGames, "", NULL, Spin, N_("Default Number of Games in Match (or Pairing):") },
299 { 0, 0, 1000000000, NULL, (void*) &appData.matchPause, "", NULL, Spin, N_("Pause between Match Games (msec):") },
300 { 0, 0, 0, NULL, (void*) &appData.saveGameFile, ".pgn .game", NULL, FileName, N_("Save Tourney Games on:") },
301 { 0, 0, 0, NULL, (void*) &appData.loadGameFile, ".pgn .game", NULL, FileName, N_("Game File with Opening Lines:") },
302 { 0, -2, 1000000000, NULL, (void*) &appData.loadGameIndex, "", NULL, Spin, N_("Game Number (-1 or -2 = Auto-Increment):") },
303 { 0, 0, 0, NULL, (void*) &appData.loadPositionFile, ".fen .epd .pos", NULL, FileName, N_("File with Start Positions:") },
304 { 0, -2, 1000000000, NULL, (void*) &appData.loadPositionIndex, "", NULL, Spin, N_("Position Number (-1 or -2 = Auto-Increment):") },
305 { 0, 0, 1000000000, NULL, (void*) &appData.rewindIndex, "", NULL, Spin, N_("Rewind Index after this many Games (0 = never):") },
306 { 0, 0, 0, NULL, (void*) &appData.defNoBook, "", NULL, CheckBox, N_("Disable own engine books by default") },
307 { 0, 0, 0, NULL, (void*) &DoTimeControl, NULL, NULL, Button, N_("Time Control") },
308 { 0, SAME_ROW, 0, NULL, (void*) &DoCommonEngine, NULL, NULL, Button, N_("Common Engine") },
309 { 0, SAME_ROW, 0, NULL, (void*) &DoGeneral, NULL, NULL, Button, N_("General Options") },
310 { 0, SAME_ROW, 0, NULL, (void*) &PseudoOK, NULL, NULL, Button, N_("Continue Later") },
311 { 0, 0, 0, NULL, (void*) &ReplaceParticipant, NULL, NULL, Button, N_("Replace Engine") },
312 { 0, SAME_ROW, 0, NULL, (void*) &UpgradeParticipant, NULL, NULL, Button, N_("Upgrade Engine") },
313 { 0, SAME_ROW, 0, NULL, (void*) &CloneTourney, NULL, NULL, Button, N_("Clone Tourney") },
314 { 0, SAME_ROW, 0, NULL, (void*) &MatchOK, "", NULL, EndMark , "" }
315 };
316
317 static void
ReplaceParticipant()318 ReplaceParticipant ()
319 {
320 GenericReadout(matchOptions, PARTICIPANTS);
321 Substitute(strdup(engineName), True);
322 }
323
324 static void
UpgradeParticipant()325 UpgradeParticipant ()
326 {
327 GenericReadout(matchOptions, PARTICIPANTS);
328 Substitute(strdup(engineName), False);
329 }
330
331 static void
PseudoOK()332 PseudoOK ()
333 {
334 if(matchMode) return;
335 GenericReadout(matchOptions, -2); // read all, but suppress calling of MatchOK
336 ASSIGN(appData.participants, engineName);
337 ASSIGN(appData.tourneyFile, tfName);
338 PopDown(MasterDlg); // early popdown to prevent FreezeUI called through MatchEvent from causing XtGrab warning
339 }
340
341 static void
CloneTourney()342 CloneTourney ()
343 {
344 FILE *f;
345 char *name;
346 GetWidgetText(matchOptions, &name);
347 if(name && name[0] && (f = fopen(name, "r")) ) {
348 char *saveSaveFile;
349 saveSaveFile = appData.saveGameFile; appData.saveGameFile = NULL; // this is a persistent option, protect from change
350 ParseArgsFromFile(f);
351 engineName = appData.participants; GenericUpdate(matchOptions, -1);
352 FREE(appData.saveGameFile); appData.saveGameFile = saveSaveFile;
353 } else DisplayError(_("First you must specify an existing tourney file to clone"), 0);
354 }
355
356 static void
AddToTourney(int n,int sel)357 AddToTourney (int n, int sel)
358 {
359 int nr;
360 char buf[MSG_SIZ];
361 if(sel < 1) buf[0] = NULLCHAR; // back to top level
362 else if(engineList[sel][0] == '#') safeStrCpy(buf, engineList[sel], MSG_SIZ); // group header, open group
363 else { // normal line, select engine
364 AddLine(&matchOptions[PARTICIPANTS], engineMnemonic[sel]);
365 return;
366 }
367 nr = NamesToList(firstChessProgramNames, engineList, engineMnemonic, buf); // replace list by only the group contents
368 ASSIGN(engineMnemonic[0], buf);
369 LoadListBox(&matchOptions[PARTICIPANTS+1], _("# no engines are installed"), -1, -1);
370 HighlightWithScroll(&matchOptions[PARTICIPANTS+1], 0, nr);
371 }
372
373 void
MatchOptionsProc()374 MatchOptionsProc ()
375 {
376 if(matchOptions[PARTICIPANTS+1].type != ListBox) {
377 DisplayError(_("Internal error: PARTICIPANTS set wrong"), 0);
378 return;
379 }
380 NamesToList(firstChessProgramNames, engineList, engineMnemonic, "");
381 matchOptions[9].min = -(appData.pairingEngine[0] != NULLCHAR); // with pairing engine, allow Swiss
382 ASSIGN(tfName, appData.tourneyFile[0] ? appData.tourneyFile : MakeName(appData.defName));
383 ASSIGN(engineName, appData.participants);
384 ASSIGN(engineMnemonic[0], "");
385 GenericPopUp(matchOptions, _("Tournament Options"), MasterDlg, BoardWindow, MODAL, 0);
386 }
387
388 // ------------------------------------------- General Options --------------------------------------------------
389
390 static int oldShow, oldBlind, oldPonder;
391
392 static int
GeneralOptionsOK(int n)393 GeneralOptionsOK (int n)
394 {
395 int newPonder = appData.ponderNextMove;
396 appData.ponderNextMove = oldPonder;
397 PonderNextMoveEvent(newPonder);
398 if(!appData.highlightLastMove) ClearHighlights(), ClearPremoveHighlights();
399 if(oldShow != appData.showCoords || oldBlind != appData.blindfold) DrawPosition(TRUE, NULL);
400 return 1;
401 }
402
403 static Option generalOptions[] = {
404 { 0, 0, 0, NULL, (void*) &appData.whitePOV, "", NULL, CheckBox, N_("Absolute Analysis Scores") },
405 { 0, 0, 0, NULL, (void*) &appData.sweepSelect, "", NULL, CheckBox, N_("Almost Always Queen (Detour Under-Promote)") },
406 { 0, 0, 0, NULL, (void*) &appData.animateDragging, "", NULL, CheckBox, N_("Animate Dragging") },
407 { 0, 0, 0, NULL, (void*) &appData.animate, "", NULL, CheckBox, N_("Animate Moving") },
408 { 0, 0, 0, NULL, (void*) &appData.autoCallFlag, "", NULL, CheckBox, N_("Auto Flag") },
409 { 0, 0, 0, NULL, (void*) &appData.autoFlipView, "", NULL, CheckBox, N_("Auto Flip View") },
410 { 0, 0, 0, NULL, (void*) &appData.blindfold, "", NULL, CheckBox, N_("Blindfold") },
411 /* TRANSLATORS: the drop menu is used to drop a piece, e.g. during bughouse or editing a position */
412 { 0, 0, 0, NULL, (void*) &appData.dropMenu, "", NULL, CheckBox, N_("Drop Menu") },
413 { 0, 0, 0, NULL, (void*) &appData.variations, "", NULL, CheckBox, N_("Enable Variation Trees") },
414 { 0, 0, 0, NULL, (void*) &appData.headers, "", NULL, CheckBox, N_("Headers in Engine Output Window") },
415 { 0, 0, 0, NULL, (void*) &appData.hideThinkingFromHuman, "", NULL, CheckBox, N_("Hide Thinking from Human") },
416 { 0, 0, 0, NULL, (void*) &appData.highlightLastMove, "", NULL, CheckBox, N_("Highlight Last Move") },
417 { 0, 0, 0, NULL, (void*) &appData.highlightMoveWithArrow, "", NULL, CheckBox, N_("Highlight with Arrow") },
418 { 0, 0, 0, NULL, (void*) &appData.oneClick, "", NULL, CheckBox, N_("One-Click Moving") },
419 { 0, 0, 0, NULL, (void*) &appData.periodicUpdates, "", NULL, CheckBox, N_("Periodic Updates (in Analysis Mode)") },
420 { 0, SAME_ROW, 0, NULL, NULL, NULL, NULL, Break, "" },
421 { 0, 0, 0, NULL, (void*) &appData.autoExtend, "", NULL, CheckBox, N_("Play Move(s) of Clicked PV (Analysis)") },
422 { 0, 0, 0, NULL, (void*) &appData.ponderNextMove, "", NULL, CheckBox, N_("Ponder Next Move") },
423 { 0, 0, 0, NULL, (void*) &appData.popupExitMessage, "", NULL, CheckBox, N_("Popup Exit Messages") },
424 { 0, 0, 0, NULL, (void*) &appData.popupMoveErrors, "", NULL, CheckBox, N_("Popup Move Errors") },
425 { 0, 0, 0, NULL, (void*) &appData.showEvalInMoveHistory, "", NULL, CheckBox, N_("Scores in Move List") },
426 { 0, 0, 0, NULL, (void*) &appData.showCoords, "", NULL, CheckBox, N_("Show Coordinates") },
427 { 0, 0, 0, NULL, (void*) &appData.markers, "", NULL, CheckBox, N_("Show Target Squares") },
428 { 0, 0, 0, NULL, (void*) &appData.useStickyWindows, "", NULL, CheckBox, N_("Sticky Windows") },
429 { 0, 0, 0, NULL, (void*) &appData.testLegality, "", NULL, CheckBox, N_("Test Legality") },
430 { 0, 0, 0, NULL, (void*) &appData.topLevel, "", NULL, CheckBox, N_("Top-Level Dialogs") },
431 { 0, 0,10, NULL, (void*) &appData.flashCount, "", NULL, Spin, N_("Flash Moves (0 = no flashing):") },
432 { 0, 1,10, NULL, (void*) &appData.flashRate, "", NULL, Spin, N_("Flash Rate (high = fast):") },
433 { 0, 5,100, NULL, (void*) &appData.animSpeed, "", NULL, Spin, N_("Animation Speed (high = slow):") },
434 { 0, 1,5, NULL, (void*) &appData.zoom, "", NULL, Spin, N_("Zoom factor in Evaluation Graph:") },
435 { 0, 0, 0, NULL, (void*) &GeneralOptionsOK, "", NULL, EndMark , "" }
436 };
437
438 void
OptionsProc()439 OptionsProc ()
440 {
441 oldPonder = appData.ponderNextMove;
442 oldShow = appData.showCoords; oldBlind = appData.blindfold;
443 GenericPopUp(generalOptions, _("General Options"), TransientDlg, BoardWindow, MODAL, 0);
444 }
445
446 //---------------------------------------------- New Variant ------------------------------------------------
447
448 static void Pick P((int n));
449
450 static char warning[MSG_SIZ];
451 static int ranksTmp, filesTmp, sizeTmp;
452
453 static Option variantDescriptors[] = {
454 { VariantNormal, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("Normal")},
455 { VariantMakruk, SAME_ROW, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("Makruk")},
456 { VariantFischeRandom, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("FRC")},
457 { VariantShatranj,SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("Shatranj")},
458 { VariantWildCastle, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("Wild castle")},
459 { VariantKnightmate,SAME_ROW,135,NULL,(void*) &Pick, "#FFFFFF", NULL, Button, N_("Knightmate")},
460 { VariantNoCastle, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("No castle")},
461 { VariantCylinder,SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("Cylinder *")},
462 { Variant3Check, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("3-checks")},
463 { VariantBerolina,SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("berolina *")},
464 { VariantAtomic, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("atomic")},
465 { VariantTwoKings,SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("two kings")},
466 { -1, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_(" ")}, // dummy, to have good alignment
467 { VariantSpartan,SAME_ROW, 135, NULL, (void*) &Pick, "#FF0000", NULL, Button, N_("Spartan")},
468 { 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_("Board size ( -1 = default for selected variant):")},
469 { 0, -1, BOARD_RANKS-1, NULL, (void*) &ranksTmp, "", NULL, Spin, N_("Number of Board Ranks:") },
470 { 0, -1, BOARD_FILES, NULL, (void*) &filesTmp, "", NULL, Spin, N_("Number of Board Files:") },
471 { 0, -1, BOARD_RANKS-1, NULL, (void*) &sizeTmp, "", NULL, Spin, N_("Holdings Size:") },
472 { 0, 0, 275, NULL, NULL, NULL, NULL, Label, warning },
473 { 0, 0, 275, NULL, NULL, NULL, NULL, Label, N_("Variants marked with * can only be played\nwith legality testing off.")},
474 { 0, SAME_ROW, 0, NULL, NULL, NULL, NULL, Break, ""},
475 { VariantASEAN, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_("ASEAN")},
476 { VariantGreat, SAME_ROW, 135, NULL, (void*) &Pick, "#BFBFFF", NULL, Button, N_("Great Shatranj (10x8)")},
477 { VariantSChess, 0, 135, NULL, (void*) &Pick, "#FFBFBF", NULL, Button, N_("Seirawan")},
478 { VariantFalcon, SAME_ROW, 135, NULL, (void*) &Pick, "#BFBFFF", NULL, Button, N_("Falcon (10x8)")},
479 { VariantSuper, 0, 135, NULL, (void*) &Pick, "#FFBFBF", NULL, Button, N_("Superchess")},
480 { VariantCapablanca,SAME_ROW,135,NULL,(void*) &Pick, "#BFBFFF", NULL, Button, N_("Capablanca (10x8)")},
481 { VariantCrazyhouse, 0, 135, NULL, (void*) &Pick, "#FFBFBF", NULL, Button, N_("Crazyhouse")},
482 { VariantGothic, SAME_ROW, 135, NULL, (void*) &Pick, "#BFBFFF", NULL, Button, N_("Gothic (10x8)")},
483 { VariantBughouse, 0, 135, NULL, (void*) &Pick, "#FFBFBF", NULL, Button, N_("Bughouse")},
484 { VariantJanus, SAME_ROW, 135, NULL, (void*) &Pick, "#BFBFFF", NULL, Button, N_("Janus (10x8)")},
485 { VariantSuicide, 0, 135, NULL, (void*) &Pick, "#FFFFBF", NULL, Button, N_("Suicide")},
486 { VariantCapaRandom,SAME_ROW,135,NULL,(void*) &Pick, "#BFBFFF", NULL, Button, N_("CRC (10x8)")},
487 { VariantGiveaway, 0, 135, NULL, (void*) &Pick, "#FFFFBF", NULL, Button, N_("give-away")},
488 { VariantGrand, SAME_ROW, 135, NULL, (void*) &Pick, "#5070FF", NULL, Button, N_("grand (10x10)")},
489 { VariantLosers, 0, 135, NULL, (void*) &Pick, "#FFFFBF", NULL, Button, N_("losers")},
490 { VariantShogi, SAME_ROW, 135, NULL, (void*) &Pick, "#BFFFFF", NULL, Button, N_("shogi (9x9)")},
491 { VariantFairy, 0, 135, NULL, (void*) &Pick, "#BFBFBF", NULL, Button, N_("fairy")},
492 { VariantXiangqi, SAME_ROW,135, NULL, (void*) &Pick, "#BFFFFF", NULL, Button, N_("xiangqi (9x10)")},
493 { VariantLion, 0, 135, NULL, (void*) &Pick, "#BFBFBF", NULL, Button, N_("mighty lion")},
494 { VariantCourier, SAME_ROW,135, NULL, (void*) &Pick, "#BFFFBF", NULL, Button, N_("courier (12x8)")},
495 { VariantChuChess, 0, 135, NULL, (void*) &Pick, "#BFBFBF", NULL, Button, N_("elven chess (10x10)")},
496 { VariantChu, SAME_ROW, 135, NULL, (void*) &Pick, "#BFFFBF", NULL, Button, N_("chu shogi (12x12)")},
497 //{ -1, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_(" ")}, // dummy, to have good alignment
498 // optional buttons for engine-defined variants
499 { 0, NO_OK, 0, NULL, NULL, "", NULL, EndMark , "" },
500 { 0, SAME_ROW, 0, NULL, NULL, NULL, NULL, Skip, ""},
501 { VariantUnknown, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
502 { VariantUnknown, SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
503 { VariantUnknown, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
504 { VariantUnknown, SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
505 { VariantUnknown, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
506 { VariantUnknown, SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
507 { VariantUnknown, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
508 { VariantUnknown, SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
509 { VariantUnknown, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
510 { VariantUnknown, SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
511 { VariantUnknown, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
512 { VariantUnknown, SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
513 { VariantUnknown, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
514 { VariantUnknown, SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
515 { VariantUnknown, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
516 { VariantUnknown, SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
517 { VariantUnknown, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
518 { VariantUnknown, SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
519 { VariantUnknown, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
520 { VariantUnknown, SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
521 { VariantUnknown, 0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
522 { VariantUnknown, SAME_ROW,135, NULL, (void*) &Pick, "#FFFFFF", NULL, Skip, NULL },
523 { 0, NO_OK, 0, NULL, NULL, "", NULL, EndMark , "" }
524 };
525
526 static void
Pick(int n)527 Pick (int n)
528 {
529 VariantClass v = variantDescriptors[n].value;
530 if(v == VariantUnknown) safeStrCpy(engineVariant, variantDescriptors[n].name, MSG_SIZ); else *engineVariant = NULLCHAR;
531 GenericReadout(variantDescriptors, -1); // read new ranks and file settings
532 if(!appData.noChessProgram) {
533 char buf[MSG_SIZ];
534 if (!SupportedVariant(first.variants, v, filesTmp, ranksTmp, sizeTmp, first.protocolVersion, first.tidy)) {
535 DisplayError(variantError, 0);
536 return; /* ignore OK if first engine does not support it */
537 } else
538 if (second.initDone &&
539 !SupportedVariant(second.variants, v, filesTmp, ranksTmp, sizeTmp, second.protocolVersion, second.tidy)) {
540 snprintf(buf, MSG_SIZ, _("Warning: second engine (%s) does not support this!"), second.tidy);
541 DisplayError(buf, 0); /* use of second engine is optional; only warn user */
542 }
543 }
544
545 gameInfo.variant = v;
546 appData.variant = VariantName(v);
547
548 shuffleOpenings = FALSE; /* [HGM] shuffle: possible shuffle reset when we switch */
549 startedFromPositionFile = FALSE; /* [HGM] loadPos: no longer valid in new variant */
550 appData.fischerCastling = FALSE; /* [HGM] fischer: no longer valid in new variant */
551 appData.NrRanks = ranksTmp;
552 appData.NrFiles = filesTmp;
553 appData.holdingsSize = sizeTmp;
554 appData.pieceToCharTable = NULL;
555 appData.pieceNickNames = "";
556 appData.colorNickNames = "";
557 PopDown(TransientDlg);
558 Reset(True, True);
559 return;
560 }
561
562 void
NewVariantProc()563 NewVariantProc ()
564 {
565 static int start;
566 int i, last;
567 ranksTmp = filesTmp = sizeTmp = -1; // prefer defaults over actual settings
568 if(appData.noChessProgram) sprintf(warning, _("Only bughouse is not available in viewer mode.")); else
569 sprintf(warning, _("All variants not supported by the first engine\n(currently %s) are disabled."), first.tidy);
570 if(!start) {
571 while(variantDescriptors[start].type != EndMark) start++; // locate spares
572 start += 2; // conditional EndMark and Break
573 }
574 last = -1;
575 for(i=0; variantDescriptors[start+i].type != EndMark; i++) { // create buttons for engine-defined variants
576 char *v = EngineDefinedVariant(&first, i);
577 if(v) {
578 last = i;
579 ASSIGN(variantDescriptors[start+i].name, v);
580 variantDescriptors[start+i].type = Button;
581 } else variantDescriptors[start+i].type = Skip;
582 }
583 if(!(last&1)) { // odd number, add filler
584 ASSIGN(variantDescriptors[start+last+1].name, " ");
585 variantDescriptors[start+last+1].type = Button;
586 variantDescriptors[start+last+1].value = Skip;
587 }
588 variantDescriptors[start-2].type = (last < 0 ? EndMark : Skip);
589 variantDescriptors[start-1].type = (last < 6 ? Skip : Break);
590 safeStrCpy(engineVariant+100, engineVariant, 100); *engineVariant = NULLCHAR; // yeghh...
591 GenericPopUp(variantDescriptors, _("New Variant"), TransientDlg, BoardWindow, MODAL, 0);
592 safeStrCpy(engineVariant, engineVariant+100, MSG_SIZ); // must temporarily clear to avoid enabling all variant buttons
593 }
594
595 //------------------------------------------- Common Engine Options -------------------------------------
596
597 static int oldCores;
598 static char *egtPath;
599
600 static int
CommonOptionsOK(int n)601 CommonOptionsOK (int n)
602 {
603 int newPonder = appData.ponderNextMove;
604 if(*egtPath != '/' && strchr(egtPath, ':')) {
605 ASSIGN(appData.egtFormats, egtPath);
606 } else {
607 ASSIGN(appData.defaultPathEGTB, egtPath);
608 }
609 // make sure changes are sent to first engine by re-initializing it
610 // if it was already started pre-emptively at end of previous game
611 if(gameMode == BeginningOfGame) Reset(True, True); else {
612 // Some changed setting need immediate sending always.
613 if(oldCores != appData.smpCores)
614 NewSettingEvent(False, &(first.maxCores), "cores", appData.smpCores);
615 appData.ponderNextMove = oldPonder;
616 PonderNextMoveEvent(newPonder);
617 }
618 return 1;
619 }
620
621 static Option commonEngineOptions[] = {
622 { 0, 0, 0, NULL, (void*) &appData.ponderNextMove, "", NULL, CheckBox, N_("Ponder Next Move") },
623 { 0, 0, 1000, NULL, (void*) &appData.smpCores, "", NULL, Spin, N_("Maximum Number of CPUs per Engine:") },
624 { 0, 0, 0, NULL, (void*) &appData.polyglotDir, "", NULL, PathName, N_("Polygot Directory:") },
625 { 0, 0,16000, NULL, (void*) &appData.defaultHashSize, "", NULL, Spin, N_("Hash-Table Size (MB):") },
626 { 0, 0, 0, NULL, (void*) &egtPath, "", NULL, PathName, N_("EGTB Path:") },
627 { 0, 0, 1000, NULL, (void*) &appData.defaultCacheSizeEGTB, "", NULL, Spin, N_("EGTB Cache Size (MB):") },
628 { 0, 0, 0, NULL, (void*) &appData.usePolyglotBook, "", NULL, CheckBox, N_("Use GUI Book") },
629 { 0, 0, 0, NULL, (void*) &appData.polyglotBook, ".bin", NULL, FileName, N_("Opening-Book Filename:") },
630 { 0, 0, 100, NULL, (void*) &appData.bookDepth, "", NULL, Spin, N_("Book Depth (moves):") },
631 { 0, 0, 100, NULL, (void*) &appData.bookStrength, "", NULL, Spin, N_("Book Variety (0) vs. Strength (100):") },
632 { 0, 0, 0, NULL, (void*) &appData.firstHasOwnBookUCI, "", NULL, CheckBox, N_("Engine #1 Has Own Book") },
633 { 0, 0, 0, NULL, (void*) &appData.secondHasOwnBookUCI, "", NULL, CheckBox, N_("Engine #2 Has Own Book ") },
634 { 0,SAME_ROW,0,NULL, (void*) &CommonOptionsOK, "", NULL, EndMark , "" }
635 };
636
637 void
UciMenuProc()638 UciMenuProc ()
639 {
640 oldCores = appData.smpCores;
641 oldPonder = appData.ponderNextMove;
642 if(appData.egtFormats && *appData.egtFormats) { ASSIGN(egtPath, appData.egtFormats); }
643 else { ASSIGN(egtPath, appData.defaultPathEGTB); }
644 GenericPopUp(commonEngineOptions, _("Common Engine Settings"), TransientDlg, BoardWindow, MODAL, 0);
645 }
646
647 //------------------------------------------ Adjudication Options --------------------------------------
648
649 static Option adjudicationOptions[] = {
650 { 0, 0, 0, NULL, (void*) &appData.checkMates, "", NULL, CheckBox, N_("Detect all Mates") },
651 { 0, 0, 0, NULL, (void*) &appData.testClaims, "", NULL, CheckBox, N_("Verify Engine Result Claims") },
652 { 0, 0, 0, NULL, (void*) &appData.materialDraws, "", NULL, CheckBox, N_("Draw if Insufficient Mating Material") },
653 { 0, 0, 0, NULL, (void*) &appData.trivialDraws, "", NULL, CheckBox, N_("Adjudicate Trivial Draws (3-Move Delay)") },
654 { 0, 0,100, NULL, (void*) &appData.ruleMoves, "", NULL, Spin, N_("N-Move Rule:") },
655 { 0, 0, 6, NULL, (void*) &appData.drawRepeats, "", NULL, Spin, N_("N-fold Repeats:") },
656 { 0, 0,1000, NULL, (void*) &appData.adjudicateDrawMoves, "", NULL, Spin, N_("Draw after N Moves Total:") },
657 { 0, -5000,0, NULL, (void*) &appData.adjudicateLossThreshold, "", NULL, Spin, N_("Win / Loss Threshold:") },
658 { 0, 0, 0, NULL, (void*) &first.scoreIsAbsolute, "", NULL, CheckBox, N_("Negate Score of Engine #1") },
659 { 0, 0, 0, NULL, (void*) &second.scoreIsAbsolute, "", NULL, CheckBox, N_("Negate Score of Engine #2") },
660 { 0,SAME_ROW, 0, NULL, NULL, "", NULL, EndMark , "" }
661 };
662
663 void
EngineMenuProc()664 EngineMenuProc ()
665 {
666 GenericPopUp(adjudicationOptions, _("Adjudicate non-ICS Games"), TransientDlg, BoardWindow, MODAL, 0);
667 }
668
669 //--------------------------------------------- ICS Options ---------------------------------------------
670
671 static int
IcsOptionsOK(int n)672 IcsOptionsOK (int n)
673 {
674 ParseIcsTextColors();
675 return 1;
676 }
677
678 Option icsOptions[] = {
679 { 0, 0, 0, NULL, (void*) &appData.autoKibitz, "", NULL, CheckBox, N_("Auto-Kibitz") },
680 { 0, 0, 0, NULL, (void*) &appData.autoComment, "", NULL, CheckBox, N_("Auto-Comment") },
681 { 0, 0, 0, NULL, (void*) &appData.autoObserve, "", NULL, CheckBox, N_("Auto-Observe") },
682 { 0, 0, 0, NULL, (void*) &appData.autoRaiseBoard, "", NULL, CheckBox, N_("Auto-Raise Board") },
683 { 0, 0, 0, NULL, (void*) &appData.autoCreateLogon, "", NULL, CheckBox, N_("Auto-Create Logon Script") },
684 { 0, 0, 0, NULL, (void*) &appData.bgObserve, "", NULL, CheckBox, N_("Background Observe while Playing") },
685 { 0, 0, 0, NULL, (void*) &appData.dualBoard, "", NULL, CheckBox, N_("Dual Board for Background-Observed Game") },
686 { 0, 0, 0, NULL, (void*) &appData.getMoveList, "", NULL, CheckBox, N_("Get Move List") },
687 { 0, 0, 0, NULL, (void*) &appData.quietPlay, "", NULL, CheckBox, N_("Quiet Play") },
688 { 0, 0, 0, NULL, (void*) &appData.seekGraph, "", NULL, CheckBox, N_("Seek Graph") },
689 { 0, 0, 0, NULL, (void*) &appData.autoRefresh, "", NULL, CheckBox, N_("Auto-Refresh Seek Graph") },
690 { 0, 0, 0, NULL, (void*) &appData.autoBox, "", NULL, CheckBox, N_("Auto-InputBox PopUp") },
691 { 0, 0, 0, NULL, (void*) &appData.quitNext, "", NULL, CheckBox, N_("Quit after game") },
692 { 0, 0, 0, NULL, (void*) &appData.premove, "", NULL, CheckBox, N_("Premove") },
693 { 0, 0, 0, NULL, (void*) &appData.premoveWhite, "", NULL, CheckBox, N_("Premove for White") },
694 { 0, 0, 0, NULL, (void*) &appData.premoveWhiteText, "", NULL, TextBox, N_("First White Move:") },
695 { 0, 0, 0, NULL, (void*) &appData.premoveBlack, "", NULL, CheckBox, N_("Premove for Black") },
696 { 0, 0, 0, NULL, (void*) &appData.premoveBlackText, "", NULL, TextBox, N_("First Black Move:") },
697 { 0, SAME_ROW, 0, NULL, NULL, NULL, NULL, Break, "" },
698 { 0, 0, 0, NULL, (void*) &appData.icsAlarm, "", NULL, CheckBox, N_("Alarm") },
699 { 0, 0, 100000000, NULL, (void*) &appData.icsAlarmTime, "", NULL, Spin, N_("Alarm Time (msec):") },
700 //{ 0, 0, 0, NULL, (void*) &appData.chatBoxes, "", NULL, TextBox, N_("Startup Chat Boxes:") },
701 { 0, 0, 0, NULL, (void*) &appData.colorize, "", NULL, CheckBox, N_("Colorize Messages") },
702 { 0, 0, 0, NULL, (void*) &appData.colorShout, "", NULL, TextBox, N_("Shout Text Colors:") },
703 { 0, 0, 0, NULL, (void*) &appData.colorSShout, "", NULL, TextBox, N_("S-Shout Text Colors:") },
704 { 0, 0, 0, NULL, (void*) &appData.colorChannel1, "", NULL, TextBox, N_("Channel #1 Text Colors:") },
705 { 0, 0, 0, NULL, (void*) &appData.colorChannel, "", NULL, TextBox, N_("Other Channel Text Colors:") },
706 { 0, 0, 0, NULL, (void*) &appData.colorKibitz, "", NULL, TextBox, N_("Kibitz Text Colors:") },
707 { 0, 0, 0, NULL, (void*) &appData.colorTell, "", NULL, TextBox, N_("Tell Text Colors:") },
708 { 0, 0, 0, NULL, (void*) &appData.colorChallenge, "", NULL, TextBox, N_("Challenge Text Colors:") },
709 { 0, 0, 0, NULL, (void*) &appData.colorRequest, "", NULL, TextBox, N_("Request Text Colors:") },
710 { 0, 0, 0, NULL, (void*) &appData.colorSeek, "", NULL, TextBox, N_("Seek Text Colors:") },
711 { 0, 0, 0, NULL, (void*) &appData.colorNormal, "", NULL, TextBox, N_("Other Text Colors:") },
712 { 0, 0, 0, NULL, (void*) &IcsOptionsOK, "", NULL, EndMark , "" }
713 };
714
715 void
IcsOptionsProc()716 IcsOptionsProc ()
717 {
718 GenericPopUp(icsOptions, _("ICS Options"), TransientDlg, BoardWindow, MODAL, 0);
719 }
720
721 //-------------------------------------------- Load Game Options ---------------------------------
722
723 static char *modeNames[] = { N_("Exact position match"), N_("Shown position is subset"), N_("Same material with exactly same Pawn chain"),
724 N_("Same material"), N_("Material range (top board half optional)"), N_("Material difference (optional stuff balanced)"), NULL };
725 static char *modeValues[] = { "1", "2", "3", "4", "5", "6" };
726 static char *searchMode, *countRange;
727
728 static int
LoadOptionsOK()729 LoadOptionsOK ()
730 {
731 appData.minPieces = appData.maxPieces = 0;
732 sscanf(countRange, "%d-%d", &appData.minPieces, &appData.maxPieces);
733 if(appData.maxPieces < appData.minPieces) appData.maxPieces = appData.minPieces;
734 appData.searchMode = atoi(searchMode);
735 return 1;
736 }
737
738 static Option loadOptions[] = {
739 { 0, 0, 0, NULL, (void*) &appData.autoDisplayTags, "", NULL, CheckBox, N_("Auto-Display Tags") },
740 { 0, 0, 0, NULL, (void*) &appData.autoDisplayComment, "", NULL, CheckBox, N_("Auto-Display Comment") },
741 { 0, LR, 0, NULL, NULL, NULL, NULL, Label, N_("Auto-Play speed of loaded games\n(0 = instant, -1 = off):") },
742 { 0, -1,10000000, NULL, (void*) &appData.timeDelay, "", NULL, Fractional, N_("Seconds per Move:") },
743 { 0, LR, 0, NULL, NULL, NULL, NULL, Label, N_("\noptions to use in game-viewer mode:") },
744 { 0, 0,300, NULL, (void*) &appData.viewerOptions, "", NULL, TextBox, "" },
745 { 0, LR, 0, NULL, NULL, NULL, NULL, Label, N_("\nThresholds for position filtering in game list:") },
746 { 0, 0,5000, NULL, (void*) &appData.eloThreshold1, "", NULL, Spin, N_("Elo of strongest player at least:") },
747 { 0, 0,5000, NULL, (void*) &appData.eloThreshold2, "", NULL, Spin, N_("Elo of weakest player at least:") },
748 { 0, 0,5000, NULL, (void*) &appData.dateThreshold, "", NULL, Spin, N_("No games before year:") },
749 { 0, 1,50, NULL, (void*) &appData.stretch, "", NULL, Spin, N_("Minimum nr consecutive positions:") },
750 { 0, 0,197, NULL, (void*) &countRange, "", NULL, TextBox, "Final nr of pieces" },
751 { 0, 0,205, NULL, (void*) &searchMode, (char*) modeValues, modeNames, ComboBox, N_("Search mode:") },
752 { 0, 0, 0, NULL, (void*) &appData.ignoreColors, "", NULL, CheckBox, N_("Also match reversed colors") },
753 { 0, 0, 0, NULL, (void*) &appData.findMirror, "", NULL, CheckBox, N_("Also match left-right flipped position") },
754 { 0, 0, 0, NULL, (void*) &LoadOptionsOK, "", NULL, EndMark , "" }
755 };
756
757 void
LoadOptionsPopUp(DialogClass parent)758 LoadOptionsPopUp (DialogClass parent)
759 {
760 ASSIGN(countRange, "");
761 ASSIGN(searchMode, modeValues[appData.searchMode-1]);
762 GenericPopUp(loadOptions, _("Load Game Options"), TransientDlg, parent, MODAL, 0);
763 }
764
765 void
LoadOptionsProc()766 LoadOptionsProc ()
767 { // called from menu
768 LoadOptionsPopUp(BoardWindow);
769 }
770
771 //------------------------------------------- Save Game Options --------------------------------------------
772
773 static Option saveOptions[] = {
774 { 0, 0, 0, NULL, (void*) &appData.autoSaveGames, "", NULL, CheckBox, N_("Auto-Save Games") },
775 { 0, 0, 0, NULL, (void*) &appData.onlyOwn, "", NULL, CheckBox, N_("Own Games Only") },
776 { 0, 0, 0, NULL, (void*) &appData.saveGameFile, ".pgn", NULL, FileName, N_("Save Games on File:") },
777 { 0, 0, 0, NULL, (void*) &appData.savePositionFile, ".fen", NULL, FileName, N_("Save Final Positions on File:") },
778 { 0, 0, 0, NULL, (void*) &appData.pgnEventHeader, "", NULL, TextBox, N_("PGN Event Header:") },
779 { 0, 0, 0, NULL, (void*) &appData.oldSaveStyle, "", NULL, CheckBox, N_("Old Save Style (as opposed to PGN)") },
780 { 0, 0, 0, NULL, (void*) &appData.numberTag, "", NULL, CheckBox, N_("Include Number Tag in tourney PGN") },
781 { 0, 0, 0, NULL, (void*) &appData.saveExtendedInfoInPGN, "", NULL, CheckBox, N_("Save Score/Depth Info in PGN") },
782 { 0, 0, 0, NULL, (void*) &appData.saveOutOfBookInfo, "", NULL, CheckBox, N_("Save Out-of-Book Info in PGN ") },
783 { 0, SAME_ROW, 0, NULL, NULL, "", NULL, EndMark , "" }
784 };
785
786 void
SaveOptionsProc()787 SaveOptionsProc ()
788 {
789 GenericPopUp(saveOptions, _("Save Game Options"), TransientDlg, BoardWindow, MODAL, 0);
790 }
791
792 //----------------------------------------------- Sound Options ---------------------------------------------
793
794 static void Test P((int n));
795 static char *trialSound;
796
797 static char *soundNames[] = {
798 N_("No Sound"),
799 N_("Default Beep"),
800 N_("Above WAV File"),
801 N_("Car Horn"),
802 N_("Cymbal"),
803 N_("Ding"),
804 N_("Gong"),
805 N_("Laser"),
806 N_("Penalty"),
807 N_("Phone"),
808 N_("Pop"),
809 N_("Roar"),
810 N_("Slap"),
811 N_("Wood Thunk"),
812 NULL,
813 N_("User File")
814 };
815
816 static char *soundFiles[] = { // sound files corresponding to above names
817 "",
818 "$",
819 NULL, // kludge alert: as first thing in the dialog readout this is replaced with the user-given .WAV filename
820 "honkhonk.wav",
821 "cymbal.wav",
822 "ding1.wav",
823 "gong.wav",
824 "laser.wav",
825 "penalty.wav",
826 "phone.wav",
827 "pop2.wav",
828 "roar.wav",
829 "slap.wav",
830 "woodthunk.wav",
831 NULL,
832 NULL
833 };
834
835 static Option soundOptions[] = {
836 { 0, 0, 0, NULL, (void*) (soundFiles+2) /* kludge! */, ".wav", NULL, FileName, N_("User WAV File:") },
837 { 0, 0, 0, NULL, (void*) &appData.soundProgram, "", NULL, TextBox, N_("Sound Program:") },
838 { 0, 0, 0, NULL, (void*) &trialSound, (char*) soundFiles, soundNames, ComboBox, N_("Try-Out Sound:") },
839 { 0, SAME_ROW, 0, NULL, (void*) &Test, NULL, NULL, Button, N_("Play") },
840 { 0, 0, 0, NULL, (void*) &appData.soundMove, (char*) soundFiles, soundNames, ComboBox, N_("Move:") },
841 { 0, 0, 0, NULL, (void*) &appData.soundIcsWin, (char*) soundFiles, soundNames, ComboBox, N_("Win:") },
842 { 0, 0, 0, NULL, (void*) &appData.soundIcsLoss, (char*) soundFiles, soundNames, ComboBox, N_("Lose:") },
843 { 0, 0, 0, NULL, (void*) &appData.soundIcsDraw, (char*) soundFiles, soundNames, ComboBox, N_("Draw:") },
844 { 0, 0, 0, NULL, (void*) &appData.soundIcsUnfinished, (char*) soundFiles, soundNames, ComboBox, N_("Unfinished:") },
845 { 0, 0, 0, NULL, (void*) &appData.soundIcsAlarm, (char*) soundFiles, soundNames, ComboBox, N_("Alarm:") },
846 { 0, 0, 0, NULL, (void*) &appData.soundChallenge, (char*) soundFiles, soundNames, ComboBox, N_("Challenge:") },
847 { 0, SAME_ROW, 0, NULL, NULL, NULL, NULL, Break, "" },
848 { 0, 0, 0, NULL, (void*) &appData.soundDirectory, "", NULL, PathName, N_("Sounds Directory:") },
849 { 0, 0, 0, NULL, (void*) &appData.soundShout, (char*) soundFiles, soundNames, ComboBox, N_("Shout:") },
850 { 0, 0, 0, NULL, (void*) &appData.soundSShout, (char*) soundFiles, soundNames, ComboBox, N_("S-Shout:") },
851 { 0, 0, 0, NULL, (void*) &appData.soundChannel, (char*) soundFiles, soundNames, ComboBox, N_("Channel:") },
852 { 0, 0, 0, NULL, (void*) &appData.soundChannel1, (char*) soundFiles, soundNames, ComboBox, N_("Channel 1:") },
853 { 0, 0, 0, NULL, (void*) &appData.soundTell, (char*) soundFiles, soundNames, ComboBox, N_("Tell:") },
854 { 0, 0, 0, NULL, (void*) &appData.soundKibitz, (char*) soundFiles, soundNames, ComboBox, N_("Kibitz:") },
855 { 0, 0, 0, NULL, (void*) &appData.soundRequest, (char*) soundFiles, soundNames, ComboBox, N_("Request:") },
856 { 0, 0, 0, NULL, (void*) &appData.soundRoar, (char*) soundFiles, soundNames, ComboBox, N_("Lion roar:") },
857 { 0, 0, 0, NULL, (void*) &appData.soundSeek, (char*) soundFiles, soundNames, ComboBox, N_("Seek:") },
858 { 0, SAME_ROW, 0, NULL, NULL, "", NULL, EndMark , "" }
859 };
860
861 static void
Test(int n)862 Test (int n)
863 {
864 GenericReadout(soundOptions, 1);
865 if(soundFiles[values[2]]) PlaySoundFile(soundFiles[values[2]]);
866 }
867
868 void
SoundOptionsProc()869 SoundOptionsProc ()
870 {
871 free(soundFiles[2]);
872 soundFiles[2] = strdup("*");
873 GenericPopUp(soundOptions, _("Sound Options"), TransientDlg, BoardWindow, MODAL, 0);
874 }
875
876 //--------------------------------------------- Board Options --------------------------------------
877
878 static void DefColor P((int n));
879 static void AdjustColor P((int i));
880 static void ThemeSel P((int n, int sel));
881 static int BoardOptionsOK P((int n));
882
883 static char oldPieceDir[MSG_SIZ];
884 extern char *engineLine, *nickName; // defined later on
885
886 #define THEMELIST 1
887
888 static Option boardOptions[] = {
889 { 0,LR|T2T, 0, NULL, NULL, NULL, NULL, Label, N_("Selectable themes:") },
890 { 300,LR|TB,200, NULL, (void*) engineMnemonic, (char*) &ThemeSel, NULL, ListBox, "" },
891 { 0,LR|T2T, 0, NULL, NULL, NULL, NULL, Label, N_("New name for current theme:") },
892 { 0, 0, 0, NULL, (void*) &nickName, "", NULL, TextBox, "" },
893 { 0,SAME_ROW, 0, NULL, NULL, NULL, NULL, Break, NULL },
894 { 0, 0, 70, NULL, (void*) &appData.whitePieceColor, "", NULL, TextBox, N_("White Piece Color:") },
895 { 1000, SAME_ROW, 0, NULL, (void*) &DefColor, NULL, (char**) "#FFFFCC", Button, " " },
896 /* TRANSLATORS: R = single letter for the color red */
897 { 1, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("R") },
898 /* TRANSLATORS: G = single letter for the color green */
899 { 2, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("G") },
900 /* TRANSLATORS: B = single letter for the color blue */
901 { 3, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("B") },
902 /* TRANSLATORS: D = single letter to make a color darker */
903 { 4, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("D") },
904 { 0, 0, 70, NULL, (void*) &appData.blackPieceColor, "", NULL, TextBox, N_("Black Piece Color:") },
905 { 1000, SAME_ROW, 0, NULL, (void*) &DefColor, NULL, (char**) "#202020", Button, " " },
906 { 1, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("R") },
907 { 2, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("G") },
908 { 3, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("B") },
909 { 4, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("D") },
910 { 0, 0, 70, NULL, (void*) &appData.lightSquareColor, "", NULL, TextBox, N_("Light Square Color:") },
911 { 1000, SAME_ROW, 0, NULL, (void*) &DefColor, NULL, (char**) "#C8C365", Button, " " },
912 { 1, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("R") },
913 { 2, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("G") },
914 { 3, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("B") },
915 { 4, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("D") },
916 { 0, 0, 70, NULL, (void*) &appData.darkSquareColor, "", NULL, TextBox, N_("Dark Square Color:") },
917 { 1000, SAME_ROW, 0, NULL, (void*) &DefColor, NULL, (char**) "#77A26D", Button, " " },
918 { 1, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("R") },
919 { 2, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("G") },
920 { 3, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("B") },
921 { 4, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("D") },
922 { 0, 0, 70, NULL, (void*) &appData.highlightSquareColor, "", NULL, TextBox, N_("Highlight Color:") },
923 { 1000, SAME_ROW, 0, NULL, (void*) &DefColor, NULL, (char**) "#FFFF00", Button, " " },
924 { 1, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("R") },
925 { 2, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("G") },
926 { 3, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("B") },
927 { 4, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("D") },
928 { 0, 0, 70, NULL, (void*) &appData.premoveHighlightColor, "", NULL, TextBox, N_("Premove Highlight Color:") },
929 { 1000, SAME_ROW, 0, NULL, (void*) &DefColor, NULL, (char**) "#FF0000", Button, " " },
930 { 1, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("R") },
931 { 2, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("G") },
932 { 3, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("B") },
933 { 4, SAME_ROW, 0, NULL, (void*) &AdjustColor, NULL, NULL, Button, N_("D") },
934 { 0, 0, 0, NULL, (void*) &appData.upsideDown, "", NULL, CheckBox, N_("Flip Pieces Shogi Style (Colored buttons restore default)") },
935 //{ 0, 0, 0, NULL, (void*) &appData.allWhite, "", NULL, CheckBox, N_("Use Outline Pieces for Black") },
936 { 0, 0, 0, NULL, (void*) &appData.monoMode, "", NULL, CheckBox, N_("Mono Mode") },
937 { 0, 0, 200, NULL, (void*) &appData.logoSize, "", NULL, Spin, N_("Logo Size (0=off, requires restart):") },
938 { 0,-1, 5, NULL, (void*) &appData.overrideLineGap, "", NULL, Spin, N_("Line Gap (-1 = default for board size):") },
939 { 0, 0, 0, NULL, (void*) &appData.useBitmaps, "", NULL, CheckBox, N_("Use Board Textures") },
940 { 0, 0, 0, NULL, (void*) &appData.liteBackTextureFile, ".png", NULL, FileName, N_("Light-Squares Texture File:") },
941 { 0, 0, 0, NULL, (void*) &appData.darkBackTextureFile, ".png", NULL, FileName, N_("Dark-Squares Texture File:") },
942 { 0, 0, 0, NULL, (void*) &appData.trueColors, "", NULL, CheckBox, N_("Use external piece bitmaps with their own colors") },
943 { 0, 0, 0, NULL, (void*) &appData.pieceDirectory, "", NULL, PathName, N_("Directory with Pieces Images:") },
944 { 0, 0, 0, NULL, (void*) &BoardOptionsOK, "", NULL, EndMark , "" }
945 };
946
947 static int
BoardOptionsOK(int n)948 BoardOptionsOK (int n)
949 {
950 if(n && (n = SelectedListBoxItem(&boardOptions[THEMELIST])) > 0 && *engineList[n] != '#') { // called by pressing OK, and theme selected
951 ASSIGN(engineLine, engineList[n]);
952 }
953 LoadTheme();
954 return 1;
955 }
956
957 static void
SetColorText(int n,char * buf)958 SetColorText (int n, char *buf)
959 {
960 SetWidgetText(&boardOptions[n-1], buf, TransientDlg);
961 SetColor(buf, &boardOptions[n]);
962 }
963
964 static void
DefColor(int n)965 DefColor (int n)
966 {
967 SetColorText(n, (char*) boardOptions[n].choice);
968 }
969
970 void
RefreshColor(int source,int n)971 RefreshColor (int source, int n)
972 {
973 int col, j, r, g, b, step = 10;
974 char *s, buf[MSG_SIZ]; // color string
975 GetWidgetText(&boardOptions[source], &s);
976 if(sscanf(s, "#%x", &col) != 1) return; // malformed
977 b = col & 0xFF; g = col & 0xFF00; r = col & 0xFF0000;
978 switch(n) {
979 case 1: r += 0x10000*step;break;
980 case 2: g += 0x100*step; break;
981 case 3: b += step; break;
982 case 4: r -= 0x10000*step; g -= 0x100*step; b -= step; break;
983 }
984 if(r < 0) r = 0; if(g < 0) g = 0; if(b < 0) b = 0;
985 if(r > 0xFF0000) r = 0xFF0000; if(g > 0xFF00) g = 0xFF00; if(b > 0xFF) b = 0xFF;
986 col = r | g | b;
987 snprintf(buf, MSG_SIZ, "#%06x", col);
988 for(j=1; j<7; j++) if(buf[j] >= 'a') buf[j] -= 32; // capitalize
989 SetColorText(source+1, buf);
990 }
991
992 static void
AdjustColor(int i)993 AdjustColor (int i)
994 {
995 int n = boardOptions[i].value;
996 RefreshColor(i-n-1, n);
997 }
998
999 void
ThemeSel(int n,int sel)1000 ThemeSel (int n, int sel)
1001 {
1002 int nr;
1003 char buf[MSG_SIZ];
1004 if(sel < 1) buf[0] = NULLCHAR; // back to top level
1005 else if(engineList[sel][0] == '#') safeStrCpy(buf, engineList[sel], MSG_SIZ); // group header, open group
1006 else { // normal line, select engine
1007 ASSIGN(engineLine, engineList[sel]);
1008 LoadTheme();
1009 PopDown(TransientDlg);
1010 return;
1011 }
1012 nr = NamesToList(appData.themeNames, engineList, engineMnemonic, buf); // replace list by only the group contents
1013 ASSIGN(engineMnemonic[0], buf);
1014 LoadListBox(&boardOptions[THEMELIST], _("# no themes are defined"), -1, -1);
1015 HighlightWithScroll(&boardOptions[THEMELIST], 0, nr);
1016 }
1017
1018 void
BoardOptionsProc()1019 BoardOptionsProc ()
1020 {
1021 strncpy(oldPieceDir, appData.pieceDirectory, MSG_SIZ-1); // to see if it changed
1022 ASSIGN(engineLine, "");
1023 ASSIGN(nickName, "");
1024 ASSIGN(engineMnemonic[0], "");
1025 NamesToList(appData.themeNames, engineList, engineMnemonic, "");
1026 GenericPopUp(boardOptions, _("Board Options"), TransientDlg, BoardWindow, MODAL, 0);
1027 }
1028
1029 //-------------------------------------------- ICS Text Menu Options ------------------------------
1030
1031 Option textOptions[100];
1032 static void PutText P((char *text, int pos));
1033 static void NewChat P((char *name));
1034 static char clickedWord[MSG_SIZ], click;
1035
1036 void
SendString(char * p)1037 SendString (char *p)
1038 {
1039 char buf[MSG_SIZ], buf2[MSG_SIZ], *q;
1040 if(q = strstr(p, "$name")) { // in Xaw this is already intercepted
1041 if(!shellUp[TextMenuDlg] || !clickedWord[0]) return;
1042 strncpy(buf2, p, MSG_SIZ);
1043 snprintf(buf2 + (q-p), MSG_SIZ -(q-p), "%s%s", clickedWord, q+5);
1044 p = buf2;
1045 }
1046 if(!strcmp(p, "$copy")) { // special case for copy selection
1047 CopySomething(clickedWord);
1048 } else
1049 if(!strcmp(p, "$chat")) { // special case for opening chat
1050 NewChat(clickedWord);
1051 } else
1052 if(q = strstr(p, "$input")) {
1053 if(!shellUp[TextMenuDlg]) return;
1054 strncpy(buf, p, MSG_SIZ);
1055 strncpy(buf + (q-p), q+6, MSG_SIZ-(q-p));
1056 PutText(buf, q-p);
1057 } else {
1058 snprintf(buf, MSG_SIZ, "%s\n", p);
1059 SendToICS(buf);
1060 }
1061 if(click) { // popped up by memo click
1062 click = clickedWord[0] = 0;
1063 PopDown(TextMenuDlg);
1064 }
1065 }
1066
1067 void
IcsTextPopUp()1068 IcsTextPopUp ()
1069 {
1070 int i=0, j;
1071 char *p, *q, *r;
1072 if((p = icsTextMenuString) == NULL) return;
1073 do {
1074 q = r = p; while(*p && *p != ';') p++;
1075 if(textOptions[i].name == NULL) textOptions[i].name = (char*) malloc(MSG_SIZ);
1076 for(j=0; j<p-q; j++) textOptions[i].name[j] = *r++;
1077 textOptions[i].name[j++] = 0;
1078 if(!*p) break;
1079 if(*++p == '\n') p++; // optional linefeed after button-text terminating semicolon
1080 q = p;
1081 textOptions[i].choice = (char**) (r = textOptions[i].name + j);
1082 while(*p && (*p != ';' || p[1] != '\n')) textOptions[i].name[j++] = *p++;
1083 textOptions[i].name[j++] = 0;
1084 if(*p) p += 2;
1085 textOptions[i].max = 135;
1086 textOptions[i].min = i&1;
1087 textOptions[i].handle = NULL;
1088 textOptions[i].target = &SendText;
1089 textOptions[i].textValue = strstr(r, "$input") ? "#80FF80" : strstr(r, "$name") ? "#FF8080" : "#FFFFFF";
1090 textOptions[i].type = Button;
1091 } while(++i < 99 && *p);
1092 if(i == 0) return;
1093 textOptions[i].type = EndMark;
1094 textOptions[i].target = NULL;
1095 textOptions[i].min = 2;
1096 MarkMenu("View.ICStextmenu", TextMenuDlg);
1097 GenericPopUp(textOptions, _("ICS text menu"), TextMenuDlg, BoardWindow, NONMODAL, appData.topLevel);
1098 }
1099
1100 void
IcsTextProc()1101 IcsTextProc ()
1102 {
1103 if(shellUp[TextMenuDlg]) PopDown(TextMenuDlg);
1104 else IcsTextPopUp();
1105 }
1106
1107 //---------------------------------------------------- Edit Comment -----------------------------------
1108
1109 static char *commentText;
1110 static int commentIndex;
1111 static void ClearComment P((int n));
1112 static void SaveChanges P((int n));
1113 int savedIndex; /* gross that this is global (and even across files...) */
1114
1115 static int CommentClick P((Option *opt, int n, int x, int y, char *val, int index));
1116
1117 static int
NewComCallback(int n)1118 NewComCallback (int n)
1119 {
1120 ReplaceComment(commentIndex, commentText);
1121 return 1;
1122 }
1123
1124 Option commentOptions[] = {
1125 { 200, T_VSCRL | T_FILL | T_WRAP | T_TOP, 250, NULL, (void*) &commentText, NULL, (char **) &CommentClick, TextBox, "", &appData.commentFont },
1126 { 0, 0, 50, NULL, (void*) &ClearComment, NULL, NULL, Button, N_("clear") },
1127 { 0, SAME_ROW, 100, NULL, (void*) &SaveChanges, NULL, NULL, Button, N_("save changes") },
1128 { 0, SAME_ROW, 0, NULL, (void*) &NewComCallback, "", NULL, EndMark , "" }
1129 };
1130
1131 static int
CommentClick(Option * opt,int n,int x,int y,char * val,int index)1132 CommentClick (Option *opt, int n, int x, int y, char *val, int index)
1133 {
1134 if(n != 3) return FALSE; // only button-3 press is of interest
1135 ReplaceComment(savedIndex, val);
1136 if(savedIndex != currentMove) ToNrEvent(savedIndex);
1137 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1138 return TRUE;
1139 }
1140
1141 static void
SaveChanges(int n)1142 SaveChanges (int n)
1143 {
1144 GenericReadout(commentOptions, 0);
1145 ReplaceComment(commentIndex, commentText);
1146 }
1147
1148 static void
ClearComment(int n)1149 ClearComment (int n)
1150 {
1151 SetWidgetText(&commentOptions[0], "", CommentDlg);
1152 }
1153
1154 void
NewCommentPopup(char * title,char * text,int index)1155 NewCommentPopup (char *title, char *text, int index)
1156 {
1157 if(DialogExists(CommentDlg)) { // if already exists, alter title and content
1158 SetDialogTitle(CommentDlg, title);
1159 SetWidgetText(&commentOptions[0], text, CommentDlg);
1160 }
1161 if(commentText) free(commentText); commentText = strdup(text);
1162 commentIndex = index;
1163 MarkMenu("View.Comments", CommentDlg);
1164 if(GenericPopUp(commentOptions, title, CommentDlg, BoardWindow, NONMODAL, appData.topLevel))
1165 AddHandler(&commentOptions[0], CommentDlg, 1);
1166 }
1167
1168 void
EditCommentPopUp(int index,char * title,char * text)1169 EditCommentPopUp (int index, char *title, char *text)
1170 {
1171 savedIndex = index;
1172 if (text == NULL) text = "";
1173 NewCommentPopup(title, text, index);
1174 }
1175
1176 void
CommentPopUp(char * title,char * text)1177 CommentPopUp (char *title, char *text)
1178 {
1179 savedIndex = currentMove; // [HGM] vari
1180 NewCommentPopup(title, text, currentMove);
1181 }
1182
1183 void
CommentPopDown()1184 CommentPopDown ()
1185 {
1186 PopDown(CommentDlg);
1187 }
1188
1189
1190 void
EditCommentProc()1191 EditCommentProc ()
1192 {
1193 if (PopDown(CommentDlg)) { // popdown succesful
1194 // MarkMenuItem("Edit.EditComment", False);
1195 // MarkMenuItem("View.Comments", False);
1196 } else // was not up
1197 EditCommentEvent();
1198 }
1199
1200 //------------------------------------------------------ Edit Tags ----------------------------------
1201
1202 static void changeTags P((int n));
1203 static char *tagsText, **resPtr;
1204
1205 static int TagsClick P((Option *opt, int n, int x, int y, char *val, int index));
1206
1207 static int
NewTagsCallback(int n)1208 NewTagsCallback (int n)
1209 {
1210 if(bookUp) SaveToBook(tagsText), DisplayBook(currentMove); else
1211 if(resPtr) { ASSIGN(*resPtr, tagsText); } else
1212 ReplaceTags(tagsText, &gameInfo);
1213 return 1;
1214 }
1215
1216 static void
NewMove()1217 NewMove ()
1218 {
1219 addToBookFlag = !addToBookFlag;
1220 }
1221
1222 static Option tagsOptions[] = {
1223 { 0, 0, 0, NULL, NULL, NULL, NULL, Label, NULL },
1224 { 200, T_VSCRL | T_FILL | T_WRAP | T_TOP, 200, NULL, (void*) &tagsText, NULL, (char **) &TagsClick, TextBox, "", &appData.tagsFont },
1225 { 0, 0, 100, NULL, (void*) &NewMove, NULL, NULL, Button, N_("add next move") },
1226 { 0,SAME_ROW,100,NULL, (void*) &changeTags, NULL, NULL, Button, N_("save changes") },
1227 { 0,SAME_ROW, 0, NULL, (void*) &NewTagsCallback, "", NULL, EndMark , "" }
1228 };
1229
TagsClick(Option * opt,int n,int x,int y,char * val,int index)1230 static int TagsClick (Option *opt, int n, int x, int y, char *val, int index)
1231 {
1232 if(!bookUp || n != 3) return FALSE; // only button-3 press in Edit Book is of interest
1233 PlayBookMove(val, index);
1234 return TRUE;
1235 }
1236
1237 static void
changeTags(int n)1238 changeTags (int n)
1239 {
1240 GenericReadout(tagsOptions, 1);
1241 if(bookUp) SaveToBook(tagsText), DisplayBook(currentMove); else
1242 if(resPtr) { ASSIGN(*resPtr, tagsText); } else
1243 ReplaceTags(tagsText, &gameInfo);
1244 }
1245
1246 void
NewTagsPopup(char * text,char * msg)1247 NewTagsPopup (char *text, char *msg)
1248 {
1249 char *title = bookUp ? _("Edit book") : _("Tags");
1250
1251 tagsOptions[2].type = bookUp ? Button : Skip;
1252 tagsOptions[3].min = bookUp ? SAME_ROW : 0;
1253 if(DialogExists(TagsDlg)) { // if already exists, alter title and content
1254 SetWidgetText(&tagsOptions[1], text, TagsDlg);
1255 SetDialogTitle(TagsDlg, title);
1256 }
1257 if(tagsText) free(tagsText); tagsText = strdup(text);
1258 tagsOptions[0].name = msg;
1259 MarkMenu("View.Tags", TagsDlg);
1260 GenericPopUp(tagsOptions, title, TagsDlg, BoardWindow, NONMODAL, appData.topLevel);
1261 }
1262
1263 void
TagsPopUp(char * tags,char * msg)1264 TagsPopUp (char *tags, char *msg)
1265 {
1266 NewTagsPopup(tags, cmailMsgLoaded ? msg : NULL);
1267 }
1268
1269 void
EditTagsPopUp(char * tags,char ** dest)1270 EditTagsPopUp (char *tags, char **dest)
1271 { // wrapper to preserve old name used in back-end
1272 resPtr = dest;
1273 NewTagsPopup(tags, NULL);
1274 }
1275
1276 void
TagsPopDown()1277 TagsPopDown()
1278 {
1279 PopDown(TagsDlg);
1280 bookUp = False;
1281 }
1282
1283 void
EditTagsProc()1284 EditTagsProc ()
1285 {
1286 if (bookUp || !PopDown(TagsDlg)) EditTagsEvent();
1287 }
1288
1289 void
AddBookMove(char * text)1290 AddBookMove (char *text)
1291 {
1292 AppendText(&tagsOptions[1], text);
1293 }
1294
1295 //---------------------------------------------- ICS Input Box ----------------------------------
1296
1297 char *icsText;
1298
1299 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
1300 #define HISTORY_SIZE 64
1301 static char *history[HISTORY_SIZE];
1302 static int histIn = 0, histP = 0;
1303
1304 static void
SaveInHistory(char * cmd)1305 SaveInHistory (char *cmd)
1306 {
1307 if (history[histIn] != NULL) {
1308 free(history[histIn]);
1309 history[histIn] = NULL;
1310 }
1311 if (*cmd == NULLCHAR) return;
1312 history[histIn] = StrSave(cmd);
1313 histIn = (histIn + 1) % HISTORY_SIZE;
1314 if (history[histIn] != NULL) {
1315 free(history[histIn]);
1316 history[histIn] = NULL;
1317 }
1318 histP = histIn;
1319 }
1320
1321 static char *
PrevInHistory(char * cmd)1322 PrevInHistory (char *cmd)
1323 {
1324 int newhp;
1325 if (histP == histIn) {
1326 if (history[histIn] != NULL) free(history[histIn]);
1327 history[histIn] = StrSave(cmd);
1328 }
1329 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
1330 if (newhp == histIn || history[newhp] == NULL) return NULL;
1331 histP = newhp;
1332 return history[histP];
1333 }
1334
1335 static char *
NextInHistory()1336 NextInHistory ()
1337 {
1338 if (histP == histIn) return NULL;
1339 histP = (histP + 1) % HISTORY_SIZE;
1340 return history[histP];
1341 }
1342 // end of borrowed code
1343
1344 #define INPUT 0
1345
1346 Option boxOptions[] = {
1347 { 30, T_TOP, 400, NULL, (void*) &icsText, NULL, NULL, TextBox, "" },
1348 { 0, NO_OK, 0, NULL, NULL, "", NULL, EndMark , "" }
1349 };
1350
1351 void
ICSInputSendText()1352 ICSInputSendText ()
1353 {
1354 char *val;
1355
1356 GetWidgetText(&boxOptions[INPUT], &val);
1357 SaveInHistory(val);
1358 SendMultiLineToICS(val);
1359 SetWidgetText(&boxOptions[INPUT], "", InputBoxDlg);
1360 }
1361
1362 void
IcsKey(int n)1363 IcsKey (int n)
1364 { // [HGM] input: let up-arrow recall previous line from history
1365 char *val = NULL; // to suppress spurious warning
1366
1367 if (!shellUp[InputBoxDlg]) return;
1368 switch(n) {
1369 case 0:
1370 ICSInputSendText();
1371 return;
1372 case 1:
1373 GetWidgetText(&boxOptions[INPUT], &val);
1374 val = PrevInHistory(val);
1375 break;
1376 case -1:
1377 val = NextInHistory();
1378 }
1379 SetWidgetText(&boxOptions[INPUT], val = val ? val : "", InputBoxDlg);
1380 SetInsertPos(&boxOptions[INPUT], strlen(val));
1381 }
1382
1383 void
ICSInputBoxPopUp()1384 ICSInputBoxPopUp ()
1385 {
1386 MarkMenu("View.ICSInputBox", InputBoxDlg);
1387 if(GenericPopUp(boxOptions, _("ICS input box"), InputBoxDlg, BoardWindow, NONMODAL, 0))
1388 AddHandler(&boxOptions[INPUT], InputBoxDlg, 3);
1389 CursorAtEnd(&boxOptions[INPUT]);
1390 }
1391
1392 void
IcsInputBoxProc()1393 IcsInputBoxProc ()
1394 {
1395 if (!PopDown(InputBoxDlg)) ICSInputBoxPopUp();
1396 }
1397
1398 //--------------------------------------------- Move Type In ------------------------------------------
1399
1400 static int TypeInOK P((int n));
1401
1402 Option typeOptions[] = {
1403 { 30, T_TOP, 400, NULL, (void*) &icsText, NULL, NULL, TextBox, "" },
1404 { 0, NO_OK, 0, NULL, (void*) &TypeInOK, "", NULL, EndMark , "" }
1405 };
1406
1407 static int
TypeInOK(int n)1408 TypeInOK (int n)
1409 {
1410 TypeInDoneEvent(icsText);
1411 return TRUE;
1412 }
1413
1414 void
PopUpMoveDialog(char firstchar)1415 PopUpMoveDialog (char firstchar)
1416 {
1417 static char buf[2];
1418 buf[0] = firstchar; ASSIGN(icsText, buf);
1419 if(GenericPopUp(typeOptions, _("Type a move"), TransientDlg, BoardWindow, MODAL, 0))
1420 AddHandler(&typeOptions[0], TransientDlg, 2);
1421 CursorAtEnd(&typeOptions[0]);
1422 }
1423
1424 void
BoxAutoPopUp(char * buf)1425 BoxAutoPopUp (char *buf)
1426 { // only used in Xaw. GTK calls ConsoleAutoPopUp in stead (when we type to board)
1427 if(!appData.autoBox) return;
1428 if(appData.icsActive) { // text typed to board in ICS mode: divert to ICS input box
1429 if(DialogExists(InputBoxDlg)) { // box already exists: append to current contents
1430 char *p, newText[MSG_SIZ];
1431 GetWidgetText(&boxOptions[INPUT], &p);
1432 snprintf(newText, MSG_SIZ, "%s%c", p, *buf);
1433 SetWidgetText(&boxOptions[INPUT], newText, InputBoxDlg);
1434 if(shellUp[InputBoxDlg]) HardSetFocus (&boxOptions[INPUT], InputBoxDlg); //why???
1435 } else icsText = buf; // box did not exist: make sure it pops up with char in it
1436 ICSInputBoxPopUp();
1437 } else PopUpMoveDialog(*buf);
1438 }
1439
1440 //------------------------------------------ Engine Settings ------------------------------------
1441
1442 void
SettingsPopUp(ChessProgramState * cps)1443 SettingsPopUp (ChessProgramState *cps)
1444 {
1445 if(!cps->nrOptions) { DisplayNote(_("Engine has no options")); return; }
1446 currentCps = cps;
1447 GenericPopUp(cps->option, _("Engine Settings"), TransientDlg, BoardWindow, MODAL, 0);
1448 }
1449
1450 void
FirstSettingsProc()1451 FirstSettingsProc ()
1452 {
1453 SettingsPopUp(&first);
1454 }
1455
1456 void
SecondSettingsProc()1457 SecondSettingsProc ()
1458 {
1459 if(WaitForEngine(&second, SettingsMenuIfReady)) return;
1460 SettingsPopUp(&second);
1461 }
1462
1463 //----------------------------------------------- Load Engine --------------------------------------
1464
1465 char *engineDir, *engineLine, *nickName, *params;
1466 Boolean isUCI, isUSI, hasBook, storeVariant, v1, addToList, useNick, secondEng;
1467
1468 static void EngSel P((int n, int sel));
1469 static int InstallOK P((int n));
1470
1471 static Option installOptions[] = {
1472 { 0,LR|T2T, 0, NULL, NULL, NULL, NULL, Label, N_("Select engine from list:") },
1473 { 300,LR|TB,200, NULL, (void*) engineMnemonic, (char*) &EngSel, NULL, ListBox, "" },
1474 { 0,SAME_ROW, 0, NULL, NULL, NULL, NULL, Break, NULL },
1475 { 0, LR, 0, NULL, NULL, NULL, NULL, Label, N_("or specify one below:") },
1476 { 0, 0, 0, NULL, (void*) &nickName, NULL, NULL, TextBox, N_("Nickname (optional):") },
1477 { 0, 0, 0, NULL, (void*) &useNick, NULL, NULL, CheckBox, N_("Use nickname in PGN player tags of engine-engine games") },
1478 { 0, 0, 0, NULL, (void*) &engineDir, NULL, NULL, PathName, N_("Engine Directory:") },
1479 { 0, 0, 0, NULL, (void*) &engineName, NULL, NULL, FileName, N_("Engine Command:") },
1480 { 0, LR, 0, NULL, NULL, NULL, NULL, Label, N_("(Directory will be derived from engine path when empty)") },
1481 { 0, 0, 0, NULL, (void*) &isUCI, NULL, NULL, CheckBox, N_("UCI") },
1482 { 0, 0, 0, NULL, (void*) &isUSI, NULL, NULL, CheckBox, N_("USI/UCCI (uses specified -uxiAdapter)") },
1483 { 0, 0, 0, NULL, (void*) &v1, NULL, NULL, CheckBox, N_("WB protocol v1 (do not wait for engine features)") },
1484 { 0, 0, 0, NULL, (void*) &hasBook, NULL, NULL, CheckBox, N_("Must not use GUI book") },
1485 { 0, 0, 0, NULL, (void*) &addToList, NULL, NULL, CheckBox, N_("Add this engine to the list") },
1486 { 0, 0, 0, NULL, (void*) &storeVariant, NULL, NULL, CheckBox, N_("Force current variant with this engine") },
1487 { 0, 0, 0, NULL, (void*) &InstallOK, "", NULL, EndMark , "" }
1488 };
1489
1490 static int
InstallOK(int n)1491 InstallOK (int n)
1492 {
1493 if(n && (n = SelectedListBoxItem(&installOptions[1])) > 0) { // called by pressing OK, and engine selected
1494 ASSIGN(engineLine, engineList[n]);
1495 }
1496 PopDown(TransientDlg); // early popdown, to allow FreezeUI to instate grab
1497 if(isUSI) {
1498 isUCI = 2; // kludge to pass isUSI to Load()
1499 if(!*appData.ucciAdapter) { ASSIGN(appData.ucciAdapter, "usi2wb -%variant \"%fcp\"\"%fd\""); } // make sure -uxiAdapter is defined
1500 }
1501 if(!secondEng) Load(&first, 0); else Load(&second, 1);
1502 return FALSE; // no double PopDown!
1503 }
1504
1505 static void
EngSel(int n,int sel)1506 EngSel (int n, int sel)
1507 {
1508 int nr;
1509 char buf[MSG_SIZ];
1510 if(sel < 1) buf[0] = NULLCHAR; // back to top level
1511 else if(engineList[sel][0] == '#') safeStrCpy(buf, engineList[sel], MSG_SIZ); // group header, open group
1512 else { // normal line, select engine
1513 ASSIGN(engineLine, engineList[sel]);
1514 InstallOK(0);
1515 return;
1516 }
1517 nr = NamesToList(firstChessProgramNames, engineList, engineMnemonic, buf); // replace list by only the group contents
1518 ASSIGN(engineMnemonic[0], buf);
1519 LoadListBox(&installOptions[1], _("# no engines are installed"), -1, -1);
1520 HighlightWithScroll(&installOptions[1], 0, nr);
1521 }
1522
1523 static void
LoadEngineProc(int engineNr,char * title)1524 LoadEngineProc (int engineNr, char *title)
1525 {
1526 isUCI = isUSI = storeVariant = v1 = useNick = False; addToList = hasBook = True; // defaults
1527 secondEng = engineNr;
1528 if(engineLine) free(engineLine); engineLine = strdup("");
1529 if(engineDir) free(engineDir); engineDir = strdup(".");
1530 if(nickName) free(nickName); nickName = strdup("");
1531 if(params) free(params); params = strdup("");
1532 ASSIGN(engineMnemonic[0], "");
1533 NamesToList(firstChessProgramNames, engineList, engineMnemonic, "");
1534 GenericPopUp(installOptions, title, TransientDlg, BoardWindow, MODAL, 0);
1535 }
1536
1537 void
LoadEngine1Proc()1538 LoadEngine1Proc ()
1539 {
1540 LoadEngineProc (0, _("Load first engine"));
1541 }
1542
1543 void
LoadEngine2Proc()1544 LoadEngine2Proc ()
1545 {
1546 LoadEngineProc (1, _("Load second engine"));
1547 }
1548
1549 //----------------------------------------------------- Edit Book -----------------------------------------
1550
1551 void
EditBookProc()1552 EditBookProc ()
1553 {
1554 EditBookEvent();
1555 }
1556
1557 //--------------------------------------------------- New Shuffle Game ------------------------------
1558
1559 static void SetRandom P((int n));
1560
1561 static int
ShuffleOK(int n)1562 ShuffleOK (int n)
1563 {
1564 ResetGameEvent();
1565 return 1;
1566 }
1567
1568 static Option shuffleOptions[] = {
1569 { 0, 0, 0, NULL, (void*) &shuffleOpenings, NULL, NULL, CheckBox, N_("shuffle") },
1570 { 0, 0, 0, NULL, (void*) &appData.fischerCastling, NULL, NULL, CheckBox, N_("Fischer castling") },
1571 { 0,-1,2000000000, NULL, (void*) &appData.defaultFrcPosition, "", NULL, Spin, N_("Start-position number:") },
1572 { 0, 0, 0, NULL, (void*) &SetRandom, NULL, NULL, Button, N_("randomize") },
1573 { 0, SAME_ROW, 0, NULL, (void*) &SetRandom, NULL, NULL, Button, N_("pick fixed") },
1574 { 0,SAME_ROW, 0, NULL, (void*) &ShuffleOK, "", NULL, EndMark , "" }
1575 };
1576
1577 static void
SetRandom(int n)1578 SetRandom (int n)
1579 {
1580 int r = n==2 ? -1 : random() & (1<<30)-1;
1581 char buf[MSG_SIZ];
1582 snprintf(buf, MSG_SIZ, "%d", r);
1583 SetWidgetText(&shuffleOptions[1], buf, TransientDlg);
1584 SetWidgetState(&shuffleOptions[0], True);
1585 }
1586
1587 void
ShuffleMenuProc()1588 ShuffleMenuProc ()
1589 {
1590 GenericPopUp(shuffleOptions, _("New Shuffle Game"), TransientDlg, BoardWindow, MODAL, 0);
1591 }
1592
1593 //------------------------------------------------------ Time Control -----------------------------------
1594
1595 static int TcOK P((int n));
1596 int tmpMoves, tmpTc, tmpInc, tmpOdds1, tmpOdds2, tcType;
1597
1598 static void SetTcType P((int n));
1599
1600 static char *
Value(int n)1601 Value (int n)
1602 {
1603 static char buf[MSG_SIZ];
1604 snprintf(buf, MSG_SIZ, "%d", n);
1605 return buf;
1606 }
1607
1608 static Option tcOptions[] = {
1609 { 0, 0, 0, NULL, (void*) &SetTcType, NULL, NULL, Button, N_("classical") },
1610 { 0,SAME_ROW,0,NULL, (void*) &SetTcType, NULL, NULL, Button, N_("incremental") },
1611 { 0,SAME_ROW,0,NULL, (void*) &SetTcType, NULL, NULL, Button, N_("fixed max") },
1612 { 0, 0, 200, NULL, (void*) &tmpMoves, NULL, NULL, Spin, N_("Moves per session:") },
1613 { 0, 0,10000, NULL, (void*) &tmpTc, NULL, NULL, Spin, N_("Initial time (min):") },
1614 { 0, 0, 10000, NULL, (void*) &tmpInc, NULL, NULL, Spin, N_("Increment or max (sec/move):") },
1615 { 0, 0, 0, NULL, NULL, NULL, NULL, Label, N_("Time-Odds factors:") },
1616 { 0, 1, 1000, NULL, (void*) &tmpOdds1, NULL, NULL, Spin, N_("Engine #1") },
1617 { 0, 1, 1000, NULL, (void*) &tmpOdds2, NULL, NULL, Spin, N_("Engine #2 / Human") },
1618 { 0, 0, 0, NULL, (void*) &TcOK, "", NULL, EndMark , "" }
1619 };
1620
1621 static int
TcOK(int n)1622 TcOK (int n)
1623 {
1624 char *tc;
1625 if(tcType == 0 && tmpMoves <= 0) return 0;
1626 if(tcType == 2 && tmpInc <= 0) return 0;
1627 GetWidgetText(&tcOptions[4], &tc); // get original text, in case it is min:sec
1628 searchTime = 0;
1629 switch(tcType) {
1630 case 0:
1631 if(!ParseTimeControl(tc, -1, tmpMoves)) return 0;
1632 appData.movesPerSession = tmpMoves;
1633 ASSIGN(appData.timeControl, tc);
1634 appData.timeIncrement = -1;
1635 break;
1636 case 1:
1637 if(!ParseTimeControl(tc, tmpInc, 0)) return 0;
1638 ASSIGN(appData.timeControl, tc);
1639 appData.timeIncrement = tmpInc;
1640 break;
1641 case 2:
1642 searchTime = tmpInc;
1643 }
1644 appData.firstTimeOdds = first.timeOdds = tmpOdds1;
1645 appData.secondTimeOdds = second.timeOdds = tmpOdds2;
1646 Reset(True, True);
1647 return 1;
1648 }
1649
1650 static void
SetTcType(int n)1651 SetTcType (int n)
1652 {
1653 switch(tcType = n) {
1654 case 0:
1655 SetWidgetText(&tcOptions[3], Value(tmpMoves), TransientDlg);
1656 SetWidgetText(&tcOptions[4], Value(tmpTc), TransientDlg);
1657 SetWidgetText(&tcOptions[5], _("Unused"), TransientDlg);
1658 break;
1659 case 1:
1660 SetWidgetText(&tcOptions[3], _("Unused"), TransientDlg);
1661 SetWidgetText(&tcOptions[4], Value(tmpTc), TransientDlg);
1662 SetWidgetText(&tcOptions[5], Value(tmpInc), TransientDlg);
1663 break;
1664 case 2:
1665 SetWidgetText(&tcOptions[3], _("Unused"), TransientDlg);
1666 SetWidgetText(&tcOptions[4], _("Unused"), TransientDlg);
1667 SetWidgetText(&tcOptions[5], Value(tmpInc), TransientDlg);
1668 }
1669 }
1670
1671 void
TimeControlProc()1672 TimeControlProc ()
1673 {
1674 tmpMoves = appData.movesPerSession;
1675 tmpInc = appData.timeIncrement; if(tmpInc < 0) tmpInc = 0;
1676 tmpOdds1 = tmpOdds2 = 1; tcType = 0;
1677 tmpTc = atoi(appData.timeControl);
1678 GenericPopUp(tcOptions, _("Time Control"), TransientDlg, BoardWindow, MODAL, 0);
1679 SetTcType(searchTime ? 2 : appData.timeIncrement < 0 ? 0 : 1);
1680 }
1681
1682 //------------------------------- Ask Question -----------------------------------------
1683
1684 int SendReply P((int n));
1685 char pendingReplyPrefix[MSG_SIZ];
1686 ProcRef pendingReplyPR;
1687 char *answer;
1688
1689 Option askOptions[] = {
1690 { 0, 0, 0, NULL, NULL, NULL, NULL, Label, NULL },
1691 { 0, 0, 0, NULL, (void*) &answer, "", NULL, TextBox, "" },
1692 { 0, 0, 0, NULL, (void*) &SendReply, "", NULL, EndMark , "" }
1693 };
1694
1695 int
SendReply(int n)1696 SendReply (int n)
1697 {
1698 char buf[MSG_SIZ];
1699 int err;
1700 char *reply=answer;
1701 // GetWidgetText(&askOptions[1], &reply);
1702 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
1703 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
1704 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
1705 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
1706 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err); // does not go into debug file??? => bug
1707 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
1708 return TRUE;
1709 }
1710
1711 void
AskQuestion(char * title,char * question,char * replyPrefix,ProcRef pr)1712 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
1713 {
1714 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
1715 pendingReplyPR = pr;
1716 ASSIGN(answer, "");
1717 askOptions[0].name = question;
1718 if(GenericPopUp(askOptions, title, AskDlg, BoardWindow, MODAL, 0))
1719 AddHandler(&askOptions[1], AskDlg, 2);
1720 }
1721
1722 //---------------------------- Promotion Popup --------------------------------------
1723
1724 static int count;
1725
1726 static void PromoPick P((int n));
1727
1728 static Option promoOptions[] = {
1729 { 0, 0, 0, NULL, (void*) &PromoPick, NULL, NULL, Button, NULL },
1730 { 0, SAME_ROW, 0, NULL, (void*) &PromoPick, NULL, NULL, Button, NULL },
1731 { 0, SAME_ROW, 0, NULL, (void*) &PromoPick, NULL, NULL, Button, NULL },
1732 { 0, SAME_ROW, 0, NULL, (void*) &PromoPick, NULL, NULL, Button, NULL },
1733 { 0, SAME_ROW, 0, NULL, (void*) &PromoPick, NULL, NULL, Button, NULL },
1734 { 0, SAME_ROW, 0, NULL, (void*) &PromoPick, NULL, NULL, Button, NULL },
1735 { 0, SAME_ROW, 0, NULL, (void*) &PromoPick, NULL, NULL, Button, NULL },
1736 { 0, SAME_ROW, 0, NULL, (void*) &PromoPick, NULL, NULL, Button, NULL },
1737 { 0, SAME_ROW | NO_OK, 0, NULL, NULL, "", NULL, EndMark , "" }
1738 };
1739
1740 static void
PromoPick(int n)1741 PromoPick (int n)
1742 {
1743 int promoChar = promoOptions[n+count].value;
1744
1745 PopDown(PromoDlg);
1746
1747 if (promoChar == 0) fromX = -1;
1748 if (fromX == -1) return;
1749
1750 if (! promoChar) {
1751 fromX = fromY = -1;
1752 ClearHighlights();
1753 return;
1754 }
1755 if(promoChar == '=' && !IS_SHOGI(gameInfo.variant)) promoChar = NULLCHAR;
1756 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
1757
1758 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
1759 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
1760 fromX = fromY = -1;
1761 }
1762
1763 static void
SetPromo(char * name,int nr,char promoChar)1764 SetPromo (char *name, int nr, char promoChar)
1765 {
1766 ASSIGN(promoOptions[nr].name, name);
1767 promoOptions[nr].value = promoChar;
1768 promoOptions[nr].min = SAME_ROW;
1769 }
1770
1771 void
PromotionPopUp(char choice)1772 PromotionPopUp (char choice)
1773 { // choice depends on variant: prepare dialog acordingly
1774 count = 8;
1775 SetPromo(_("Cancel"), --count, -1); // Beware: GenericPopUp cannot handle user buttons named "cancel" (lowe case)!
1776 if(choice != '+' && !IS_SHOGI(gameInfo.variant)) {
1777 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
1778 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
1779 gameInfo.variant == VariantGiveaway) {
1780 SetPromo(_("King"), --count, 'k');
1781 }
1782 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
1783 SetPromo(_("Captain"), --count, 'c');
1784 SetPromo(_("Lieutenant"), --count, 'l');
1785 SetPromo(_("General"), --count, 'g');
1786 SetPromo(_("Warlord"), --count, 'w');
1787 } else {
1788 SetPromo(_("Knight"), --count, 'n');
1789 SetPromo(_("Bishop"), --count, 'b');
1790 SetPromo(_("Rook"), --count, 'r');
1791 if(gameInfo.variant == VariantCapablanca ||
1792 gameInfo.variant == VariantGothic ||
1793 gameInfo.variant == VariantCapaRandom) {
1794 SetPromo(_("Archbishop"), --count, 'a');
1795 SetPromo(_("Chancellor"), --count, 'c');
1796 }
1797 SetPromo(_("Queen"), --count, 'q');
1798 if(gameInfo.variant == VariantChuChess)
1799 SetPromo(_("Lion"), --count, 'l');
1800 }
1801 } else // [HGM] shogi
1802 {
1803 SetPromo(_("Defer"), --count, '=');
1804 SetPromo(_("Promote"), --count, '+');
1805 }
1806 promoOptions[count].min = 0;
1807 GenericPopUp(promoOptions + count, "Promotion", PromoDlg, BoardWindow, NONMODAL, 0);
1808 }
1809
1810 //---------------------------- Chat Windows ----------------------------------------------
1811
1812 static char *line, *memo, *chatMemo, *partner, *texts[MAX_CHAT], dirty[MAX_CHAT], *inputs[MAX_CHAT], *icsLine, *tmpLine;
1813 static int activePartner;
1814 int hidden = 1;
1815
1816 void ChatSwitch P((int n));
1817 int ChatOK P((int n));
1818
1819 #define CHAT_ICS 6
1820 #define CHAT_PARTNER 8
1821 #define CHAT_OUT 11
1822 #define CHAT_PANE 12
1823 #define CHAT_IN 13
1824
1825 void PaneSwitch P((void));
1826 void ClearChat P((void));
1827
1828 WindowPlacement wpTextMenu;
1829
1830 int
ContextMenu(Option * opt,int button,int x,int y,char * text,int index)1831 ContextMenu (Option *opt, int button, int x, int y, char *text, int index)
1832 { // callback for ICS-output clicks; handles button 3, passes on other events
1833 int h;
1834 if(button == -3) return TRUE; // supress default GTK context menu on up-click
1835 if(button != 3) return FALSE;
1836 if(index == -1) { // pre-existing selection in memo
1837 strncpy(clickedWord, text, MSG_SIZ);
1838 } else { // figure out what word was clicked
1839 char *start, *end;
1840 start = end = text + index;
1841 while(isalnum(*end)) end++;
1842 while(start > text && isalnum(start[-1])) start--;
1843 clickedWord[0] = NULLCHAR;
1844 if(end-start >= 80) end = start + 80; // intended for small words and numbers
1845 strncpy(clickedWord, start, end-start); clickedWord[end-start] = NULLCHAR;
1846 }
1847 click = !shellUp[TextMenuDlg]; // request auto-popdown of textmenu when we popped it up
1848 h = wpTextMenu.height; // remembered height of text menu
1849 if(h <= 0) h = 65; // when not available, position w.r.t. top
1850 GetPlacement(ChatDlg, &wpTextMenu);
1851 if(opt->target == (void*) &chatMemo) wpTextMenu.y += (wpTextMenu.height - 30)/2; // click in chat
1852 wpTextMenu.x += x - 50; wpTextMenu.y += y - h + 50; // request positioning
1853 if(wpTextMenu.x < 0) wpTextMenu.x = 0;
1854 if(wpTextMenu.y < 0) wpTextMenu.y = 0;
1855 wpTextMenu.width = wpTextMenu.height = -1;
1856 IcsTextPopUp();
1857 return TRUE;
1858 }
1859
1860 Option chatOptions[] = {
1861 { 0, 0, 0, NULL, NULL, NULL, NULL, Label , N_("Chats:") },
1862 { 1, SAME_ROW|TT, 75, NULL, (void*) &ChatSwitch, NULL, NULL, Button, N_("New Chat") },
1863 { 2, SAME_ROW|TT, 75, NULL, (void*) &ChatSwitch, NULL, NULL, Button, N_("New Chat") },
1864 { 3, SAME_ROW|TT, 75, NULL, (void*) &ChatSwitch, NULL, NULL, Button, N_("New Chat") },
1865 { 4, SAME_ROW|TT, 75, NULL, (void*) &ChatSwitch, NULL, NULL, Button, N_("New Chat") },
1866 { 5, SAME_ROW|TT, 75, NULL, (void*) &ChatSwitch, NULL, NULL, Button, N_("New Chat") },
1867 { 250, T_VSCRL | T_FILL | T_WRAP | T_TOP, 510, NULL, (void*) &memo, NULL, (void*) &ContextMenu, TextBox, "" },
1868 { 0, 0, 0, NULL, NULL, "", NULL, Break , "" },
1869 { 0, T_TOP, 100, NULL, (void*) &partner, NULL, NULL, TextBox, N_("Chat partner:") },
1870 { 0, SAME_ROW, 0, NULL, (void*) &ClearChat, NULL, NULL, Button, N_("End Chat") },
1871 { 0, SAME_ROW, 0, NULL, (void*) &PaneSwitch, NULL, NULL, Button, N_("Hide") },
1872 { 250, T_VSCRL | T_FILL | T_WRAP | T_TOP, 510, NULL, (void*) &chatMemo, NULL, (void*) &ContextMenu, TextBox, "" },
1873 { 0, 0, 0, NULL, NULL, "", NULL, Break , "" },
1874 { 0, 0, 510, NULL, (void*) &line, NULL, NULL, TextBox, "" },
1875 { 0, NO_OK|SAME_ROW, 0, NULL, (void*) &ChatOK, NULL, NULL, EndMark , "" }
1876 };
1877
1878 static void
PutText(char * text,int pos)1879 PutText (char *text, int pos)
1880 {
1881 char buf[MSG_SIZ], *p;
1882 DialogClass dlg = ChatDlg;
1883 Option *opt = &chatOptions[CHAT_IN];
1884
1885 if(strstr(text, "$add ") == text) {
1886 GetWidgetText(&boxOptions[INPUT], &p);
1887 snprintf(buf, MSG_SIZ, "%s%s", p, text+5); text = buf;
1888 pos += strlen(p) - 5;
1889 }
1890 if(shellUp[InputBoxDlg]) opt = &boxOptions[INPUT], dlg = InputBoxDlg; // for the benefit of Xaw give priority to ICS Input Box
1891 SetWidgetText(opt, text, dlg);
1892 SetInsertPos(opt, pos);
1893 HardSetFocus(opt, dlg);
1894 CursorAtEnd(opt);
1895 }
1896
1897 int
IcsHist(int n,Option * opt,DialogClass dlg)1898 IcsHist (int n, Option *opt, DialogClass dlg)
1899 { // [HGM] input: let up-arrow recall previous line from history
1900 char *val = NULL; // to suppress spurious warning
1901 int chat, start;
1902
1903 if(opt != &chatOptions[CHAT_IN] && !(opt == &chatOptions[CHAT_PARTNER] && n == 33)) return 0;
1904 switch(n) {
1905 case 5:
1906 if(!hidden) ClearChat();
1907 break;
1908 case 8:
1909 if(!hidden) PaneSwitch();
1910 break;
1911 case 33: // <Esc>
1912 if(1) BoardToTop(); else
1913 if(hidden) BoardToTop();
1914 else PaneSwitch();
1915 break;
1916 case 15:
1917 NewChat(lastTalker);
1918 break;
1919 case 14:
1920 for(chat=0; chat < MAX_CHAT; chat++) if(!chatPartner[chat][0]) break;
1921 if(chat < MAX_CHAT) ChatSwitch(chat + 1);
1922 break;
1923 case 10: // <Tab>
1924 chat = start = (activePartner - hidden + MAX_CHAT) % MAX_CHAT;
1925 while(!dirty[chat = (chat + 1)%MAX_CHAT]) if(chat == start) break;
1926 if(!dirty[chat])
1927 while(!chatPartner[chat = (chat + 1)%MAX_CHAT][0]) if(chat == start) break;
1928 if(!chatPartner[chat][0]) break; // if all unused, ignore
1929 ChatSwitch(chat + 1);
1930 break;
1931 case 1:
1932 GetWidgetText(opt, &val);
1933 val = PrevInHistory(val);
1934 break;
1935 case -1:
1936 val = NextInHistory();
1937 }
1938 SetWidgetText(opt, val = val ? val : "", dlg);
1939 SetInsertPos(opt, strlen(val));
1940 return 1;
1941 }
1942
1943 void
OutputChatMessage(int partner,char * mess)1944 OutputChatMessage (int partner, char *mess)
1945 {
1946 char *p = texts[partner];
1947 int len = strlen(mess) + 1;
1948
1949 if(!DialogExists(ChatDlg)) return;
1950 if(p) len += strlen(p);
1951 texts[partner] = (char*) malloc(len);
1952 snprintf(texts[partner], len, "%s%s", p ? p : "", mess);
1953 FREE(p);
1954 if(partner == activePartner && !hidden) {
1955 AppendText(&chatOptions[CHAT_OUT], mess);
1956 SetInsertPos(&chatOptions[CHAT_OUT], len-2);
1957 } else {
1958 SetColor("#FFC000", &chatOptions[partner + 1]);
1959 dirty[partner] = 1;
1960 }
1961 }
1962
1963 int
ChatOK(int n)1964 ChatOK (int n)
1965 { // can only be called through <Enter> in chat-partner text-edit, as there is no OK button
1966 char buf[MSG_SIZ];
1967
1968 if(!hidden && (!partner || strcmp(partner, chatPartner[activePartner]) || !*partner)) {
1969 safeStrCpy(chatPartner[activePartner], partner, MSG_SIZ);
1970 SetWidgetText(&chatOptions[CHAT_OUT], "", -1); // clear text if we alter partner
1971 SetWidgetText(&chatOptions[CHAT_IN], "", ChatDlg); // clear text if we alter partner
1972 SetWidgetLabel(&chatOptions[activePartner+1], chatPartner[activePartner][0] ? chatPartner[activePartner] : _("New Chat"));
1973 if(!*partner) PaneSwitch();
1974 HardSetFocus(&chatOptions[CHAT_IN], 0);
1975 }
1976 if(line[0] || hidden) { // something was typed (for ICS commands we also allow empty line!)
1977 SetWidgetText(&chatOptions[CHAT_IN], "", ChatDlg);
1978 // from here on it could be back-end
1979 if(line[strlen(line)-1] == '\n') line[strlen(line)-1] = NULLCHAR;
1980 SaveInHistory(line);
1981 if(hidden || !*chatPartner[activePartner]) snprintf(buf, MSG_SIZ, "%s\n", line); else // command for ICS
1982 if(!strcmp("whispers", chatPartner[activePartner]))
1983 snprintf(buf, MSG_SIZ, "whisper %s\n", line); // WHISPER box uses "whisper" to send
1984 else if(!strcmp("shouts", chatPartner[activePartner]))
1985 snprintf(buf, MSG_SIZ, "shout %s\n", line); // SHOUT box uses "shout" to send
1986 else if(!strcmp("c-shouts", chatPartner[activePartner]))
1987 snprintf(buf, MSG_SIZ, "cshout %s\n", line); // C-SHOUT box uses "cshout" to send
1988 else if(!strcmp("kibitzes", chatPartner[activePartner]))
1989 snprintf(buf, MSG_SIZ, "kibitz %s\n", line); // KIBITZ box uses "kibitz" to send
1990 else {
1991 if(!atoi(chatPartner[activePartner])) {
1992 snprintf(buf, MSG_SIZ, "> %s\n", line); // echo only tells to handle, not channel
1993 OutputChatMessage(activePartner, buf);
1994 snprintf(buf, MSG_SIZ, "xtell %s %s\n", chatPartner[activePartner], line);
1995 } else
1996 snprintf(buf, MSG_SIZ, "tell %s %s\n", chatPartner[activePartner], line);
1997 }
1998 SendToICS(buf);
1999 }
2000 return FALSE; // never pop down
2001 }
2002
2003 void
DelayedSetText()2004 DelayedSetText ()
2005 {
2006 SetWidgetText(&chatOptions[CHAT_IN], tmpLine, -1); // leave focus on chat-partner field!
2007 SetInsertPos(&chatOptions[CHAT_IN], strlen(tmpLine));
2008 }
2009
2010 void
DelayedScroll()2011 DelayedScroll ()
2012 { // If we do this immediately it does it before shrinking the memo, so the lower half remains hidden (Ughh!)
2013 SetInsertPos(&chatOptions[CHAT_ICS], 999999);
2014 SetWidgetText(&chatOptions[CHAT_IN], tmpLine, ChatDlg);
2015 SetInsertPos(&chatOptions[CHAT_IN], strlen(tmpLine));
2016 }
2017
2018 void
ChatSwitch(int n)2019 ChatSwitch (int n)
2020 {
2021 int i, j;
2022 char *v;
2023 if(chatOptions[CHAT_ICS].type == Skip) hidden = 0; // In Xaw there is no ICS pane we can hide behind
2024 Show(&chatOptions[CHAT_PANE], 0); // show
2025 if(hidden) ScheduleDelayedEvent(DelayedScroll, 50); // Awful!
2026 else ScheduleDelayedEvent(DelayedSetText, 50);
2027 GetWidgetText(&chatOptions[CHAT_IN], &v);
2028 if(hidden) { ASSIGN(icsLine, v); } else { ASSIGN(inputs[activePartner], v); }
2029 hidden = 0;
2030 activePartner = --n;
2031 if(!texts[n]) texts[n] = strdup("");
2032 dirty[n] = 0;
2033 SetWidgetText(&chatOptions[CHAT_OUT], texts[n], ChatDlg);
2034 SetInsertPos(&chatOptions[CHAT_OUT], strlen(texts[n]));
2035 SetWidgetText(&chatOptions[CHAT_PARTNER], chatPartner[n], ChatDlg);
2036 for(i=j=0; i<MAX_CHAT; i++) {
2037 SetWidgetLabel(&chatOptions[++j], *chatPartner[i] ? chatPartner[i] : _("New Chat"));
2038 SetColor(dirty[i] ? "#FFC000" : "#FFFFFF", &chatOptions[j]);
2039 }
2040 if(!inputs[n]) { ASSIGN(inputs[n], ""); }
2041 // SetWidgetText(&chatOptions[CHAT_IN], inputs[n], ChatDlg); // does not work (in this widget only)
2042 // SetInsertPos(&chatOptions[CHAT_IN], strlen(inputs[n]));
2043 tmpLine = inputs[n]; // for the delayed event
2044 HardSetFocus(&chatOptions[strcmp(chatPartner[n], "") ? CHAT_IN : CHAT_PARTNER], 0);
2045 }
2046
2047 void
PaneSwitch()2048 PaneSwitch ()
2049 {
2050 char *v;
2051 Show(&chatOptions[CHAT_PANE], hidden = 1); // hide
2052 GetWidgetText(&chatOptions[CHAT_IN], &v);
2053 ASSIGN(inputs[activePartner], v);
2054 if(!icsLine) { ASSIGN(icsLine, ""); }
2055 tmpLine = icsLine; ScheduleDelayedEvent(DelayedSetText, 50);
2056 // SetWidgetText(&chatOptions[CHAT_IN], icsLine, ChatDlg); // does not work (in this widget only)
2057 // SetInsertPos(&chatOptions[CHAT_IN], strlen(icsLine));
2058 }
2059
2060 void
ClearChat()2061 ClearChat ()
2062 { // clear the chat to make it free for other use
2063 chatPartner[activePartner][0] = NULLCHAR;
2064 ASSIGN(texts[activePartner], "");
2065 ASSIGN(inputs[activePartner], "");
2066 SetWidgetText(&chatOptions[CHAT_PARTNER], "", ChatDlg);
2067 SetWidgetText(&chatOptions[CHAT_OUT], "", ChatDlg);
2068 SetWidgetText(&chatOptions[CHAT_IN], "", ChatDlg);
2069 SetWidgetLabel(&chatOptions[activePartner+1], _("New Chat"));
2070 HardSetFocus(&chatOptions[CHAT_PARTNER], 0);
2071 }
2072
2073 static void
NewChat(char * name)2074 NewChat (char *name)
2075 { // open a chat on program request. If no empty one available, use last
2076 int i;
2077 for(i=0; i<MAX_CHAT-1; i++) if(!chatPartner[i][0]) break;
2078 safeStrCpy(chatPartner[i], name, MSG_SIZ);
2079 ChatSwitch(i+1);
2080 }
2081
2082 void
ConsoleWrite(char * message,int count)2083 ConsoleWrite(char *message, int count)
2084 {
2085 if(shellUp[ChatDlg] && chatOptions[CHAT_ICS].type != Skip) { // in Xaw this is a no-op
2086 AppendColorized(&chatOptions[CHAT_ICS], message, count);
2087 SetInsertPos(&chatOptions[CHAT_ICS], 999999);
2088 }
2089 }
2090
2091 void
ChatPopUp()2092 ChatPopUp ()
2093 {
2094 if(GenericPopUp(chatOptions, _("ICS Interaction"), ChatDlg, BoardWindow, NONMODAL, appData.topLevel))
2095 AddHandler(&chatOptions[CHAT_PARTNER], ChatDlg, 2), AddHandler(&chatOptions[CHAT_IN], ChatDlg, 2); // treats return as OK
2096 Show(&chatOptions[CHAT_PANE], hidden = 1); // hide
2097 // HardSetFocus(&chatOptions[CHAT_IN], 0);
2098 MarkMenu("View.OpenChatWindow", ChatDlg);
2099 CursorAtEnd(&chatOptions[CHAT_IN]);
2100 }
2101
2102 void
ChatProc()2103 ChatProc ()
2104 {
2105 if(shellUp[ChatDlg]) PopDown(ChatDlg);
2106 else ChatPopUp();
2107 }
2108
2109 void
ConsoleAutoPopUp(char * buf)2110 ConsoleAutoPopUp (char *buf)
2111 {
2112 if(*buf == 27) { if(appData.icsActive && DialogExists(ChatDlg)) HardSetFocus (&chatOptions[CHAT_IN], ChatDlg); return; }
2113 if(!appData.autoBox) return;
2114 if(appData.icsActive) { // text typed to board in ICS mode: divert to ICS input box
2115 if(DialogExists(ChatDlg)) { // box already exists: append to current contents
2116 char *p, newText[MSG_SIZ];
2117 GetWidgetText(&chatOptions[CHAT_IN], &p);
2118 snprintf(newText, MSG_SIZ, "%s%c", p, *buf);
2119 SetWidgetText(&chatOptions[CHAT_IN], newText, ChatDlg);
2120 if(shellUp[ChatDlg]) HardSetFocus (&chatOptions[CHAT_IN], ChatDlg); //why???
2121 } else { ASSIGN(line, buf); } // box did not exist: make sure it pops up with char in it
2122 ChatPopUp();
2123 } else PopUpMoveDialog(*buf);
2124 }
2125
2126 //--------------------------------- Game-List options dialog ------------------------------------------
2127
2128 char *strings[LPUSERGLT_SIZE];
2129 int stringPtr;
2130
2131 void
GLT_ClearList()2132 GLT_ClearList ()
2133 {
2134 strings[0] = NULL;
2135 stringPtr = 0;
2136 }
2137
2138 void
GLT_AddToList(char * name)2139 GLT_AddToList (char *name)
2140 {
2141 strings[stringPtr++] = name;
2142 strings[stringPtr] = NULL;
2143 }
2144
2145 Boolean
GLT_GetFromList(int index,char * name)2146 GLT_GetFromList (int index, char *name)
2147 {
2148 safeStrCpy(name, strings[index], MSG_SIZ);
2149 return TRUE;
2150 }
2151
2152 void
GLT_DeSelectList()2153 GLT_DeSelectList ()
2154 {
2155 }
2156
2157 static void GLT_Button P((int n));
2158 static int GLT_OK P((int n));
2159
2160 static Option listOptions[] = {
2161 {300, LR|TB, 200, NULL, (void*) strings, NULL, NULL, ListBox, "" }, // For GTK we need to specify a height, as default would just show 3 lines
2162 { 0, 0, 0, NULL, (void*) &GLT_Button, NULL, NULL, Button, N_("factory") },
2163 { 0, SAME_ROW, 0, NULL, (void*) &GLT_Button, NULL, NULL, Button, N_("up") },
2164 { 0, SAME_ROW, 0, NULL, (void*) &GLT_Button, NULL, NULL, Button, N_("down") },
2165 { 0, SAME_ROW, 0, NULL, (void*) &GLT_OK, "", NULL, EndMark , "" }
2166 };
2167
2168 static int
GLT_OK(int n)2169 GLT_OK (int n)
2170 {
2171 GLT_ParseList();
2172 appData.gameListTags = strdup(lpUserGLT);
2173 GameListUpdate();
2174 return 1;
2175 }
2176
2177 static void
GLT_Button(int n)2178 GLT_Button (int n)
2179 {
2180 int index = SelectedListBoxItem (&listOptions[0]);
2181 char *p;
2182 if (index < 0) {
2183 DisplayError(_("No tag selected"), 0);
2184 return;
2185 }
2186 p = strings[index];
2187 if (n == 3) {
2188 if(index >= strlen(GLT_ALL_TAGS)) return;
2189 strings[index] = strings[index+1];
2190 strings[++index] = p;
2191 LoadListBox(&listOptions[0], "?", index, index-1); // only change the two specified entries
2192 } else
2193 if (n == 2) {
2194 if(index == 0) return;
2195 strings[index] = strings[index-1];
2196 strings[--index] = p;
2197 LoadListBox(&listOptions[0], "?", index, index+1);
2198 } else
2199 if (n == 1) {
2200 safeStrCpy(lpUserGLT, GLT_DEFAULT_TAGS, LPUSERGLT_SIZE);
2201 GLT_TagsToList(lpUserGLT);
2202 index = 0;
2203 LoadListBox(&listOptions[0], "?", -1, -1);
2204 }
2205 HighlightListBoxItem(&listOptions[0], index);
2206 }
2207
2208 void
GameListOptionsPopUp(DialogClass parent)2209 GameListOptionsPopUp (DialogClass parent)
2210 {
2211 safeStrCpy(lpUserGLT, appData.gameListTags, LPUSERGLT_SIZE);
2212 GLT_TagsToList(lpUserGLT);
2213
2214 GenericPopUp(listOptions, _("Game-list options"), TransientDlg, parent, MODAL, 0);
2215 }
2216
2217 void
GameListOptionsProc()2218 GameListOptionsProc ()
2219 {
2220 GameListOptionsPopUp(BoardWindow);
2221 }
2222
2223 //----------------------------- Error popup in various uses -----------------------------
2224
2225 /*
2226 * [HGM] Note:
2227 * XBoard has always had some pathologic behavior with multiple simultaneous error popups,
2228 * (which can occur even for modal popups when asynchrounous events, e.g. caused by engine, request a popup),
2229 * and this new implementation reproduces that as well:
2230 * Only the shell of the last instance is remembered in shells[ErrorDlg] (which replaces errorShell),
2231 * so that PopDowns ordered from the code always refer to that instance, and once that is down,
2232 * have no clue as to how to reach the others. For the Delete Window button calling PopDown this
2233 * has now been repaired, as the action routine assigned to it gets the shell passed as argument.
2234 */
2235
2236 int errorUp = False;
2237
2238 void
ErrorPopDown()2239 ErrorPopDown ()
2240 {
2241 if (!errorUp) return;
2242 dialogError = errorUp = False;
2243 PopDown(ErrorDlg); PopDown(FatalDlg); // on explicit request we pop down any error dialog
2244 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
2245 }
2246
2247 static int
ErrorOK(int n)2248 ErrorOK (int n)
2249 {
2250 dialogError = errorUp = False;
2251 PopDown(n == 1 ? FatalDlg : ErrorDlg); // kludge: non-modal dialogs have one less (dummy) option
2252 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
2253 return FALSE; // prevent second Popdown !
2254 }
2255
2256 static Option errorOptions[] = {
2257 { 0, 0, 0, NULL, NULL, NULL, NULL, Label, NULL }, // dummy option: will never be displayed
2258 { 0, 0, 0, NULL, NULL, NULL, NULL, Label, NULL }, // textValue field will be set before popup
2259 { 0,NO_CANCEL,0, NULL, (void*) &ErrorOK, "", NULL, EndMark , "" }
2260 };
2261
2262 void
ErrorPopUp(char * title,char * label,int modal)2263 ErrorPopUp (char *title, char *label, int modal)
2264 {
2265 errorUp = True;
2266 errorOptions[1].name = label;
2267 if(dialogError = shellUp[TransientDlg])
2268 GenericPopUp(errorOptions+1, title, FatalDlg, TransientDlg, MODAL, 0); // pop up as daughter of the transient dialog
2269 else
2270 GenericPopUp(errorOptions+modal, title, modal ? FatalDlg: ErrorDlg, BoardWindow, modal, 0); // kludge: option start address indicates modality
2271 }
2272
2273 void
DisplayError(String message,int error)2274 DisplayError (String message, int error)
2275 {
2276 char buf[MSG_SIZ];
2277
2278 if (error == 0) {
2279 if (appData.debugMode || appData.matchMode) {
2280 fprintf(stderr, "%s: %s\n", programName, message);
2281 }
2282 } else {
2283 if (appData.debugMode || appData.matchMode) {
2284 fprintf(stderr, "%s: %s: %s\n",
2285 programName, message, strerror(error));
2286 }
2287 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
2288 message = buf;
2289 }
2290 ErrorPopUp(_("Error"), message, FALSE);
2291 }
2292
2293
2294 void
DisplayMoveError(String message)2295 DisplayMoveError (String message)
2296 {
2297 fromX = fromY = -1;
2298 ClearHighlights();
2299 DrawPosition(TRUE, NULL); // selective redraw would miss the from-square of the rejected move, displayed empty after drag, but not marked damaged!
2300 if (appData.debugMode || appData.matchMode) {
2301 fprintf(stderr, "%s: %s\n", programName, message);
2302 }
2303 if (appData.popupMoveErrors) {
2304 ErrorPopUp(_("Error"), message, FALSE);
2305 } else {
2306 DisplayMessage(message, "");
2307 }
2308 }
2309
2310
2311 void
DisplayFatalError(String message,int error,int status)2312 DisplayFatalError (String message, int error, int status)
2313 {
2314 char buf[MSG_SIZ];
2315
2316 errorExitStatus = status;
2317 if (error == 0) {
2318 fprintf(stderr, "%s: %s\n", programName, message);
2319 } else {
2320 fprintf(stderr, "%s: %s: %s\n",
2321 programName, message, strerror(error));
2322 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
2323 message = buf;
2324 }
2325 if(mainOptions[W_BOARD].handle) {
2326 if (appData.popupExitMessage) {
2327 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
2328 } else {
2329 ExitEvent(status);
2330 }
2331 }
2332 }
2333
2334 void
DisplayInformation(String message)2335 DisplayInformation (String message)
2336 {
2337 ErrorPopDown();
2338 ErrorPopUp(_("Information"), message, TRUE);
2339 }
2340
2341 void
DisplayNote(String message)2342 DisplayNote (String message)
2343 {
2344 ErrorPopDown();
2345 ErrorPopUp(_("Note"), message, FALSE);
2346 }
2347
2348 void
DisplayTitle(char * text)2349 DisplayTitle (char *text)
2350 {
2351 char title[MSG_SIZ];
2352 char icon[MSG_SIZ];
2353
2354 if (text == NULL) text = "";
2355
2356 if(partnerUp) { SetDialogTitle(DummyDlg, text); return; }
2357
2358 if (*text != NULLCHAR) {
2359 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
2360 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
2361 } else if (appData.icsActive) {
2362 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
2363 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
2364 } else if (appData.cmailGameName[0] != NULLCHAR) {
2365 snprintf(icon, sizeof(icon), "%s", "CMail");
2366 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
2367 #ifdef GOTHIC
2368 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
2369 } else if (gameInfo.variant == VariantGothic) {
2370 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
2371 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
2372 #endif
2373 #ifdef FALCON
2374 } else if (gameInfo.variant == VariantFalcon) {
2375 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
2376 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
2377 #endif
2378 } else if (appData.noChessProgram) {
2379 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
2380 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
2381 } else {
2382 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
2383 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
2384 }
2385 SetWindowTitle(text, title, icon);
2386 }
2387
2388 #define PAUSE_BUTTON "P"
2389 #define PIECE_MENU_SIZE 18
2390 static String pieceMenuStrings[2][PIECE_MENU_SIZE+1] = {
2391 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
2392 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
2393 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
2394 N_("Empty square"), N_("Clear board"), NULL },
2395 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
2396 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
2397 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
2398 N_("Empty square"), N_("Clear board"), NULL }
2399 };
2400 /* must be in same order as pieceMenuStrings! */
2401 static ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
2402 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
2403 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
2404 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
2405 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
2406 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
2407 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
2408 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
2409 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
2410 };
2411
2412 #define DROP_MENU_SIZE 6
2413 static String dropMenuStrings[DROP_MENU_SIZE+1] = {
2414 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen"), NULL
2415 };
2416 /* must be in same order as dropMenuStrings! */
2417 static ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
2418 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
2419 WhiteRook, WhiteQueen
2420 };
2421
2422 // [HGM] experimental code to pop up window just like the main window, using GenercicPopUp
2423
2424 static Option *Exp P((int n, int x, int y));
2425 void MenuCallback P((int n));
2426 void SizeKludge P((int n));
2427 static Option *LogoW P((int n, int x, int y));
2428 static Option *LogoB P((int n, int x, int y));
2429
2430 static int pmFromX = -1, pmFromY = -1;
2431 void *userLogo;
2432
2433 void
DisplayLogos(Option * w1,Option * w2)2434 DisplayLogos (Option *w1, Option *w2)
2435 {
2436 void *whiteLogo = first.programLogo, *blackLogo = second.programLogo;
2437 if(appData.autoLogo) {
2438 if(appData.noChessProgram) whiteLogo = blackLogo = NULL;
2439 if(appData.icsActive) whiteLogo = blackLogo = second.programLogo;
2440 switch(gameMode) { // pick logos based on game mode
2441 case IcsObserving:
2442 whiteLogo = second.programLogo; // ICS logo
2443 blackLogo = second.programLogo;
2444 default:
2445 break;
2446 case IcsPlayingWhite:
2447 if(!appData.zippyPlay) whiteLogo = userLogo;
2448 blackLogo = second.programLogo; // ICS logo
2449 break;
2450 case IcsPlayingBlack:
2451 whiteLogo = second.programLogo; // ICS logo
2452 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
2453 break;
2454 case TwoMachinesPlay:
2455 if(first.twoMachinesColor[0] == 'b') {
2456 whiteLogo = second.programLogo;
2457 blackLogo = first.programLogo;
2458 }
2459 break;
2460 case MachinePlaysWhite:
2461 blackLogo = userLogo;
2462 break;
2463 case MachinePlaysBlack:
2464 whiteLogo = userLogo;
2465 blackLogo = first.programLogo;
2466 }
2467 }
2468 DrawLogo(w1, whiteLogo);
2469 DrawLogo(w2, blackLogo);
2470 }
2471
2472 static void
PMSelect(int n)2473 PMSelect (int n)
2474 { // user callback for board context menus
2475 if (pmFromX < 0 || pmFromY < 0) return;
2476 if(n == W_DROP) DropMenuEvent(dropMenuTranslation[values[n]], pmFromX, pmFromY);
2477 else EditPositionMenuEvent(pieceMenuTranslation[n - W_MENUW][values[n]], pmFromX, pmFromY);
2478 }
2479
2480 static void
CCB(int n)2481 CCB (int n)
2482 {
2483 shiftKey = (ShiftKeys() & 3) != 0;
2484 if(n < 0) { // button != 1
2485 n = -n;
2486 if(shiftKey && (gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack)) {
2487 AdjustClock(n == W_BLACK, 1);
2488 }
2489 } else
2490 ClockClick(n == W_BLACK);
2491 }
2492
2493 Option mainOptions[] = { // description of main window in terms of generic dialog creator
2494 { 0, 0xCA, 0, NULL, NULL, "", NULL, BarBegin, "" }, // menu bar
2495 { 0, COMBO_CALLBACK, 0, NULL, (void*)&MenuCallback, NULL, NULL, DropDown, N_("_File") },
2496 { 0, COMBO_CALLBACK, 0, NULL, (void*)&MenuCallback, NULL, NULL, DropDown, N_("_Edit") },
2497 { 0, COMBO_CALLBACK, 0, NULL, (void*)&MenuCallback, NULL, NULL, DropDown, N_("_View") },
2498 { 0, COMBO_CALLBACK, 0, NULL, (void*)&MenuCallback, NULL, NULL, DropDown, N_("_Mode") },
2499 { 0, COMBO_CALLBACK, 0, NULL, (void*)&MenuCallback, NULL, NULL, DropDown, N_("_Action") },
2500 { 0, COMBO_CALLBACK, 0, NULL, (void*)&MenuCallback, NULL, NULL, DropDown, N_("E_ngine") },
2501 { 0, COMBO_CALLBACK, 0, NULL, (void*)&MenuCallback, NULL, NULL, DropDown, N_("_Options") },
2502 { 0, COMBO_CALLBACK, 0, NULL, (void*)&MenuCallback, NULL, NULL, DropDown, N_("_Help") },
2503 { 0, 0, 0, NULL, (void*)&SizeKludge, "", NULL, BarEnd, "" },
2504 { 0, LR|T2T|BORDER|SAME_ROW, 0, NULL, NULL, NULL, NULL, Label, "1" }, // optional title in window
2505 { 50, LL|TT, 100, NULL, (void*) &LogoW, NULL, NULL, Skip, "" }, // white logo
2506 { 12, L2L|T2T, 200, NULL, (void*) &CCB, NULL, NULL, Label, "White" }, // white clock
2507 { 13, R2R|T2T|SAME_ROW, 200, NULL, (void*) &CCB, NULL, NULL, Label, "Black" }, // black clock
2508 { 50, RR|TT|SAME_ROW, 100, NULL, (void*) &LogoB, NULL, NULL, Skip, "" }, // black logo
2509 { 0, LR|T2T|BORDER, 401, NULL, NULL, "", NULL, Skip, "2" }, // backup for title in window (if no room for other)
2510 { 0, LR|T2T|BORDER, 270, NULL, NULL, NULL, NULL, Label, "message", &appData.font }, // message field
2511 { 0, RR|TT|SAME_ROW, 125, NULL, NULL, "", NULL, BoxBegin, "" }, // (optional) button bar
2512 { 0, 0, 0, NULL, (void*) &ToStartEvent, NULL, NULL, Button, N_("<<"), &appData.font },
2513 { 0, SAME_ROW, 0, NULL, (void*) &BackwardEvent, NULL, NULL, Button, N_("<"), &appData.font },
2514 { 0, SAME_ROW, 0, NULL, (void*) &PauseEvent, NULL, NULL, Button, N_(PAUSE_BUTTON), &appData.font },
2515 { 0, SAME_ROW, 0, NULL, (void*) &ForwardEvent, NULL, NULL, Button, N_(">"), &appData.font },
2516 { 0, SAME_ROW, 0, NULL, (void*) &ToEndEvent, NULL, NULL, Button, N_(">>"), &appData.font },
2517 { 0, 0, 0, NULL, NULL, "", NULL, BoxEnd, "" },
2518 { 401, LR|TB, 401, NULL, (char*) &Exp, NULL, NULL, Graph, "shadow board" }, // board
2519 { 2, COMBO_CALLBACK, 0, NULL, (void*) &PMSelect, NULL, pieceMenuStrings[0], PopUp, "menuW" },
2520 { 2, COMBO_CALLBACK, 0, NULL, (void*) &PMSelect, NULL, pieceMenuStrings[1], PopUp, "menuB" },
2521 { -1, COMBO_CALLBACK, 0, NULL, (void*) &PMSelect, NULL, dropMenuStrings, PopUp, "menuD" },
2522 { 0, NO_OK, 0, NULL, NULL, "", NULL, EndMark , "" }
2523 };
2524
2525 Option *
LogoW(int n,int x,int y)2526 LogoW (int n, int x, int y)
2527 {
2528 if(n == 10) DisplayLogos(&mainOptions[W_WHITE-1], NULL);
2529 return NULL;
2530 }
2531
2532 Option *
LogoB(int n,int x,int y)2533 LogoB (int n, int x, int y)
2534 {
2535 if(n == 10) DisplayLogos(NULL, &mainOptions[W_BLACK+1]);
2536 return NULL;
2537 }
2538
2539 void
SizeKludge(int n)2540 SizeKludge (int n)
2541 { // callback called by GenericPopUp immediately after sizing the menu bar
2542 int width = BOARD_WIDTH*(squareSize + lineGap) + lineGap;
2543 int w = width - 44 - mainOptions[n].min;
2544 mainOptions[W_TITLE].max = w; // width left behind menu bar
2545 if(w < 0.4*width) // if no reasonable amount of space for title, force small layout
2546 mainOptions[W_SMALL].type = mainOptions[W_TITLE].type, mainOptions[W_TITLE].type = Skip;
2547 }
2548
2549 void
MenuCallback(int n)2550 MenuCallback (int n)
2551 {
2552 MenuProc *proc = (MenuProc *) (((MenuItem*)(mainOptions[n].choice))[values[n]].proc);
2553
2554 if(!proc) RecentEngineEvent(values[n] - firstEngineItem); else (proc)();
2555 }
2556
2557 static Option *
Exp(int n,int x,int y)2558 Exp (int n, int x, int y)
2559 {
2560 static int but1, but3, oldW, oldH;
2561 int menuNr = -3, sizing, f, r;
2562 TimeMark now;
2563 extern Boolean right;
2564
2565 if(right) { // kludgy way to let button 1 double as button 3 when back-end requests this
2566 if(but1 && n == 0) but1 = 0, but3 = 1;
2567 else if(n == -1) n = -3, right = FALSE;
2568 }
2569
2570 if(n == 0) { // motion
2571 if(SeekGraphClick(Press, x, y, 1)) return NULL;
2572 if((but1 || dragging == 2) && !PromoScroll(x, y)) DragPieceMove(x, y);
2573 if(but3) MovePV(x, y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2574 if(appData.highlightDragging) {
2575 f = EventToSquare(x, BOARD_WIDTH); if ( flipView && f >= 0) f = BOARD_WIDTH - 1 - f;
2576 r = EventToSquare(y, BOARD_HEIGHT); if (!flipView && r >= 0) r = BOARD_HEIGHT - 1 - r;
2577 HoverEvent(x, y, f, r);
2578 }
2579 return NULL;
2580 }
2581 if(n != 10 && PopDown(PromoDlg)) fromX = fromY = -1; // user starts fiddling with board when promotion dialog is up
2582 else GetTimeMark(&now);
2583 shiftKey = ShiftKeys();
2584 controlKey = (shiftKey & 0xC) != 0;
2585 shiftKey = (shiftKey & 3) != 0;
2586 switch(n) {
2587 case 1: LeftClick(Press, x, y), but1 = 1; break;
2588 case -1: LeftClick(Release, x, y), but1 = 0; break;
2589 case 2: shiftKey = !shiftKey;
2590 case 3: menuNr = RightClick(Press, x, y, &pmFromX, &pmFromY), but3 = 1; break;
2591 case -2: shiftKey = !shiftKey;
2592 case -3: menuNr = RightClick(Release, x, y, &pmFromX, &pmFromY), but3 = 0; break;
2593 case 4: BackwardEvent(); break;
2594 case 5: ForwardEvent(); break;
2595 case 10:
2596 sizing = (oldW != x || oldH != y);
2597 oldW = x; oldH = y;
2598 InitDrawingHandle(mainOptions + W_BOARD);
2599 if(sizing && SubtractTimeMarks(&now, &programStartTime) > 10000) return NULL; // don't redraw while sizing (except at startup)
2600 DrawPosition(True, NULL);
2601 default:
2602 return NULL;
2603 }
2604
2605 switch(menuNr) {
2606 case 0: return &mainOptions[shiftKey ? W_MENUW: W_MENUB];
2607 case 1: SetupDropMenu(); return &mainOptions[W_DROP];
2608 case 2:
2609 case -1: ErrorPopDown();
2610 case -2:
2611 default: break; // -3, so no clicks caught
2612 }
2613 return NULL;
2614 }
2615
2616 Option *
BoardPopUp(int squareSize,int lineGap,void * clockFontThingy)2617 BoardPopUp (int squareSize, int lineGap, void *clockFontThingy)
2618 {
2619 int i, size = BOARD_WIDTH*(squareSize + lineGap) + lineGap, logo = appData.logoSize;
2620 int f = 2*appData.fixedSize; // width fudge, needed for unknown reasons to not clip board
2621 mainOptions[W_WHITE].choice = (char**) clockFontThingy;
2622 mainOptions[W_BLACK].choice = (char**) clockFontThingy;
2623 mainOptions[W_BOARD].value = BOARD_HEIGHT*(squareSize + lineGap) + lineGap;
2624 mainOptions[W_BOARD].max = mainOptions[W_SMALL].max = size; // board size
2625 mainOptions[W_SMALL].max = size - 2; // board title (subtract border!)
2626 mainOptions[W_BLACK].max = mainOptions[W_WHITE].max = size/2-3; // clock width
2627 mainOptions[W_MESSG].max = appData.showButtonBar ? size-135+f : size-2+f; // message
2628 mainOptions[W_MENU].max = size-40; // menu bar
2629 mainOptions[W_TITLE].type = appData.titleInWindow ? Label : Skip ;
2630 if(logo && logo <= size/4) { // Activate logos
2631 mainOptions[W_WHITE-1].type = mainOptions[W_BLACK+1].type = Graph;
2632 mainOptions[W_WHITE-1].max = mainOptions[W_BLACK+1].max = logo;
2633 mainOptions[W_WHITE-1].value= mainOptions[W_BLACK+1].value= logo/2;
2634 mainOptions[W_WHITE].min |= SAME_ROW;
2635 mainOptions[W_WHITE].max = mainOptions[W_BLACK].max -= logo + 4;
2636 mainOptions[W_WHITE].name = mainOptions[W_BLACK].name = "Double\nHeight";
2637 }
2638 if(!appData.showButtonBar) for(i=W_BUTTON; i<W_BOARD; i++) mainOptions[i].type = Skip;
2639 for(i=0; i<8; i++) mainOptions[i+1].choice = (char**) menuBar[i].mi;
2640 AppendEnginesToMenu(appData.recentEngineList);
2641 GenericPopUp(mainOptions, "XBoard", BoardWindow, BoardWindow, NONMODAL, 1); // allways top-level
2642 return mainOptions;
2643 }
2644
2645 static Option *
SlaveExp(int n,int x,int y)2646 SlaveExp (int n, int x, int y)
2647 {
2648 if(n == 10) { // expose event
2649 flipView = !flipView; partnerUp = !partnerUp;
2650 DrawPosition(True, NULL); // [HGM] dual: draw other board in other orientation
2651 flipView = !flipView; partnerUp = !partnerUp;
2652 }
2653 return NULL;
2654 }
2655
2656 Option dualOptions[] = { // auxiliary board window
2657 { 0, L2L|T2T, 198, NULL, NULL, NULL, NULL, Label, "White" }, // white clock
2658 { 0, R2R|T2T|SAME_ROW, 198, NULL, NULL, NULL, NULL, Label, "Black" }, // black clock
2659 { 0, LR|T2T|BORDER, 401, NULL, NULL, NULL, NULL, Label, "This feature is experimental" }, // message field
2660 { 401, LR|TT, 401, NULL, (char*) &SlaveExp, NULL, NULL, Graph, "shadow board" }, // board
2661 { 0, NO_OK, 0, NULL, NULL, "", NULL, EndMark , "" }
2662 };
2663
2664 void
SlavePopUp()2665 SlavePopUp ()
2666 {
2667 int size = BOARD_WIDTH*(squareSize + lineGap) + lineGap;
2668 // copy params from main board
2669 dualOptions[0].choice = mainOptions[W_WHITE].choice;
2670 dualOptions[1].choice = mainOptions[W_BLACK].choice;
2671 dualOptions[3].value = BOARD_HEIGHT*(squareSize + lineGap) + lineGap;
2672 dualOptions[3].max = dualOptions[2].max = size; // board width
2673 dualOptions[0].max = dualOptions[1].max = size/2 - 3; // clock width
2674 GenericPopUp(dualOptions, "XBoard", DummyDlg, BoardWindow, NONMODAL, appData.topLevel);
2675 SlaveResize(dualOptions+3);
2676 }
2677
2678 void
DisplayWhiteClock(long timeRemaining,int highlight)2679 DisplayWhiteClock (long timeRemaining, int highlight)
2680 {
2681 if(appData.noGUI) return;
2682 if(twoBoards && partnerUp) {
2683 DisplayTimerLabel(&dualOptions[0], _("White"), timeRemaining, highlight);
2684 return;
2685 }
2686 DisplayTimerLabel(&mainOptions[W_WHITE], _("White"), timeRemaining, highlight);
2687 if(highlight) SetClockIcon(0);
2688 }
2689
2690 void
DisplayBlackClock(long timeRemaining,int highlight)2691 DisplayBlackClock (long timeRemaining, int highlight)
2692 {
2693 if(appData.noGUI) return;
2694 if(twoBoards && partnerUp) {
2695 DisplayTimerLabel(&dualOptions[1], _("Black"), timeRemaining, highlight);
2696 return;
2697 }
2698 DisplayTimerLabel(&mainOptions[W_BLACK], _("Black"), timeRemaining, highlight);
2699 if(highlight) SetClockIcon(1);
2700 }
2701
2702
2703 //---------------------------------------------
2704
2705 void
DisplayMessage(char * message,char * extMessage)2706 DisplayMessage (char *message, char *extMessage)
2707 {
2708 /* display a message in the message widget */
2709
2710 char buf[MSG_SIZ];
2711
2712 if (extMessage)
2713 {
2714 if (*message)
2715 {
2716 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
2717 message = buf;
2718 }
2719 else
2720 {
2721 message = extMessage;
2722 };
2723 };
2724
2725 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
2726
2727 /* need to test if messageWidget already exists, since this function
2728 can also be called during the startup, if for example a Xresource
2729 is not set up correctly */
2730 if(mainOptions[W_MESSG].handle)
2731 SetWidgetLabel(&mainOptions[W_MESSG], message);
2732
2733 return;
2734 }
2735
2736 //----------------------------------- File Browser -------------------------------
2737
2738 #ifdef HAVE_DIRENT_H
2739 #include <dirent.h>
2740 #else
2741 #include <sys/dir.h>
2742 #define dirent direct
2743 #endif
2744
2745 #include <sys/stat.h>
2746
2747 #define MAXFILES 1000
2748
2749 static DialogClass savDlg;
2750 static ChessProgramState *savCps;
2751 static FILE **savFP;
2752 static char *fileName, *extFilter, *savMode, **namePtr;
2753 static int folderPtr, filePtr, oldVal, byExtension, extFlag, pageStart, cnt;
2754 static char curDir[MSG_SIZ], title[MSG_SIZ], *folderList[MAXFILES], *fileList[MAXFILES];
2755
2756 static char *FileTypes[] = {
2757 "Chess Games",
2758 "Chess Positions",
2759 "Tournaments",
2760 "Opening Books",
2761 "Sound files",
2762 "Images",
2763 "Settings (*.ini)",
2764 "Log files",
2765 "All files",
2766 NULL,
2767 "PGN",
2768 "Old-Style Games",
2769 "FEN",
2770 "Old-Style Positions",
2771 NULL,
2772 NULL
2773 };
2774
2775 static char *Extensions[] = {
2776 ".pgn .game",
2777 ".fen .epd .pos",
2778 ".trn",
2779 ".bin",
2780 ".wav",
2781 ".ini",
2782 ".log",
2783 "",
2784 "INVALID",
2785 ".pgn",
2786 ".game",
2787 ".fen",
2788 ".pos",
2789 NULL,
2790 ""
2791 };
2792
2793 void DirSelProc P((int n, int sel));
2794 void FileSelProc P((int n, int sel));
2795 void SetTypeFilter P((int n));
2796 int BrowseOK P((int n));
2797 void Switch P((int n));
2798 void CreateDir P((int n));
2799
2800 Option browseOptions[] = {
2801 { 0, LR|T2T, 500, NULL, NULL, NULL, NULL, Label, title },
2802 { 0, L2L|T2T, 250, NULL, NULL, NULL, NULL, Label, N_("Directories:") },
2803 { 0,R2R|T2T|SAME_ROW,100, NULL, NULL, NULL, NULL, Label, N_("Files:") },
2804 { 0, R2R|TT|SAME_ROW, 70, NULL, (void*) &Switch, NULL, NULL, Button, N_("by name") },
2805 { 0, R2R|TT|SAME_ROW, 70, NULL, (void*) &Switch, NULL, NULL, Button, N_("by type") },
2806 { 300, L2L|TB, 250, NULL, (void*) folderList, (char*) &DirSelProc, NULL, ListBox, "" },
2807 { 300, R2R|TB|SAME_ROW,250, NULL, (void*) fileList, (char*) &FileSelProc, NULL, ListBox, "" },
2808 { 0, 0, 300, NULL, (void*) &fileName, NULL, NULL, TextBox, N_("Filename:") },
2809 { 0, SAME_ROW, 120, NULL, (void*) &CreateDir, NULL, NULL, Button, N_("New directory") },
2810 { 0, COMBO_CALLBACK, 150, NULL, (void*) &SetTypeFilter, NULL, FileTypes, ComboBox, N_("File type:") },
2811 { 0, SAME_ROW, 0, NULL, (void*) &BrowseOK, "", NULL, EndMark , "" }
2812 };
2813
2814 int
BrowseOK(int n)2815 BrowseOK (int n)
2816 {
2817 if(!fileName[0]) { // it is enough to have a file selected
2818 if(browseOptions[6].textValue) { // kludge: if callback specified we browse for file
2819 int sel = SelectedListBoxItem(&browseOptions[6]);
2820 if(sel < 0 || sel >= filePtr) return FALSE;
2821 ASSIGN(fileName, fileList[sel]);
2822 } else { // we browse for path
2823 ASSIGN(fileName, curDir); // kludge: without callback we browse for path
2824 }
2825 }
2826 if(!fileName[0]) return FALSE; // refuse OK when no file
2827 if(!savMode[0]) { // browsing for name only (dialog Browse button)
2828 if(fileName[0] == '/') // We already had a path name
2829 snprintf(title, MSG_SIZ, "%s", fileName);
2830 else
2831 snprintf(title, MSG_SIZ, "%s/%s", curDir, fileName);
2832 SetWidgetText((Option*) savFP, title, savDlg);
2833 currentCps = savCps; // could return to Engine Settings dialog!
2834 return TRUE;
2835 }
2836 *savFP = fopen(fileName, savMode);
2837 if(*savFP == NULL) return FALSE; // refuse OK if file not openable
2838 ASSIGN(*namePtr, fileName);
2839 ScheduleDelayedEvent(DelayedLoad, 50);
2840 currentCps = savCps; // not sure this is ever non-null
2841 return TRUE;
2842 }
2843
2844 int
AlphaNumCompare(char * p,char * q)2845 AlphaNumCompare (char *p, char *q)
2846 {
2847 while(*p) {
2848 if(isdigit(*p) && isdigit(*q) && atoi(p) != atoi(q))
2849 return (atoi(p) > atoi(q) ? 1 : -1);
2850 if(*p != *q) break;
2851 p++, q++;
2852 }
2853 if(*p == *q) return 0;
2854 return (*p > *q ? 1 : -1);
2855 }
2856
2857 int
Comp(const void * s,const void * t)2858 Comp (const void *s, const void *t)
2859 {
2860 char *p = *(char**) s, *q = *(char**) t;
2861 if(extFlag) {
2862 char *h; int r;
2863 while(h = strchr(p, '.')) p = h+1;
2864 if(p == *(char**) s) p = "";
2865 while(h = strchr(q, '.')) q = h+1;
2866 if(q == *(char**) t) q = "";
2867 r = AlphaNumCompare(p, q);
2868 if(r) return r;
2869 }
2870 return AlphaNumCompare( *(char**) s, *(char**) t );
2871 }
2872
2873 void
ListDir(int pathFlag)2874 ListDir (int pathFlag)
2875 {
2876 DIR *dir;
2877 struct dirent *dp;
2878 struct stat statBuf;
2879 static int lastFlag;
2880
2881 if(pathFlag < 0) pathFlag = lastFlag;
2882 lastFlag = pathFlag;
2883 dir = opendir(".");
2884 getcwd(curDir, MSG_SIZ);
2885 snprintf(title, MSG_SIZ, "%s %s", _("Contents of"), curDir);
2886 folderPtr = filePtr = cnt = 0; // clear listing
2887
2888 while (dp = readdir(dir)) { // pass 1: list foders
2889 char *s = dp->d_name;
2890 if(!stat(s, &statBuf) && S_ISDIR(statBuf.st_mode)) { // stat succeeds and tells us it is directory
2891 if(s[0] == '.' && strcmp(s, "..")) continue; // suppress hidden, except ".."
2892 ASSIGN(folderList[folderPtr], s); if(folderPtr < MAXFILES-2) folderPtr++;
2893 } else if(!pathFlag) {
2894 char *s = dp->d_name, match=0;
2895 // if(cnt == pageStart) { ASSIGN }
2896 if(s[0] == '.') continue; // suppress hidden files
2897 if(extFilter[0]) { // [HGM] filter on extension
2898 char *p = extFilter, *q;
2899 do {
2900 if(q = strchr(p, ' ')) *q = 0;
2901 if(strstr(s, p)) match++;
2902 if(q) *q = ' ';
2903 } while(q && (p = q+1));
2904 if(!match) continue;
2905 }
2906 if(filePtr == MAXFILES-2) continue;
2907 if(cnt++ < pageStart) continue;
2908 ASSIGN(fileList[filePtr], s); filePtr++;
2909 }
2910 }
2911 if(filePtr == MAXFILES-2) { ASSIGN(fileList[filePtr], _(" next page")); filePtr++; }
2912 FREE(folderList[folderPtr]); folderList[folderPtr] = NULL;
2913 FREE(fileList[filePtr]); fileList[filePtr] = NULL;
2914 closedir(dir);
2915 extFlag = 0; qsort((void*)folderList, folderPtr, sizeof(char*), &Comp);
2916 extFlag = byExtension; qsort((void*)fileList, filePtr < MAXFILES-2 ? filePtr : MAXFILES-2, sizeof(char*), &Comp);
2917 }
2918
2919 void
Refresh(int pathFlag)2920 Refresh (int pathFlag)
2921 {
2922 ListDir(pathFlag); // and make new one
2923 LoadListBox(&browseOptions[5], "", -1, -1);
2924 LoadListBox(&browseOptions[6], "", -1, -1);
2925 SetWidgetLabel(&browseOptions[0], title);
2926 }
2927
2928 static char msg1[] = N_("FIRST TYPE DIRECTORY NAME HERE");
2929 static char msg2[] = N_("TRY ANOTHER NAME");
2930
2931 void
CreateDir(int n)2932 CreateDir (int n)
2933 {
2934 char *name, *errmsg = "";
2935 GetWidgetText(&browseOptions[n-1], &name);
2936 if(!strcmp(name, msg1) || !strcmp(name, msg2)) return;
2937 if(!name[0]) errmsg = _(msg1); else
2938 if(mkdir(name, 0755)) errmsg = _(msg2);
2939 else {
2940 chdir(name);
2941 Refresh(-1);
2942 }
2943 SetWidgetText(&browseOptions[n-1], errmsg, BrowserDlg);
2944 }
2945
2946 void
Switch(int n)2947 Switch (int n)
2948 {
2949 if(byExtension == (n == 4)) return;
2950 extFlag = byExtension = (n == 4);
2951 qsort((void*)fileList, filePtr < MAXFILES-2 ? filePtr : MAXFILES-2, sizeof(char*), &Comp);
2952 LoadListBox(&browseOptions[6], "", -1, -1);
2953 }
2954
2955 void
SetTypeFilter(int n)2956 SetTypeFilter (int n)
2957 {
2958 int j = values[n];
2959 if(j == browseOptions[n].value) return; // no change
2960 browseOptions[n].value = j;
2961 SetWidgetLabel(&browseOptions[n], FileTypes[j]);
2962 ASSIGN(extFilter, Extensions[j]);
2963 pageStart = 0;
2964 Refresh(-1); // uses pathflag remembered by ListDir
2965 values[n] = oldVal; // do not disturb combo settings of underlying dialog
2966 }
2967
2968 void
FileSelProc(int n,int sel)2969 FileSelProc (int n, int sel)
2970 {
2971 if(sel < 0 || fileList[sel] == NULL) return;
2972 if(sel == MAXFILES-2) { pageStart = cnt; Refresh(-1); return; }
2973 ASSIGN(fileName, fileList[sel]);
2974 if(BrowseOK(0)) PopDown(BrowserDlg);
2975 }
2976
2977 void
DirSelProc(int n,int sel)2978 DirSelProc (int n, int sel)
2979 {
2980 if(!chdir(folderList[sel])) { // cd succeeded, so we are in new directory now
2981 Refresh(-1);
2982 }
2983 }
2984
2985 void
StartDir(char * filter,char * newName)2986 StartDir (char *filter, char *newName)
2987 {
2988 static char *gamesDir, *trnDir, *imgDir, *bookDir;
2989 static char curDir[MSG_SIZ];
2990 char **res = NULL;
2991 if(!filter || !*filter) return;
2992 if(strstr(filter, "pgn")) res = &gamesDir; else
2993 if(strstr(filter, "bin")) res = &bookDir; else
2994 if(strstr(filter, "png")) res = &imgDir; else
2995 if(strstr(filter, "trn")) res = &trnDir; else
2996 if(strstr(filter, "fen")) res = &appData.positionDir;
2997 if(res) {
2998 if(newName) {
2999 char *p, *q;
3000 if(*newName) {
3001 ASSIGN(*res, newName);
3002 for(p=*res; q=strchr(p, '/');) p = q + 1; *p = NULLCHAR;
3003 }
3004 if(*curDir) chdir(curDir);
3005 *curDir = NULLCHAR;
3006 } else {
3007 getcwd(curDir, MSG_SIZ);
3008 if(*res && **res) chdir(*res);
3009 }
3010 }
3011 }
3012
3013 void
Browse(DialogClass dlg,char * label,char * proposed,char * ext,Boolean pathFlag,char * mode,char ** name,FILE ** fp)3014 Browse (DialogClass dlg, char *label, char *proposed, char *ext, Boolean pathFlag, char *mode, char **name, FILE **fp)
3015 {
3016 int j=0;
3017 savFP = fp; savMode = mode, namePtr = name, savCps = currentCps, oldVal = values[9], savDlg = dlg; // save params, for use in callback
3018 ASSIGN(extFilter, ext);
3019 ASSIGN(fileName, proposed ? proposed : "");
3020 for(j=0; Extensions[j]; j++) // look up actual value in list of possible values, to get selection nr
3021 if(extFilter && !strcmp(extFilter, Extensions[j])) break;
3022 if(Extensions[j] == NULL) { j++; ASSIGN(FileTypes[j], extFilter); }
3023 browseOptions[9].value = j;
3024 browseOptions[6].textValue = (char*) (pathFlag ? NULL : &FileSelProc); // disable file listbox during path browsing
3025 pageStart = 0; ListDir(pathFlag);
3026 currentCps = NULL;
3027 GenericPopUp(browseOptions, label, BrowserDlg, dlg, MODAL, 0);
3028 SetWidgetLabel(&browseOptions[9], FileTypes[j]);
3029 }
3030
3031 static char *openName;
3032 FileProc fileProc;
3033 char *fileOpenMode;
3034 FILE *openFP;
3035
3036 void
DelayedLoad()3037 DelayedLoad ()
3038 {
3039 (void) (*fileProc)(openFP, 0, openName);
3040 }
3041
3042 void
FileNamePopUp(char * label,char * def,char * filter,FileProc proc,char * openMode)3043 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3044 {
3045 fileProc = proc; /* I can't see a way not */
3046 fileOpenMode = openMode; /* to use globals here */
3047 FileNamePopUpWrapper(label, def, filter, proc, False, openMode, &openName, &openFP);
3048 }
3049
3050 void
ActivateTheme(int col)3051 ActivateTheme (int col)
3052 {
3053 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap; else lineGap = defaultLineGap;
3054 InitDrawingParams(strcmp(oldPieceDir, appData.pieceDirectory));
3055 InitDrawingSizes(-1, 0);
3056 DrawPosition(True, NULL);
3057 }
3058
3059