Implement menu checkmarking and enabling
[xboard.git] / menus.c
1 /*
2  * menus.c -- platform-indendent menu handling code for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #define HIGHDRAG 1
53
54 #include "config.h"
55
56 #include <stdio.h>
57 #include <ctype.h>
58 #include <signal.h>
59 #include <errno.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <pwd.h>
63 #include <math.h>
64
65 #if STDC_HEADERS
66 # include <stdlib.h>
67 # include <string.h>
68 #else /* not STDC_HEADERS */
69 extern char *getenv();
70 # if HAVE_STRING_H
71 #  include <string.h>
72 # else /* not HAVE_STRING_H */
73 #  include <strings.h>
74 # endif /* not HAVE_STRING_H */
75 #endif /* not STDC_HEADERS */
76
77 #if HAVE_UNISTD_H
78 # include <unistd.h>
79 #endif
80
81 #if ENABLE_NLS
82 #include <locale.h>
83 #endif
84
85 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
86 #include "common.h"
87
88 #include "frontend.h"
89 #include "backend.h"
90 #include "xhistory.h"
91 #include "menus.h"
92 #include "gettext.h"
93
94 #ifdef ENABLE_NLS
95 # define  _(s) gettext (s)
96 # define N_(s) gettext_noop (s)
97 #else
98 # define  _(s) (s)
99 # define N_(s)  s
100 #endif
101
102 /*
103  * Button/menu procedures
104  */
105
106 char  *gameCopyFilename, *gamePasteFilename;
107 Boolean saveSettingsOnExit;
108 char *settingsFileName;
109
110 static int
111 LoadGamePopUp (FILE *f, int gameNumber, char *title)
112 {
113     cmailMsgLoaded = FALSE;
114     if (gameNumber == 0) {
115         int error = GameListBuild(f);
116         if (error) {
117             DisplayError(_("Cannot build game list"), error);
118         } else if (!ListEmpty(&gameList) &&
119                    ((ListGame *) gameList.tailPred)->number > 1) {
120             GameListPopUp(f, title);
121             return TRUE;
122         }
123         GameListDestroy();
124         gameNumber = 1;
125     }
126     return LoadGame(f, gameNumber, title, FALSE);
127 }
128
129 void
130 LoadGameProc ()
131 {
132     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
133         Reset(FALSE, TRUE);
134     }
135     FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
136 }
137
138 void
139 LoadNextGameProc ()
140 {
141     ReloadGame(1);
142 }
143
144 void
145 LoadPrevGameProc ()
146 {
147     ReloadGame(-1);
148 }
149
150 void
151 ReloadGameProc ()
152 {
153     ReloadGame(0);
154 }
155
156 void
157 LoadNextPositionProc ()
158 {
159     ReloadPosition(1);
160 }
161
162 void
163 LoadPrevPositionProc ()
164 {
165     ReloadPosition(-1);
166 }
167
168 void
169 ReloadPositionProc ()
170 {
171     ReloadPosition(0);
172 }
173
174 void
175 LoadPositionProc() 
176 {
177     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
178         Reset(FALSE, TRUE);
179     }
180     FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
181 }
182
183 void
184 SaveGameProc ()
185 {
186     FileNamePopUp(_("Save game file name?"),
187                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
188                   appData.oldSaveStyle ? ".game" : ".pgn",
189                   SaveGame, "a");
190 }
191
192 void
193 SavePositionProc ()
194 {
195     FileNamePopUp(_("Save position file name?"),
196                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
197                   appData.oldSaveStyle ? ".pos" : ".fen",
198                   SavePosition, "a");
199 }
200
201 void
202 ReloadCmailMsgProc ()
203 {
204     ReloadCmailMsgEvent(FALSE);
205 }
206
207 void
208 CopyFENToClipboard ()
209 { // wrapper to make call from back-end possible
210   CopyPositionProc();
211 }
212
213 void
214 CopyPositionProc ()
215 {
216     static char *selected_fen_position=NULL;
217     if(gameMode == EditPosition) EditPositionDone(TRUE);
218     if (selected_fen_position) free(selected_fen_position);
219     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
220     if (!selected_fen_position) return;
221     CopySomething(selected_fen_position);
222 }
223
224 void
225 CopyGameProc ()
226 {
227   int ret;
228
229   ret = SaveGameToFile(gameCopyFilename, FALSE);
230   if (!ret) return;
231
232   CopySomething(NULL);
233 }
234
235 void
236 CopyGameListProc ()
237 {
238   if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
239   CopySomething(NULL);
240 }
241
242 void
243 AutoSaveGame ()
244 {
245     SaveGameProc();
246 }
247
248
249 void
250 QuitProc ()
251 {
252     ExitEvent(0);
253 }
254
255 void
256 MatchProc ()
257 {
258     MatchEvent(2);
259 }
260
261 void
262 AdjuWhiteProc ()
263 {
264     UserAdjudicationEvent(+1);
265 }
266
267 void
268 AdjuBlackProc ()
269 {
270     UserAdjudicationEvent(-1);
271 }
272
273 void
274 AdjuDrawProc ()
275 {
276     UserAdjudicationEvent(0);
277 }
278
279 void
280 RevertProc ()
281 {
282     RevertEvent(False);
283 }
284
285 void
286 AnnotateProc ()
287 {
288     RevertEvent(True);
289 }
290
291 void
292 FlipViewProc ()
293 {
294     if(twoBoards) { partnerUp = 1; DrawPosition(True, NULL); partnerUp = 0; }
295     flipView = !flipView;
296     DrawPosition(True, NULL);
297 }
298
299 void
300 SaveOnExitProc ()
301 {
302   saveSettingsOnExit = !saveSettingsOnExit;
303
304   MarkMenuItem("Options.SaveSettingsonExit", saveSettingsOnExit);
305 }
306
307 void
308 SaveSettingsProc ()
309 {
310   SaveSettings(settingsFileName);
311 }
312
313 void
314 InfoProc ()
315 {
316     char buf[MSG_SIZ];
317     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
318             INFODIR, INFOFILE);
319     system(buf);
320 }
321
322 void
323 BugReportProc ()
324 {
325     char buf[MSG_SIZ];
326     snprintf(buf, MSG_SIZ, "%s mailto:bug-xboard@gnu.org", appData.sysOpen);
327     system(buf);
328 }
329
330 void
331 GuideProc ()
332 {
333     char buf[MSG_SIZ];
334     snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/user_guide/UserGuide.html", appData.sysOpen);
335     system(buf);
336 }
337
338 void
339 HomePageProc ()
340 {
341     char buf[MSG_SIZ];
342     snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/", appData.sysOpen);
343     system(buf);
344 }
345
346 void
347 NewsPageProc ()
348 {
349     char buf[MSG_SIZ];
350     snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/whats_new/portal.html", appData.sysOpen);
351     system(buf);
352 }
353
354 void
355 AboutProc ()
356 {
357     char buf[2 * MSG_SIZ];
358 #if ZIPPY
359     char *zippy = _(" (with Zippy code)");
360 #else
361     char *zippy = "";
362 #endif
363     snprintf(buf, sizeof(buf), 
364 _("%s%s\n\n"
365 "Copyright 1991 Digital Equipment Corporation\n"
366 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
367 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
368 "%s is free software and carries NO WARRANTY;"
369 "see the file COPYING for more information.\n\n"
370 "Visit XBoard on the web at: http://www.gnu.org/software/xboard/\n"
371 "Check out the newest features at: http://www.gnu.org/software/xboard/whats_new.html\n\n"
372 "Report bugs via email at: <bug-xboard@gnu.org>\n\n"
373   ),
374             programVersion, zippy, PACKAGE);
375     ErrorPopUp(_("About XBoard"), buf, FALSE);
376 }
377
378 void
379 DebugProc ()
380 {
381     appData.debugMode = !appData.debugMode;
382     if(!strcmp(appData.nameOfDebugFile, "stderr")) return; // stderr is already open, and should never be closed
383     if(!appData.debugMode) fclose(debugFP);
384     else {
385         debugFP = fopen(appData.nameOfDebugFile, "w");
386         if(debugFP == NULL) debugFP = stderr;
387         else setbuf(debugFP, NULL);
388     }
389 }
390
391 void
392 NothingProc ()
393 {
394     return;
395 }
396
397 #ifdef OPTIONSDIALOG
398 #   define MARK_MENU_ITEM(X,Y)
399 #else
400 #   define MARK_MENU_ITEM(X,Y) MarkMenuItem(X, Y)
401 #endif
402
403 void
404 PonderNextMoveProc ()
405 {
406   PonderNextMoveEvent(!appData.ponderNextMove);
407   MARK_MENU_ITEM("Options.PonderNextMove", appData.ponderNextMove);
408 }
409
410 void
411 AlwaysQueenProc ()
412 {
413     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
414     MARK_MENU_ITEM("Options.AlwaysQueen", appData.alwaysPromoteToQueen);
415 }
416
417 void
418 AnimateDraggingProc ()
419 {
420     appData.animateDragging = !appData.animateDragging;
421
422     if (appData.animateDragging) CreateAnimVars();
423     MARK_MENU_ITEM("Options.AnimateDragging", appData.animateDragging);
424 }
425
426 void
427 AnimateMovingProc ()
428 {
429     appData.animate = !appData.animate;
430     if (appData.animate) CreateAnimVars();
431     MARK_MENU_ITEM("Options.AnimateMoving", appData.animate);
432 }
433
434 void
435 AutoflagProc ()
436 {
437     appData.autoCallFlag = !appData.autoCallFlag;
438     MARK_MENU_ITEM("Options.AutoFlag", appData.autoCallFlag);
439 }
440
441 void
442 AutoflipProc ()
443 {
444     appData.autoFlipView = !appData.autoFlipView;
445     MARK_MENU_ITEM("Options.AutoFlipView", appData.autoFlipView);
446 }
447
448 void
449 BlindfoldProc ()
450 {
451     appData.blindfold = !appData.blindfold;
452     MARK_MENU_ITEM("Options.Blindfold", appData.blindfold);
453     DrawPosition(True, NULL);
454 }
455
456 void
457 TestLegalityProc ()
458 {
459     appData.testLegality = !appData.testLegality;
460     MARK_MENU_ITEM("Options.TestLegality", appData.testLegality);
461 }
462
463
464 void
465 FlashMovesProc ()
466 {
467     if (appData.flashCount == 0) {
468         appData.flashCount = 3;
469     } else {
470         appData.flashCount = -appData.flashCount;
471     }
472     MARK_MENU_ITEM("Options.FlashMoves", appData.flashCount > 0);
473 }
474
475 #if HIGHDRAG
476 void
477 HighlightDraggingProc ()
478 {
479     appData.highlightDragging = !appData.highlightDragging;
480     MARK_MENU_ITEM("Options.HighlightDragging", appData.highlightDragging);
481 }
482 #endif
483
484 void
485 HighlightLastMoveProc ()
486 {
487     appData.highlightLastMove = !appData.highlightLastMove;
488     MARK_MENU_ITEM("Options.HighlightLastMove", appData.highlightLastMove);
489 }
490
491 void
492 HighlightArrowProc ()
493 {
494     appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
495     MARK_MENU_ITEM("Options.HighlightWithArrow", appData.highlightMoveWithArrow);
496 }
497
498 void
499 IcsAlarmProc ()
500 {
501     appData.icsAlarm = !appData.icsAlarm;
502 //    MARK_MENU_ITEM("Options.ICSAlarm", appData.icsAlarm);
503 }
504
505 void
506 MoveSoundProc ()
507 {
508     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
509     MARK_MENU_ITEM("Options.MoveSound", appData.ringBellAfterMoves);
510 }
511
512 void
513 OneClickProc ()
514 {
515     appData.oneClick = !appData.oneClick;
516     MARK_MENU_ITEM("Options.OneClickMoving", appData.oneClick);
517 }
518
519 void
520 PeriodicUpdatesProc ()
521 {
522     PeriodicUpdatesEvent(!appData.periodicUpdates);
523     MARK_MENU_ITEM("Options.PeriodicUpdates", appData.periodicUpdates);
524 }
525
526 void
527 PopupExitMessageProc ()
528 {
529     appData.popupExitMessage = !appData.popupExitMessage;
530     MARK_MENU_ITEM("Options.PopupExitMessage", appData.popupExitMessage);
531 }
532
533 void
534 PopupMoveErrorsProc ()
535 {
536     appData.popupMoveErrors = !appData.popupMoveErrors;
537     MARK_MENU_ITEM("Options.PopupMoveErrors", appData.popupMoveErrors);
538 }
539
540 void
541 PremoveProc ()
542 {
543     appData.premove = !appData.premove;
544 //    MARK_MENU_ITEM("Options.Premove", appData.premove);
545 }
546
547 void
548 ShowCoordsProc ()
549 {
550     appData.showCoords = !appData.showCoords;
551     MARK_MENU_ITEM("Options.ShowCoords", appData.showCoords);
552     DrawPosition(True, NULL);
553 }
554
555 void
556 ShowThinkingProc ()
557 {
558     appData.showThinking = !appData.showThinking; // [HGM] thinking: taken out of ShowThinkingEvent
559     ShowThinkingEvent();
560 }
561
562 void
563 HideThinkingProc ()
564 {
565   appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: taken out of ShowThinkingEvent
566   ShowThinkingEvent();
567
568   MARK_MENU_ITEM("Options.HideThinking", appData.hideThinkingFromHuman);
569 }
570
571 /*
572  *  Menu definition tables
573  */
574
575 MenuItem fileMenu[] = {
576     {N_("New Game        Ctrl+N"),        "NewGame", ResetGameEvent},
577     {N_("New Shuffle Game ..."),          "NewShuffleGame", ShuffleMenuProc},
578     {N_("New Variant ...   Alt+Shift+V"), "NewVariant", NewVariantProc},      // [HGM] variant: not functional yet
579     {"----", NULL, NothingProc},
580     {N_("Load Game       Ctrl+O"),        "LoadGame", LoadGameProc, CHECK},
581     {N_("Load Position    Ctrl+Shift+O"), "LoadPosition", LoadPositionProc},
582 //    {N_("Load Next Game"), "LoadNextGame", LoadNextGameProc},
583 //    {N_("Load Previous Game"), "LoadPreviousGame", LoadPrevGameProc},
584 //    {N_("Reload Same Game"), "ReloadSameGame", ReloadGameProc},
585     {N_("Next Position     Shift+PgDn"), "LoadNextPosition", LoadNextPositionProc},
586     {N_("Prev Position     Shift+PgUp"), "LoadPreviousPosition", LoadPrevPositionProc},
587     {"----", NULL, NothingProc},
588 //    {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
589     {N_("Save Game       Ctrl+S"),        "SaveGame", SaveGameProc},
590     {N_("Save Position    Ctrl+Shift+S"), "SavePosition", SavePositionProc},
591     {"----", NULL, NothingProc},
592     {N_("Mail Move"),            "MailMove", MailMoveEvent},
593     {N_("Reload CMail Message"), "ReloadCMailMessage", ReloadCmailMsgProc},
594     {"----", NULL, NothingProc},
595     {N_("Quit                 Ctr+Q"), "Quit", QuitProc},
596     {NULL, NULL, NULL}
597 };
598
599 MenuItem editMenu[] = {
600     {N_("Copy Game    Ctrl+C"),        "CopyGame", CopyGameProc},
601     {N_("Copy Position Ctrl+Shift+C"), "CopyPosition", CopyPositionProc},
602     {N_("Copy Game List"),        "CopyGameList", CopyGameListProc},
603     {"----", NULL, NothingProc},
604     {N_("Paste Game    Ctrl+V"),        "PasteGame", PasteGameProc},
605     {N_("Paste Position Ctrl+Shift+V"), "PastePosition", PastePositionProc},
606     {"----", NULL, NothingProc},
607     {N_("Edit Game      Ctrl+E"),        "EditGame", EditGameEvent},
608     {N_("Edit Position   Ctrl+Shift+E"), "EditPosition", EditPositionEvent},
609     {N_("Edit Tags"),                    "EditTags", EditTagsProc},
610     {N_("Edit Comment"),                 "EditComment", EditCommentProc},
611     {N_("Edit Book"),                    "EditBook", EditBookEvent},
612     {"----", NULL, NothingProc},
613     {N_("Revert              Home"), "Revert", RevertProc},
614     {N_("Annotate"),                 "Annotate", AnnotateProc},
615     {N_("Truncate Game  End"),       "TruncateGame", TruncateGameEvent},
616     {"----", NULL, NothingProc},
617     {N_("Backward         Alt+Left"),   "Backward", BackwardEvent},
618     {N_("Forward           Alt+Right"), "Forward", ForwardEvent},
619     {N_("Back to Start     Alt+Home"),  "BacktoStart", ToStartEvent},
620     {N_("Forward to End Alt+End"),      "ForwardtoEnd", ToEndEvent},
621     {NULL, NULL, NULL}
622 };
623
624 MenuItem viewMenu[] = {
625     {N_("Flip View             F2"),         "FlipView", FlipViewProc, CHECK},
626     {"----", NULL, NothingProc},
627     {N_("Engine Output      Alt+Shift+O"),   "EngineOutput", EngineOutputProc, CHECK},
628     {N_("Move History       Alt+Shift+H"),   "MoveHistory", HistoryShowProc, CHECK}, // [HGM] hist: activate 4.2.7 code
629     {N_("Evaluation Graph  Alt+Shift+E"),    "EvaluationGraph", EvalGraphProc, CHECK},
630     {N_("Game List            Alt+Shift+G"), "GameList", ShowGameListProc, CHECK},
631     {N_("ICS text menu"), "ICStextmenu", IcsTextProc, CHECK},
632     {"----", NULL, NothingProc},
633     {N_("Tags"),             "Tags", EditTagsProc, CHECK},
634     {N_("Comments"),         "Comments", EditCommentProc, CHECK},
635     {N_("ICS Input Box"),    "ICSInputBox", IcsInputBoxProc, CHECK},
636     {N_("Open Chat Window"), "OpenChatWindow", ChatProc, CHECK},
637     {"----", NULL, NothingProc},
638     {N_("Board..."),          "Board", BoardOptionsProc},
639     {N_("Game List Tags..."), "GameListTags", GameListOptionsProc},
640     {NULL, NULL, NULL}
641 };
642
643 MenuItem modeMenu[] = {
644     {N_("Machine White  Ctrl+W"), "MachineWhite", MachineWhiteEvent, RADIO },
645     {N_("Machine Black  Ctrl+B"), "MachineBlack", MachineBlackEvent, RADIO },
646     {N_("Two Machines   Ctrl+T"), "TwoMachines", TwoMachinesEvent, RADIO },
647     {N_("Analysis Mode  Ctrl+A"), "AnalysisMode", (MenuProc*) AnalyzeModeEvent, RADIO},
648     {N_("Analyze Game   Ctrl+G"), "AnalyzeFile", AnalyzeFileEvent, RADIO },
649     {N_("Edit Game         Ctrl+E"), "EditGame", EditGameEvent, RADIO },
650     {N_("Edit Position      Ctrl+Shift+E"), "EditPosition", EditPositionEvent, RADIO },
651     {N_("Training"),      "Training", TrainingEvent, RADIO },
652     {N_("ICS Client"),    "ICSClient", IcsClientEvent, RADIO },
653     {"----", NULL, NothingProc},
654     {N_("Machine Match"),         "MachineMatch", MatchProc, CHECK },
655     {N_("Pause               Pause"),         "Pause", PauseEvent, CHECK },
656     {NULL, NULL, NULL}
657 };
658
659 MenuItem actionMenu[] = {
660     {N_("Accept             F3"), "Accept", AcceptEvent},
661     {N_("Decline            F4"), "Decline", DeclineEvent},
662     {N_("Rematch           F12"), "Rematch", RematchEvent},
663     {"----", NULL, NothingProc},
664     {N_("Call Flag          F5"), "CallFlag", CallFlagEvent},
665     {N_("Draw                F6"), "Draw", DrawEvent},
666     {N_("Adjourn            F7"),  "Adjourn", AdjournEvent},
667     {N_("Abort                F8"),"Abort", AbortEvent},
668     {N_("Resign              F9"), "Resign", ResignEvent},
669     {"----", NULL, NothingProc},
670     {N_("Stop Observing  F10"), "StopObserving", StopObservingEvent},
671     {N_("Stop Examining  F11"), "StopExamining", StopExaminingEvent},
672     {N_("Upload to Examine"),   "UploadtoExamine", UploadGameEvent},
673     {"----", NULL, NothingProc},
674     {N_("Adjudicate to White"), "AdjudicatetoWhite", AdjuWhiteProc},
675     {N_("Adjudicate to Black"), "AdjudicatetoBlack", AdjuBlackProc},
676     {N_("Adjudicate Draw"),     "AdjudicateDraw", AdjuDrawProc},
677     {NULL, NULL, NULL}
678 };
679
680 MenuItem engineMenu[] = {
681     {N_("Load New 1st Engine ..."), "LoadNew1stEngine", LoadEngine1Proc},
682     {N_("Load New 2nd Engine ..."), "LoadNew2ndEngine", LoadEngine2Proc},
683     {"----", NULL, NothingProc},
684     {N_("Engine #1 Settings ..."), "Engine#1Settings", FirstSettingsProc},
685     {N_("Engine #2 Settings ..."), "Engine#2Settings", SecondSettingsProc},
686     {"----", NULL, NothingProc},
687     {N_("Hint"), "Hint", HintEvent},
688     {N_("Book"), "Book", BookEvent},
689     {"----", NULL, NothingProc},
690     {N_("Move Now     Ctrl+M"),     "MoveNow", MoveNowEvent},
691     {N_("Retract Move  Ctrl+X"), "RetractMove", RetractMoveEvent},
692     {NULL, NULL, NULL}
693 };
694
695 MenuItem optionsMenu[] = {
696 #ifdef OPTIONSDIALOG
697     {N_("General ..."), "General", OptionsProc},
698 #endif
699     {N_("Time Control ...       Alt+Shift+T"), "TimeControl", TimeControlProc},
700     {N_("Common Engine ...  Alt+Shift+U"),     "CommonEngine", UciMenuProc},
701     {N_("Adjudications ...      Alt+Shift+J"), "Adjudications", EngineMenuProc},
702     {N_("ICS ..."),    "ICS", IcsOptionsProc},
703     {N_("Match ..."), "Match", MatchOptionsProc},
704     {N_("Load Game ..."),    "LoadGame", LoadOptionsProc},
705     {N_("Save Game ..."),    "SaveGame", SaveOptionsProc},
706 //    {N_(" ..."),    "", OptionsProc},
707     {N_("Game List ..."),    "GameList", GameListOptionsProc},
708     {N_("Sounds ..."),    "Sounds", SoundOptionsProc},
709     {"----", NULL, NothingProc},
710 #ifndef OPTIONSDIALOG
711     {N_("Always Queen        Ctrl+Shift+Q"),   "AlwaysQueen", AlwaysQueenProc},
712     {N_("Animate Dragging"), "AnimateDragging", AnimateDraggingProc},
713     {N_("Animate Moving      Ctrl+Shift+A"),   "AnimateMoving", AnimateMovingProc},
714     {N_("Auto Flag               Ctrl+Shift+F"), "AutoFlag", AutoflagProc},
715     {N_("Auto Flip View"),   "AutoFlipView", AutoflipProc},
716     {N_("Blindfold"),        "Blindfold", BlindfoldProc},
717     {N_("Flash Moves"),      "FlashMoves", FlashMovesProc},
718 #if HIGHDRAG
719     {N_("Highlight Dragging"),    "HighlightDragging", HighlightDraggingProc},
720 #endif
721     {N_("Highlight Last Move"),   "HighlightLastMove", HighlightLastMoveProc},
722     {N_("Highlight With Arrow"),  "HighlightWithArrow", HighlightArrowProc},
723     {N_("Move Sound"),            "MoveSound", MoveSoundProc},
724 //    {N_("ICS Alarm"),             "ICSAlarm", IcsAlarmProc},
725     {N_("One-Click Moving"),      "OneClickMoving", OneClickProc},
726     {N_("Periodic Updates"),      "PeriodicUpdates", PeriodicUpdatesProc},
727     {N_("Ponder Next Move  Ctrl+Shift+P"), "PonderNextMove", PonderNextMoveProc},
728     {N_("Popup Exit Message"),    "PopupExitMessage", PopupExitMessageProc},
729     {N_("Popup Move Errors"),     "PopupMoveErrors", PopupMoveErrorsProc},
730 //    {N_("Premove"),               "Premove", PremoveProc},
731     {N_("Show Coords"),           "ShowCoords", ShowCoordsProc},
732     {N_("Hide Thinking        Ctrl+Shift+H"),   "HideThinking", HideThinkingProc},
733     {N_("Test Legality          Ctrl+Shift+L"), "TestLegality", TestLegalityProc},
734     {"----", NULL, NothingProc},
735 #endif
736     {N_("Save Settings Now"),     "SaveSettingsNow", SaveSettingsProc},
737     {N_("Save Settings on Exit"), "SaveSettingsonExit", SaveOnExitProc, CHECK },
738     {NULL, NULL, NULL}
739 };
740
741 MenuItem helpMenu[] = {
742     {N_("Info XBoard"),     "InfoXBoard", InfoProc},
743     {N_("Man XBoard   F1"), "ManXBoard", ManProc},
744     {"----", NULL, NothingProc},
745     {N_("XBoard Home Page"), "XBoardHomePage", HomePageProc},
746     {N_("On-line User Guide"), "On-lineUserGuide", GuideProc},
747     {N_("Development News"), "DevelopmentNews", NewsPageProc},
748     {N_("e-Mail Bug Report"), "e-MailBugReport", BugReportProc},
749     {"----", NULL, NothingProc},
750     {N_("About XBoard"), "AboutXBoard", AboutProc},
751     {NULL, NULL, NULL}
752 };
753
754 MenuItem noMenu[] = {
755     { "", "LoadNextGame", LoadNextGameProc },
756     { "", "LoadPrevGame", LoadPrevGameProc },
757     { "", "ReloadGame", ReloadGameProc },
758     { "", "ReloadPosition", ReloadPositionProc },
759 #ifndef OPTIONSDIALOG
760     { "", "AlwaysQueen", AlwaysQueenProc },
761     { "", "AnimateDragging", AnimateDraggingProc },
762     { "", "AnimateMoving", AnimateMovingProc },
763     { "", "Autoflag", AutoflagProc },
764     { "", "Autoflip", AutoflipProc },
765     { "", "Blindfold", BlindfoldProc },
766     { "", "FlashMoves", FlashMovesProc },
767 #if HIGHDRAG
768     { "", "HighlightDragging", HighlightDraggingProc },
769 #endif
770     { "", "HighlightLastMove", HighlightLastMoveProc },
771 //    { "", "IcsAlarm", IcsAlarmProc },
772     { "", "MoveSound", MoveSoundProc },
773     { "", "PeriodicUpdates", PeriodicUpdatesProc },
774     { "", "PopupExitMessage", PopupExitMessageProc },
775     { "", "PopupMoveErrors", PopupMoveErrorsProc },
776 //    { "", "Premove", PremoveProc },
777     { "", "ShowCoords", ShowCoordsProc },
778     { "", "ShowThinking", ShowThinkingProc },
779     { "", "HideThinking", HideThinkingProc },
780     { "", "TestLegality", TestLegalityProc },
781 #endif
782     { "", "AboutGame", AboutGameEvent },
783     { "", "DebugProc", DebugProc },
784     { "", "Nothing", NothingProc },
785     {NULL, NULL, NULL}
786 };
787
788 Menu menuBar[] = {
789     {N_("File"),    "File", fileMenu},
790     {N_("Edit"),    "Edit", editMenu},
791     {N_("View"),    "View", viewMenu},
792     {N_("Mode"),    "Mode", modeMenu},
793     {N_("Action"),  "Action", actionMenu},
794     {N_("Engine"),  "Engine", engineMenu},
795     {N_("Options"), "Options", optionsMenu},
796     {N_("Help"),    "Help", helpMenu},
797     {NULL, NULL, NULL}
798 };
799
800 MenuItem *
801 MenuNameToItem (char *menuName)
802 {
803     int i=0;
804     char buf[MSG_SIZ], *p;
805     MenuItem *menuTab;
806     static MenuItem a = { NULL, NULL, NothingProc };
807     extern Option mainOptions[];
808     safeStrCpy(buf, menuName, MSG_SIZ);
809     p = strchr(buf, '.');
810     if(!p) menuTab = noMenu, p = menuName; else {
811         *p++ = NULLCHAR;
812         for(i=0; menuBar[i].name; i++)
813             if(!strcmp(buf, menuBar[i].name)) break;
814         if(!menuBar[i].name) return NULL; // main menu not found
815         menuTab = menuBar[i].mi;
816     }
817     if(*p == NULLCHAR) { a.handle = mainOptions[i+1].handle; return &a; } // main menu bar
818     for(i=0; menuTab[i].string; i++)
819         if(menuTab[i].ref && !strcmp(p, menuTab[i].ref)) return menuTab + i;
820     return NULL; // item not found
821 }
822
823 void
824 AppendEnginesToMenu (char *list)
825 {
826     int i=0;
827     char *p;
828     if(appData.icsActive || appData.recentEngines <= 0) return;
829     recentEngines = strdup(list);
830     while (*list) {
831         p = strchr(list, '\n'); if(p == NULL) break;
832         if(i == 0) AppendMenuItem("----", 0); // at least one valid item to add
833         *p = 0;
834         AppendMenuItem(list, i);
835         i++; *p = '\n'; list = p + 1;
836     }
837 }
838
839 Enables icsEnables[] = {
840     { "File.MailMove", False },
841     { "File.ReloadCMailMessage", False },
842     { "Mode.MachineBlack", False },
843     { "Mode.MachineWhite", False },
844     { "Mode.AnalysisMode", False },
845     { "Mode.AnalyzeFile", False },
846     { "Mode.TwoMachines", False },
847     { "Mode.MachineMatch", False },
848 #ifndef ZIPPY
849     { "Engine.Hint", False },
850     { "Engine.Book", False },
851     { "Engine.MoveNow", False },
852 #ifndef OPTIONSDIALOG
853     { "PeriodicUpdates", False },
854     { "HideThinking", False },
855     { "PonderNextMove", False },
856 #endif
857 #endif
858     { "Engine.Engine#1Settings", False },
859     { "Engine.Engine#2Settings", False },
860     { "Engine.Load1stEngine", False },
861     { "Engine.Load2ndEngine", False },
862     { "Edit.Annotate", False },
863     { "Options.Match", False },
864     { NULL, False }
865 };
866
867 Enables ncpEnables[] = {
868     { "File.MailMove", False },
869     { "File.ReloadCMailMessage", False },
870     { "Mode.MachineWhite", False },
871     { "Mode.MachineBlack", False },
872     { "Mode.AnalysisMode", False },
873     { "Mode.AnalyzeFile", False },
874     { "Mode.TwoMachines", False },
875     { "Mode.MachineMatch", False },
876     { "Mode.ICSClient", False },
877     { "View.ICStextmenu", False },
878     { "View.ICSInputBox", False },
879     { "View.OpenChatWindow", False },
880     { "Action.", False },
881     { "Edit.Revert", False },
882     { "Edit.Annotate", False },
883     { "Engine.Engine#1Settings", False },
884     { "Engine.Engine#2Settings", False },
885     { "Engine.MoveNow", False },
886     { "Engine.RetractMove", False },
887     { "Options.ICS", False },
888 #ifndef OPTIONSDIALOG
889     { "Options.AutoFlag", False },
890     { "Options.AutoFlip View", False },
891 //    { "Options.ICSAlarm", False },
892     { "Options.MoveSound", False },
893     { "Options.HideThinking", False },
894     { "Options.PeriodicUpdates", False },
895     { "Options.PonderNextMove", False },
896 #endif
897     { "Engine.Hint", False },
898     { "Engine.Book", False },
899     { NULL, False }
900 };
901
902 Enables gnuEnables[] = {
903     { "Mode.ICSClient", False },
904     { "View.ICStextmenu", False },
905     { "View.ICSInputBox", False },
906     { "View.OpenChatWindow", False },
907     { "Action.Accept", False },
908     { "Action.Decline", False },
909     { "Action.Rematch", False },
910     { "Action.Adjourn", False },
911     { "Action.StopExamining", False },
912     { "Action.StopObserving", False },
913     { "Action.UploadtoExamine", False },
914     { "Edit.Revert", False },
915     { "Edit.Annotate", False },
916     { "Options.ICS", False },
917
918     /* The next two options rely on SetCmailMode being called *after*    */
919     /* SetGNUMode so that when GNU is being used to give hints these     */
920     /* menu options are still available                                  */
921
922     { "File.MailMove", False },
923     { "File.ReloadCMailMessage", False },
924     // [HGM] The following have been added to make a switch from ncp to GNU mode possible
925     { "Mode.MachineWhite", True },
926     { "Mode.MachineBlack", True },
927     { "Mode.AnalysisMode", True },
928     { "Mode.AnalyzeFile", True },
929     { "Mode.TwoMachines", True },
930     { "Mode.MachineMatch", True },
931     { "Engine.Engine#1Settings", True },
932     { "Engine.Engine#2Settings", True },
933     { "Engine.Hint", True },
934     { "Engine.Book", True },
935     { "Engine.MoveNow", True },
936     { "Engine.RetractMove", True },
937     { "Action.", True },
938     { NULL, False }
939 };
940
941 Enables cmailEnables[] = {
942     { "Action.", True },
943     { "Action.CallFlag", False },
944     { "Action.Draw", True },
945     { "Action.Adjourn", False },
946     { "Action.Abort", False },
947     { "Action.StopObserving", False },
948     { "Action.StopExamining", False },
949     { "File.MailMove", True },
950     { "File.ReloadCMailMessage", True },
951     { NULL, False }
952 };
953
954 Enables trainingOnEnables[] = {
955   { "Edit.EditComment", False },
956   { "Mode.Pause", False },
957   { "Edit.Forward", False },
958   { "Edit.Backward", False },
959   { "Edit.ForwardtoEnd", False },
960   { "Edit.BacktoStart", False },
961   { "Engine.MoveNow", False },
962   { "Edit.TruncateGame", False },
963   { NULL, False }
964 };
965
966 Enables trainingOffEnables[] = {
967   { "Edit.EditComment", True },
968   { "Mode.Pause", True },
969   { "Edit.Forward", True },
970   { "Edit.Backward", True },
971   { "Edit.ForwardtoEnd", True },
972   { "Edit.BacktoStart", True },
973   { "Engine.MoveNow", True },
974   { "Engine.TruncateGame", True },
975   { NULL, False }
976 };
977
978 Enables machineThinkingEnables[] = {
979   { "File.LoadGame", False },
980 //  { "LoadNextGame", False },
981 //  { "LoadPreviousGame", False },
982 //  { "ReloadSameGame", False },
983   { "Edit.PasteGame", False },
984   { "File.LoadPosition", False },
985 //  { "LoadNextPosition", False },
986 //  { "LoadPreviousPosition", False },
987 //  { "ReloadSamePosition", False },
988   { "Edit.PastePosition", False },
989   { "Mode.MachineWhite", False },
990   { "Mode.MachineBlack", False },
991   { "Mode.TwoMachines", False },
992 //  { "MachineMatch", False },
993   { "Engine.RetractMove", False },
994   { NULL, False }
995 };
996
997 Enables userThinkingEnables[] = {
998   { "File.LoadGame", True },
999 //  { "LoadNextGame", True },
1000 //  { "LoadPreviousGame", True },
1001 //  { "ReloadSameGame", True },
1002   { "Edit.PasteGame", True },
1003   { "File.LoadPosition", True },
1004 //  { "LoadNextPosition", True },
1005 //  { "LoadPreviousPosition", True },
1006 //  { "ReloadSamePosition", True },
1007   { "Edit.PastePosition", True },
1008   { "Mode.MachineWhite", True },
1009   { "Mode.MachineBlack", True },
1010   { "Mode.TwoMachines", True },
1011 //  { "MachineMatch", True },
1012   { "Engine.RetractMove", True },
1013   { NULL, False }
1014 };
1015
1016 void
1017 SetICSMode ()
1018 {
1019   SetMenuEnables(icsEnables);
1020
1021 #if ZIPPY
1022   if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
1023      EnableNamedMenuItem("Mode.AnalysisMode", True);
1024      EnableNamedMenuItem("Engine.Engine#1Settings", True);
1025   }
1026 #endif
1027 }
1028
1029 void
1030 SetNCPMode ()
1031 {
1032   SetMenuEnables(ncpEnables);
1033 }
1034
1035 void
1036 SetGNUMode ()
1037 {
1038   SetMenuEnables(gnuEnables);
1039 }
1040
1041 void
1042 SetCmailMode ()
1043 {
1044   SetMenuEnables(cmailEnables);
1045 }
1046
1047 void
1048 SetTrainingModeOn ()
1049 {
1050   SetMenuEnables(trainingOnEnables);
1051   if (appData.showButtonBar) {
1052     EnableButtonBar(False);
1053   }
1054   CommentPopDown();
1055 }
1056
1057 void
1058 SetTrainingModeOff ()
1059 {
1060   SetMenuEnables(trainingOffEnables);
1061   if (appData.showButtonBar) {
1062     EnableButtonBar(True);
1063   }
1064 }
1065
1066 void
1067 SetUserThinkingEnables ()
1068 {
1069   if (appData.noChessProgram) return;
1070   SetMenuEnables(userThinkingEnables);
1071 }
1072
1073 void
1074 SetMachineThinkingEnables ()
1075 {
1076   if (appData.noChessProgram) return;
1077   SetMenuEnables(machineThinkingEnables);
1078   switch (gameMode) {
1079   case MachinePlaysBlack:
1080   case MachinePlaysWhite:
1081   case TwoMachinesPlay:
1082     EnableNamedMenuItem(ModeToWidgetName(gameMode), True);
1083     break;
1084   default:
1085     break;
1086   }
1087 }
1088
1089 void
1090 GreyRevert (Boolean grey)
1091 {
1092     MarkMenuItem("Edit.Revert", !grey);
1093     MarkMenuItem("Edit.Annotate", !grey);
1094 }
1095
1096 char *
1097 ModeToWidgetName (GameMode mode)
1098 {
1099     switch (mode) {
1100       case BeginningOfGame:
1101         if (appData.icsActive)
1102           return "Mode.ICSClient";
1103         else if (appData.noChessProgram ||
1104                  *appData.cmailGameName != NULLCHAR)
1105           return "Mode.EditGame";
1106         else
1107           return "Mode.MachineBlack";
1108       case MachinePlaysBlack:
1109         return "Mode.MachineBlack";
1110       case MachinePlaysWhite:
1111         return "Mode.MachineWhite";
1112       case AnalyzeMode:
1113         return "Mode.AnalysisMode";
1114       case AnalyzeFile:
1115         return "Mode.AnalyzeFile";
1116       case TwoMachinesPlay:
1117         return "Mode.TwoMachines";
1118       case EditGame:
1119         return "Mode.EditGame";
1120       case PlayFromGameFile:
1121         return "File.LoadGame";
1122       case EditPosition:
1123         return "Mode.EditPosition";
1124       case Training:
1125         return "Mode.Training";
1126       case IcsPlayingWhite:
1127       case IcsPlayingBlack:
1128       case IcsObserving:
1129       case IcsIdle:
1130       case IcsExamining:
1131         return "Mode.ICSClient";
1132       default:
1133       case EndOfGame:
1134         return NULL;
1135     }
1136 }
1137
1138 void
1139 InitMenuMarkers()
1140 {
1141 #ifndef OPTIONSDIALOG
1142     if (appData.alwaysPromoteToQueen) {
1143         MarkMenuItem("Options.Always Queen", True);
1144     }
1145     if (appData.animateDragging) {
1146         MarkMenuItem("Options.Animate Dragging", True);
1147     }
1148     if (appData.animate) {
1149         MarkMenuItem("Options.Animate Moving", True);
1150     }
1151     if (appData.autoCallFlag) {
1152         MarkMenuItem("Options.Auto Flag", True);
1153     }
1154     if (appData.autoFlipView) {
1155         XtSetValues(XtNameToWidget(menuBarWidget,"Options.Auto Flip View", True);
1156     }
1157     if (appData.blindfold) {
1158         MarkMenuItem("Options.Blindfold", True);
1159     }
1160     if (appData.flashCount > 0) {
1161         MarkMenuItem("Options.Flash Moves", True);
1162     }
1163 #if HIGHDRAG
1164     if (appData.highlightDragging) {
1165         MarkMenuItem("Options.Highlight Dragging", True);
1166     }
1167 #endif
1168     if (appData.highlightLastMove) {
1169         MarkMenuItem("Options.Highlight Last Move", True);
1170     }
1171     if (appData.highlightMoveWithArrow) {
1172         MarkMenuItem("Options.Arrow", True);
1173     }
1174 //    if (appData.icsAlarm) {
1175 //      MarkMenuItem("Options.ICS Alarm", True);
1176 //    }
1177     if (appData.ringBellAfterMoves) {
1178         MarkMenuItem("Options.Move Sound", True);
1179     }
1180     if (appData.oneClick) {
1181         MarkMenuItem("Options.OneClick", True);
1182     }
1183     if (appData.periodicUpdates) {
1184         MarkMenuItem("Options.Periodic Updates", True);
1185     }
1186     if (appData.ponderNextMove) {
1187         MarkMenuItem("Options.Ponder Next Move", True);
1188     }
1189     if (appData.popupExitMessage) {
1190         MarkMenuItem("Options.Popup Exit Message", True);
1191     }
1192     if (appData.popupMoveErrors) {
1193         MarkMenuItem("Options.Popup Move Errors", True);
1194     }
1195 //    if (appData.premove) {
1196 //      MarkMenuItem("Options.Premove", True);
1197 //    }
1198     if (appData.showCoords) {
1199         MarkMenuItem("Options.Show Coords", True);
1200     }
1201     if (appData.hideThinkingFromHuman) {
1202         MarkMenuItem("Options.Hide Thinking", True);
1203     }
1204     if (appData.testLegality) {
1205         MarkMenuItem("Options.Test Legality", True);
1206     }
1207 #endif
1208     if (saveSettingsOnExit) {
1209         MarkMenuItem("Options.SaveSettingsonExit", True);
1210     }
1211 }
1212
1213