--- /dev/null
+/*
+ * xboard.c -- X front end for XBoard
+ *
+ * Copyright 1991 by Digital Equipment Corporation, Maynard,
+ * Massachusetts.
+ *
+ * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
+ * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * The following terms apply to Digital Equipment Corporation's copyright
+ * interest in XBoard:
+ * ------------------------------------------------------------------------
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * ------------------------------------------------------------------------
+ *
+ * The following terms apply to the enhanced version of XBoard
+ * distributed by the Free Software Foundation:
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/. *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#define HIGHDRAG 1
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <math.h>
+#include <cairo/cairo.h>
+#include <cairo/cairo-xlib.h>
+
+#if !OMIT_SOCKETS
+# if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <netdb.h>
+# else /* not HAVE_SYS_SOCKET_H */
+# if HAVE_LAN_SOCKET_H
+# include <lan/socket.h>
+# include <lan/in.h>
+# include <lan/netdb.h>
+# else /* not HAVE_LAN_SOCKET_H */
+# define OMIT_SOCKETS 1
+# endif /* not HAVE_LAN_SOCKET_H */
+# endif /* not HAVE_SYS_SOCKET_H */
+#endif /* !OMIT_SOCKETS */
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+# include <string.h>
+# else /* not HAVE_STRING_H */
+# include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_SYS_FCNTL_H
+# include <sys/fcntl.h>
+#else /* not HAVE_SYS_FCNTL_H */
+# if HAVE_FCNTL_H
+# include <fcntl.h>
+# endif /* HAVE_FCNTL_H */
+#endif /* not HAVE_SYS_FCNTL_H */
+
+#if HAVE_SYS_SYSTEMINFO_H
+# include <sys/systeminfo.h>
+#endif /* HAVE_SYS_SYSTEMINFO_H */
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+# define HAVE_DIR_STRUCT
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# define HAVE_DIR_STRUCT
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# define HAVE_DIR_STRUCT
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# define HAVE_DIR_STRUCT
+# endif
+#endif
+
+#if ENABLE_NLS
+#include <locale.h>
+#endif
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/cursorfont.h>
+#include <X11/Xatom.h>
+#include <X11/Xmu/Atoms.h>
+#if USE_XAW3D
+#include <X11/Xaw3d/Dialog.h>
+#include <X11/Xaw3d/Form.h>
+#include <X11/Xaw3d/List.h>
+#include <X11/Xaw3d/Label.h>
+#include <X11/Xaw3d/SimpleMenu.h>
+#include <X11/Xaw3d/SmeBSB.h>
+#include <X11/Xaw3d/SmeLine.h>
+#include <X11/Xaw3d/Box.h>
+#include <X11/Xaw3d/MenuButton.h>
+#include <X11/Xaw3d/Text.h>
+#include <X11/Xaw3d/AsciiText.h>
+#else
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#endif
+
+// [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
+#include "common.h"
+
+#if HAVE_LIBXPM
+#include <X11/xpm.h>
+#define IMAGE_EXT "xpm"
+#else
+#define IMAGE_EXT "xim"
+#endif
+
+#include "bitmaps/icon_white.bm"
+#include "bitmaps/icon_black.bm"
+#include "bitmaps/checkmark.bm"
+
+#include "frontend.h"
+#include "backend.h"
+#include "backendz.h"
+#include "moves.h"
+#include "xboard.h"
+#include "childio.h"
+#include "xgamelist.h"
+#include "xhistory.h"
+#include "xevalgraph.h"
+#include "xedittags.h"
+#include "menus.h"
+#include "board.h"
+#include "dialogs.h"
+#include "engineoutput.h"
+#include "usystem.h"
+#include "gettext.h"
+#include "draw.h"
+
+
+#ifdef __EMX__
+#ifndef HAVE_USLEEP
+#define HAVE_USLEEP
+#endif
+#define usleep(t) _sleep2(((t)+500)/1000)
+#endif
+
+#ifdef ENABLE_NLS
+# define _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define _(s) (s)
+# define N_(s) s
+#endif
+
+int main P((int argc, char **argv));
+RETSIGTYPE CmailSigHandler P((int sig));
+RETSIGTYPE IntSigHandler P((int sig));
+RETSIGTYPE TermSizeSigHandler P((int sig));
+Widget CreateMenuBar P((Menu *mb, int boardWidth));
+#if ENABLE_NLS
+char *InsertPxlSize P((char *pattern, int targetPxlSize));
+XFontSet CreateFontSet P((char *base_fnt_lst));
+#else
+char *FindFont P((char *pattern, int targetPxlSize));
+#endif
+void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
+ u_int wreq, u_int hreq));
+void EventProc P((Widget widget, caddr_t unused, XEvent *event));
+void DelayedDrag P((void));
+static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
+void HandlePV P((Widget w, XEvent * event,
+ String * params, Cardinal * nParams));
+void DrawPositionProc P((Widget w, XEvent *event,
+ String *prms, Cardinal *nprms));
+void CommentClick P((Widget w, XEvent * event,
+ String * params, Cardinal * nParams));
+void ICSInputBoxPopUp P((void));
+void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
+void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+Boolean TempBackwardActive = False;
+void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void DisplayMove P((int moveNumber));
+void ICSInitScript P((void));
+void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
+void update_ics_width P(());
+int CopyMemoProc P(());
+
+/*
+* XBoard depends on Xt R4 or higher
+*/
+int xtVersion = XtSpecificationRelease;
+
+int xScreen;
+Display *xDisplay;
+Window xBoardWindow;
+Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
+Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
+Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
+Option *optList; // contains all widgets of main window
+#if ENABLE_NLS
+XFontSet fontSet, clockFontSet;
+#else
+Font clockFontID;
+XFontStruct *clockFontStruct;
+#endif
+Font coordFontID, countFontID;
+XFontStruct *coordFontStruct, *countFontStruct;
+XtAppContext appContext;
+char *layoutName;
+
+char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
+
+Position commentX = -1, commentY = -1;
+Dimension commentW, commentH;
+typedef unsigned int BoardSize;
+BoardSize boardSize;
+Boolean chessProgram;
+
+int minX, minY; // [HGM] placement: volatile limits on upper-left corner
+int smallLayout = 0, tinyLayout = 0,
+ marginW, marginH, // [HGM] for run-time resizing
+ fromX = -1, fromY = -1, toX, toY, commentUp = False,
+ errorExitStatus = -1, defaultLineGap;
+Dimension textHeight;
+Pixel timerForegroundPixel, timerBackgroundPixel;
+Pixel buttonForegroundPixel, buttonBackgroundPixel;
+char *chessDir, *programName, *programVersion;
+Boolean alwaysOnTop = False;
+char *icsTextMenuString;
+char *icsNames;
+char *firstChessProgramNames;
+char *secondChessProgramNames;
+
+WindowPlacement wpMain;
+WindowPlacement wpConsole;
+WindowPlacement wpComment;
+WindowPlacement wpMoveHistory;
+WindowPlacement wpEvalGraph;
+WindowPlacement wpEngineOutput;
+WindowPlacement wpGameList;
+WindowPlacement wpTags;
+
+
+/* This magic number is the number of intermediate frames used
+ in each half of the animation. For short moves it's reduced
+ by 1. The total number of frames will be factor * 2 + 1. */
+#define kFactor 4
+
+SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
+
+typedef struct {
+ char piece;
+ char* widget;
+} DropMenuEnables;
+
+DropMenuEnables dmEnables[] = {
+ { 'P', "Pawn" },
+ { 'N', "Knight" },
+ { 'B', "Bishop" },
+ { 'R', "Rook" },
+ { 'Q', "Queen" }
+};
+
+Arg shellArgs[] = {
+ { XtNwidth, 0 },
+ { XtNheight, 0 },
+ { XtNminWidth, 0 },
+ { XtNminHeight, 0 },
+ { XtNmaxWidth, 0 },
+ { XtNmaxHeight, 0 }
+};
+
+XtResource clientResources[] = {
+ { "flashCount", "flashCount", XtRInt, sizeof(int),
+ XtOffset(AppDataPtr, flashCount), XtRImmediate,
+ (XtPointer) FLASH_COUNT },
+};
+
+XrmOptionDescRec shellOptions[] = {
+ { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
+ { "-flash", "flashCount", XrmoptionNoArg, "3" },
+ { "-xflash", "flashCount", XrmoptionNoArg, "0" },
+};
+
+XtActionsRec boardActions[] = {
+ { "DrawPosition", DrawPositionProc },
+ { "HandlePV", HandlePV },
+ { "SelectPV", SelectPV },
+ { "StopPV", StopPV },
+ { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
+ { "QuitProc", QuitWrapper },
+ { "ManProc", ManInner },
+ { "TempBackwardProc", TempBackwardProc },
+ { "TempForwardProc", TempForwardProc },
+ { "CommentClick", (XtActionProc) CommentClick },
+ { "GenericPopDown", (XtActionProc) GenericPopDown },
+ { "ErrorPopDown", (XtActionProc) ErrorPopDown },
+ { "CopyMemoProc", (XtActionProc) CopyMemoProc },
+ { "SelectMove", (XtActionProc) SelectMove },
+ { "LoadSelectedProc", LoadSelectedProc },
+ { "SetFilterProc", SetFilterProc },
+ { "TypeInProc", TypeInProc },
+ { "EnterKeyProc", EnterKeyProc },
+ { "UpKeyProc", UpKeyProc },
+ { "DownKeyProc", DownKeyProc },
+ { "WheelProc", WheelProc },
+ { "TabProc", TabProc },
+};
+
+char globalTranslations[] =
+ ":<Key>F9: MenuItem(Actions.Resign) \n \
+ :Ctrl<Key>n: MenuItem(File.NewGame) \n \
+ :Meta<Key>V: MenuItem(File.NewVariant) \n \
+ :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
+ :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
+ :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
+ :Ctrl<Key>Down: LoadSelectedProc(3) \n \
+ :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
+ :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
+ :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
+ :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
+ :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
+ :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
+ :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
+ :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
+ :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
+ :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
+ :Ctrl<Key>q: MenuItem(File.Quit) \n \
+ :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
+ :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
+ :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
+ :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
+ :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
+ :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
+ :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
+ :Meta<Key>O: MenuItem(View.EngineOutput) \n \
+ :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
+ :Meta<Key>G: MenuItem(View.GameList) \n \
+ :Meta<Key>H: MenuItem(View.MoveHistory) \n \
+ :<Key>Pause: MenuItem(Mode.Pause) \n \
+ :<Key>F3: MenuItem(Action.Accept) \n \
+ :<Key>F4: MenuItem(Action.Decline) \n \
+ :<Key>F12: MenuItem(Action.Rematch) \n \
+ :<Key>F5: MenuItem(Action.CallFlag) \n \
+ :<Key>F6: MenuItem(Action.Draw) \n \
+ :<Key>F7: MenuItem(Action.Adjourn) \n \
+ :<Key>F8: MenuItem(Action.Abort) \n \
+ :<Key>F10: MenuItem(Action.StopObserving) \n \
+ :<Key>F11: MenuItem(Action.StopExamining) \n \
+ :Ctrl<Key>d: MenuItem(DebugProc) \n \
+ :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
+ :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
+ :Meta<Key>Right: MenuItem(Edit.Forward) \n \
+ :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
+ :Meta<Key>Left: MenuItem(Edit.Backward) \n \
+ :<Key>Left: MenuItem(Edit.Backward) \n \
+ :<Key>Right: MenuItem(Edit.Forward) \n \
+ :<Key>Home: MenuItem(Edit.Revert) \n \
+ :<Key>End: MenuItem(Edit.TruncateGame) \n \
+ :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
+ :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
+ :Meta<Key>J: MenuItem(Options.Adjudications) \n \
+ :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
+ :Meta<Key>T: MenuItem(Options.TimeControl) \n \
+ :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
+#ifndef OPTIONSDIALOG
+ "\
+ :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
+ :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
+ :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
+ :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
+ :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
+#endif
+ "\
+ :<Key>F1: MenuItem(Help.ManXBoard) \n \
+ :<Key>F2: MenuItem(View.FlipView) \n \
+ :<KeyDown>Return: TempBackwardProc() \n \
+ :<KeyUp>Return: TempForwardProc() \n";
+
+char ICSInputTranslations[] =
+ "<Key>Up: UpKeyProc() \n "
+ "<Key>Down: DownKeyProc() \n "
+ "<Key>Return: EnterKeyProc() \n";
+
+// [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
+// as the widget is destroyed before the up-click can call extend-end
+char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
+
+String xboardResources[] = {
+ "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
+ NULL
+ };
+
+
+/* Max possible square size */
+#define MAXSQSIZE 256
+
+static int xpm_avail[MAXSQSIZE];
+
+#ifdef HAVE_DIR_STRUCT
+
+/* Extract piece size from filename */
+static int
+xpm_getsize (char *name, int len, char *ext)
+{
+ char *p, *d;
+ char buf[10];
+
+ if (len < 4)
+ return 0;
+
+ if ((p=strchr(name, '.')) == NULL ||
+ StrCaseCmp(p+1, ext) != 0)
+ return 0;
+
+ p = name + 3;
+ d = buf;
+
+ while (*p && isdigit(*p))
+ *(d++) = *(p++);
+
+ *d = 0;
+ return atoi(buf);
+}
+
+/* Setup xpm_avail */
+static int
+xpm_getavail (char *dirname, char *ext)
+{
+ DIR *dir;
+ struct dirent *ent;
+ int i;
+
+ for (i=0; i<MAXSQSIZE; ++i)
+ xpm_avail[i] = 0;
+
+ if (appData.debugMode)
+ fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
+
+ dir = opendir(dirname);
+ if (!dir)
+ {
+ fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
+ programName, dirname);
+ exit(1);
+ }
+
+ while ((ent=readdir(dir)) != NULL) {
+ i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
+ if (i > 0 && i < MAXSQSIZE)
+ xpm_avail[i] = 1;
+ }
+
+ closedir(dir);
+
+ return 0;
+}
+
+void
+xpm_print_avail (FILE *fp, char *ext)
+{
+ int i;
+
+ fprintf(fp, _("Available `%s' sizes:\n"), ext);
+ for (i=1; i<MAXSQSIZE; ++i) {
+ if (xpm_avail[i])
+ printf("%d\n", i);
+ }
+}
+
+/* Return XPM piecesize closest to size */
+int
+xpm_closest_to (char *dirname, int size, char *ext)
+{
+ int i;
+ int sm_diff = MAXSQSIZE;
+ int sm_index = 0;
+ int diff;
+
+ xpm_getavail(dirname, ext);
+
+ if (appData.debugMode)
+ xpm_print_avail(stderr, ext);
+
+ for (i=1; i<MAXSQSIZE; ++i) {
+ if (xpm_avail[i]) {
+ diff = size - i;
+ diff = (diff<0) ? -diff : diff;
+ if (diff < sm_diff) {
+ sm_diff = diff;
+ sm_index = i;
+ }
+ }
+ }
+
+ if (!sm_index) {
+ fprintf(stderr, _("Error: No `%s' files!\n"), ext);
+ exit(1);
+ }
+
+ return sm_index;
+}
+#else /* !HAVE_DIR_STRUCT */
+/* If we are on a system without a DIR struct, we can't
+ read the directory, so we can't collect a list of
+ filenames, etc., so we can't do any size-fitting. */
+int
+xpm_closest_to (char *dirname, int size, char *ext)
+{
+ fprintf(stderr, _("\
+Warning: No DIR structure found on this system --\n\
+ Unable to autosize for XPM/XIM pieces.\n\
+ Please report this error to %s.\n\
+ Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
+ return size;
+}
+#endif /* HAVE_DIR_STRUCT */
+
+
+/* Arrange to catch delete-window events */
+Atom wm_delete_window;
+void
+CatchDeleteWindow (Widget w, String procname)
+{
+ char buf[MSG_SIZ];
+ XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
+ snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
+ XtAugmentTranslations(w, XtParseTranslationTable(buf));
+}
+
+void
+BoardToTop ()
+{
+ Arg args[16];
+ XtSetArg(args[0], XtNiconic, False);
+ XtSetValues(shellWidget, args, 1);
+
+ XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
+}
+
+//---------------------------------------------------------------------------------------------------------
+// some symbol definitions to provide the proper (= XBoard) context for the code in args.h
+#define XBOARD True
+#define JAWS_ARGS
+#define CW_USEDEFAULT (1<<31)
+#define ICS_TEXT_MENU_SIZE 90
+#define DEBUG_FILE "xboard.debug"
+#define SetCurrentDirectory chdir
+#define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
+#define OPTCHAR "-"
+#define SEPCHAR " "
+
+// The option definition and parsing code common to XBoard and WinBoard is collected in this file
+#include "args.h"
+
+// front-end part of option handling
+
+// [HGM] This platform-dependent table provides the location for storing the color info
+extern char *crWhite, * crBlack;
+
+void *
+colorVariable[] = {
+ &appData.whitePieceColor,
+ &appData.blackPieceColor,
+ &appData.lightSquareColor,
+ &appData.darkSquareColor,
+ &appData.highlightSquareColor,
+ &appData.premoveHighlightColor,
+ &appData.lowTimeWarningColor,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &crWhite,
+ &crBlack,
+ NULL
+};
+
+// [HGM] font: keep a font for each square size, even non-stndard ones
+#define NUM_SIZES 18
+#define MAX_SIZE 130
+Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
+char *fontTable[NUM_FONTS][MAX_SIZE];
+
+void
+ParseFont (char *name, int number)
+{ // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
+ int size;
+ if(sscanf(name, "size%d:", &size)) {
+ // [HGM] font: font is meant for specific boardSize (likely from settings file);
+ // defer processing it until we know if it matches our board size
+ if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
+ fontTable[number][size] = strdup(strchr(name, ':')+1);
+ fontValid[number][size] = True;
+ }
+ return;
+ }
+ switch(number) {
+ case 0: // CLOCK_FONT
+ appData.clockFont = strdup(name);
+ break;
+ case 1: // MESSAGE_FONT
+ appData.font = strdup(name);
+ break;
+ case 2: // COORD_FONT
+ appData.coordFont = strdup(name);
+ break;
+ default:
+ return;
+ }
+ fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
+}
+
+void
+SetFontDefaults ()
+{ // only 2 fonts currently
+ appData.clockFont = CLOCK_FONT_NAME;
+ appData.coordFont = COORD_FONT_NAME;
+ appData.font = DEFAULT_FONT_NAME;
+}
+
+void
+CreateFonts ()
+{ // no-op, until we identify the code for this already in XBoard and move it here
+}
+
+void
+ParseColor (int n, char *name)
+{ // in XBoard, just copy the color-name string
+ if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
+}
+
+void
+ParseTextAttribs (ColorClass cc, char *s)
+{
+ (&appData.colorShout)[cc] = strdup(s);
+}
+
+void
+ParseBoardSize (void *addr, char *name)
+{
+ appData.boardSize = strdup(name);
+}
+
+void
+LoadAllSounds ()
+{ // In XBoard the sound-playing program takes care of obtaining the actual sound
+}
+
+void
+SetCommPortDefaults ()
+{ // for now, this is a no-op, as the corresponding option does not exist in XBoard
+}
+
+// [HGM] args: these three cases taken out to stay in front-end
+void
+SaveFontArg (FILE *f, ArgDescriptor *ad)
+{
+ char *name;
+ int i, n = (int)(intptr_t)ad->argLoc;
+ switch(n) {
+ case 0: // CLOCK_FONT
+ name = appData.clockFont;
+ break;
+ case 1: // MESSAGE_FONT
+ name = appData.font;
+ break;
+ case 2: // COORD_FONT
+ name = appData.coordFont;
+ break;
+ default:
+ return;
+ }
+ for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
+ if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
+ fontTable[n][squareSize] = strdup(name);
+ fontValid[n][squareSize] = True;
+ break;
+ }
+ for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
+ fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
+}
+
+void
+ExportSounds ()
+{ // nothing to do, as the sounds are at all times represented by their text-string names already
+}
+
+void
+SaveAttribsArg (FILE *f, ArgDescriptor *ad)
+{ // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
+ fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
+}
+
+void
+SaveColor (FILE *f, ArgDescriptor *ad)
+{ // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
+ if(colorVariable[(int)(intptr_t)ad->argLoc])
+ fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
+}
+
+void
+SaveBoardSize (FILE *f, char *name, void *addr)
+{ // wrapper to shield back-end from BoardSize & sizeInfo
+ fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
+}
+
+void
+ParseCommPortSettings (char *s)
+{ // no such option in XBoard (yet)
+}
+
+int frameX, frameY;
+
+void
+GetActualPlacement (Widget wg, WindowPlacement *wp)
+{
+ XWindowAttributes winAt;
+ Window win, dummy;
+ int rx, ry;
+
+ if(!wg) return;
+
+ win = XtWindow(wg);
+ XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
+ XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
+ wp->x = rx - winAt.x;
+ wp->y = ry - winAt.y;
+ wp->height = winAt.height;
+ wp->width = winAt.width;
+ frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
+}
+
+void
+GetWindowCoords ()
+{ // wrapper to shield use of window handles from back-end (make addressible by number?)
+ // In XBoard this will have to wait until awareness of window parameters is implemented
+ GetActualPlacement(shellWidget, &wpMain);
+ if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
+ if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
+ if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
+ if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
+ if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
+ if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
+}
+
+void
+PrintCommPortSettings (FILE *f, char *name)
+{ // This option does not exist in XBoard
+}
+
+void
+EnsureOnScreen (int *x, int *y, int minX, int minY)
+{
+ return;
+}
+
+int
+MainWindowUp ()
+{ // [HGM] args: allows testing if main window is realized from back-end
+ return xBoardWindow != 0;
+}
+
+void
+PopUpStartupDialog ()
+{ // start menu not implemented in XBoard
+}
+
+char *
+ConvertToLine (int argc, char **argv)
+{
+ static char line[128*1024], buf[1024];
+ int i;
+
+ line[0] = NULLCHAR;
+ for(i=1; i<argc; i++)
+ {
+ if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
+ && argv[i][0] != '{' )
+ snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
+ else
+ snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
+ strncat(line, buf, 128*1024 - strlen(line) - 1 );
+ }
+
+ line[strlen(line)-1] = NULLCHAR;
+ return line;
+}
+
+//--------------------------------------------------------------------------------------------
+
+void
+ResizeBoardWindow (int w, int h, int inhibit)
+{
+ w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
+ h += marginH;
+ shellArgs[0].value = w;
+ shellArgs[1].value = h;
+ shellArgs[4].value = shellArgs[2].value = w;
+ shellArgs[5].value = shellArgs[3].value = h;
+ XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
+
+ XSync(xDisplay, False);
+}
+
+static int
+MakeOneColor (char *name, Pixel *color)
+{
+ XrmValue vFrom, vTo;
+ if (!appData.monoMode) {
+ vFrom.addr = (caddr_t) name;
+ vFrom.size = strlen(name);
+ XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
+ if (vTo.addr == NULL) {
+ appData.monoMode = True;
+ return True;
+ } else {
+ *color = *(Pixel *) vTo.addr;
+ }
+ }
+ return False;
+}
+
+int
+MakeColors ()
+{ // [HGM] taken out of main(), so it can be called from BoardOptions dialog
+ int forceMono = False;
+
+ if (appData.lowTimeWarning)
+ forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
+ if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
+ if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
+
+ return forceMono;
+}
+
+void
+InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
+{ // detervtomine what fonts to use, and create them
+ XrmValue vTo;
+ XrmDatabase xdb;
+
+ if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
+ appData.clockFont = fontTable[CLOCK_FONT][squareSize];
+ if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
+ appData.font = fontTable[MESSAGE_FONT][squareSize];
+ if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
+ appData.coordFont = fontTable[COORD_FONT][squareSize];
+
+#if ENABLE_NLS
+ appData.font = InsertPxlSize(appData.font, fontPxlSize);
+ appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
+ appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
+ fontSet = CreateFontSet(appData.font);
+ clockFontSet = CreateFontSet(appData.clockFont);
+ {
+ /* For the coordFont, use the 0th font of the fontset. */
+ XFontSet coordFontSet = CreateFontSet(appData.coordFont);
+ XFontStruct **font_struct_list;
+ XFontSetExtents *fontSize;
+ char **font_name_list;
+ XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
+ coordFontID = XLoadFont(xDisplay, font_name_list[0]);
+ coordFontStruct = XQueryFont(xDisplay, coordFontID);
+ fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
+ textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
+ }
+#else
+ appData.font = FindFont(appData.font, fontPxlSize);
+ appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
+ appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
+ clockFontID = XLoadFont(xDisplay, appData.clockFont);
+ clockFontStruct = XQueryFont(xDisplay, clockFontID);
+ coordFontID = XLoadFont(xDisplay, appData.coordFont);
+ coordFontStruct = XQueryFont(xDisplay, coordFontID);
+ // textHeight in !NLS mode!
+#endif
+ countFontID = coordFontID; // [HGM] holdings
+ countFontStruct = coordFontStruct;
+
+ xdb = XtDatabase(xDisplay);
+#if ENABLE_NLS
+ XrmPutLineResource(&xdb, "*international: True");
+ vTo.size = sizeof(XFontSet);
+ vTo.addr = (XtPointer) &fontSet;
+ XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
+#else
+ XrmPutStringResource(&xdb, "*font", appData.font);
+#endif
+}
+
+char *
+PrintArg (ArgType t)
+{
+ char *p="";
+ switch(t) {
+ case ArgZ:
+ case ArgInt: p = " N"; break;
+ case ArgString: p = " STR"; break;
+ case ArgBoolean: p = " TF"; break;
+ case ArgSettingsFilename:
+ case ArgFilename: p = " FILE"; break;
+ case ArgX: p = " Nx"; break;
+ case ArgY: p = " Ny"; break;
+ case ArgAttribs: p = " TEXTCOL"; break;
+ case ArgColor: p = " COL"; break;
+ case ArgFont: p = " FONT"; break;
+ case ArgBoardSize: p = " SIZE"; break;
+ case ArgFloat: p = " FLOAT"; break;
+ case ArgTrue:
+ case ArgFalse:
+ case ArgTwo:
+ case ArgNone:
+ case ArgCommSettings:
+ break;
+ }
+ return p;
+}
+
+void
+PrintOptions ()
+{
+ char buf[MSG_SIZ];
+ int len=0;
+ ArgDescriptor *q, *p = argDescriptors+5;
+ printf("\nXBoard accepts the following options:\n"
+ "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
+ " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
+ " SIZE = board-size spec(s)\n"
+ " Within parentheses are short forms, or options to set to true or false.\n"
+ " Persistent options (saved in the settings file) are marked with *)\n\n");
+ while(p->argName) {
+ if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
+ snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
+ if(p->save) strcat(buf+len, "*");
+ for(q=p+1; q->argLoc == p->argLoc; q++) {
+ if(q->argName[0] == '-') continue;
+ strcat(buf+len, q == p+1 ? " (" : " ");
+ sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
+ }
+ if(q != p+1) strcat(buf+len, ")");
+ len = strlen(buf);
+ if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
+ p = q;
+ }
+ if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
+}
+
+int
+main (int argc, char **argv)
+{
+ int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
+ XSetWindowAttributes window_attributes;
+ Arg args[16];
+ Dimension boardWidth, boardHeight, w, h;
+ char *p;
+ int forceMono = False;
+
+ srandom(time(0)); // [HGM] book: make random truly random
+
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+ debugFP = stderr;
+
+ if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
+ printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+ exit(0);
+ }
+
+ if(argc > 1 && !strcmp(argv[1], "--help" )) {
+ PrintOptions();
+ exit(0);
+ }
+
+ programName = strrchr(argv[0], '/');
+ if (programName == NULL)
+ programName = argv[0];
+ else
+ programName++;
+
+#ifdef ENABLE_NLS
+ XtSetLanguageProc(NULL, NULL, NULL);
+ if (appData.debugMode) {
+ fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
+ }
+
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+
+ appData.boardSize = "";
+ InitAppData(ConvertToLine(argc, argv));
+ p = getenv("HOME");
+ if (p == NULL) p = "/tmp";
+ i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
+ gameCopyFilename = (char*) malloc(i);
+ gamePasteFilename = (char*) malloc(i);
+ snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
+ snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
+
+ { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
+ static char buf[MSG_SIZ];
+ EscapeExpand(buf, appData.firstInitString);
+ appData.firstInitString = strdup(buf);
+ EscapeExpand(buf, appData.secondInitString);
+ appData.secondInitString = strdup(buf);
+ EscapeExpand(buf, appData.firstComputerString);
+ appData.firstComputerString = strdup(buf);
+ EscapeExpand(buf, appData.secondComputerString);
+ appData.secondComputerString = strdup(buf);
+ }
+
+ if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
+ chessDir = ".";
+ } else {
+ if (chdir(chessDir) != 0) {
+ fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
+ perror(chessDir);
+ exit(1);
+ }
+ }
+
+ if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
+ /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
+ if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
+ printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
+ exit(errno);
+ }
+ setbuf(debugFP, NULL);
+ }
+
+ /* [HGM,HR] make sure board size is acceptable */
+ if(appData.NrFiles > BOARD_FILES ||
+ appData.NrRanks > BOARD_RANKS )
+ DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
+
+#if !HIGHDRAG
+ /* This feature does not work; animation needs a rewrite */
+ appData.highlightDragging = FALSE;
+#endif
+ InitBackEnd1();
+
+ gameInfo.variant = StringToVariant(appData.variant);
+ InitPosition(FALSE);
+
+ shellWidget =
+ XtAppInitialize(&appContext, "XBoard", shellOptions,
+ XtNumber(shellOptions),
+ &argc, argv, xboardResources, NULL, 0);
+
+ XtGetApplicationResources(shellWidget, (XtPointer) &appData,
+ clientResources, XtNumber(clientResources),
+ NULL, 0);
+
+ xDisplay = XtDisplay(shellWidget);
+ xScreen = DefaultScreen(xDisplay);
+ wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
+
+ /*
+ * determine size, based on supplied or remembered -size, or screen size
+ */
+ if (isdigit(appData.boardSize[0])) {
+ i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
+ &lineGap, &clockFontPxlSize, &coordFontPxlSize,
+ &fontPxlSize, &smallLayout, &tinyLayout);
+ if (i == 0) {
+ fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
+ programName, appData.boardSize);
+ exit(2);
+ }
+ if (i < 7) {
+ /* Find some defaults; use the nearest known size */
+ SizeDefaults *szd, *nearest;
+ int distance = 99999;
+ nearest = szd = sizeDefaults;
+ while (szd->name != NULL) {
+ if (abs(szd->squareSize - squareSize) < distance) {
+ nearest = szd;
+ distance = abs(szd->squareSize - squareSize);
+ if (distance == 0) break;
+ }
+ szd++;
+ }
+ if (i < 2) lineGap = nearest->lineGap;
+ if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
+ if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
+ if (i < 5) fontPxlSize = nearest->fontPxlSize;
+ if (i < 6) smallLayout = nearest->smallLayout;
+ if (i < 7) tinyLayout = nearest->tinyLayout;
+ }
+ } else {
+ SizeDefaults *szd = sizeDefaults;
+ if (*appData.boardSize == NULLCHAR) {
+ while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
+ DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
+ szd++;
+ }
+ if (szd->name == NULL) szd--;
+ appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
+ } else {
+ while (szd->name != NULL &&
+ StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
+ if (szd->name == NULL) {
+ fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
+ programName, appData.boardSize);
+ exit(2);
+ }
+ }
+ squareSize = szd->squareSize;
+ lineGap = szd->lineGap;
+ clockFontPxlSize = szd->clockFontPxlSize;
+ coordFontPxlSize = szd->coordFontPxlSize;
+ fontPxlSize = szd->fontPxlSize;
+ smallLayout = szd->smallLayout;
+ tinyLayout = szd->tinyLayout;
+ // [HGM] font: use defaults from settings file if available and not overruled
+ }
+
+ defaultLineGap = lineGap;
+ if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
+
+ /* [HR] height treated separately (hacked) */
+ boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
+ boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
+
+ /*
+ * Determine what fonts to use.
+ */
+ InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
+
+ /*
+ * Detect if there are not enough colors available and adapt.
+ */
+ if (DefaultDepth(xDisplay, xScreen) <= 2) {
+ appData.monoMode = True;
+ }
+
+ forceMono = MakeColors();
+
+ if (forceMono) {
+ fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
+ programName);
+ appData.monoMode = True;
+ }
+
+ if (appData.monoMode && appData.debugMode) {
+ fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
+ (unsigned long) XWhitePixel(xDisplay, xScreen),
+ (unsigned long) XBlackPixel(xDisplay, xScreen));
+ }
+
+ ParseIcsTextColors();
+
+ XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
+
+ /*
+ * widget hierarchy
+ */
+ if (tinyLayout) {
+ layoutName = "tinyLayout";
+ } else if (smallLayout) {
+ layoutName = "smallLayout";
+ } else {
+ layoutName = "normalLayout";
+ }
+
+ optList = BoardPopUp(squareSize, lineGap, (void*)
+#if ENABLE_NLS
+ &clockFontSet);
+#else
+ clockFontStruct);
+#endif
+ InitDrawingHandle(optList + W_BOARD);
+ currBoard = &optList[W_BOARD];
+ boardWidget = optList[W_BOARD].handle;
+ menuBarWidget = optList[W_MENU].handle;
+ dropMenu = optList[W_DROP].handle;
+ titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
+ formWidget = XtParent(boardWidget);
+ XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
+ XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
+ XtGetValues(optList[W_WHITE].handle, args, 2);
+ if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
+ XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
+ XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
+ XtGetValues(optList[W_PAUSE].handle, args, 2);
+ }
+ AppendEnginesToMenu(appData.recentEngineList);
+
+ xBoardWindow = XtWindow(boardWidget);
+
+ // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
+ // not need to go into InitDrawingSizes().
+
+ /*
+ * Create X checkmark bitmap and initialize option menu checks.
+ */
+ ReadBitmap(&xMarkPixmap, "checkmark.bm",
+ checkmark_bits, checkmark_width, checkmark_height);
+ InitMenuMarkers();
+
+ /*
+ * Create an icon.
+ */
+ ReadBitmap(&wIconPixmap, "icon_white.bm",
+ icon_white_bits, icon_white_width, icon_white_height);
+ ReadBitmap(&bIconPixmap, "icon_black.bm",
+ icon_black_bits, icon_black_width, icon_black_height);
+ iconPixmap = wIconPixmap;
+ i = 0;
+ XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
+ XtSetValues(shellWidget, args, i);
+
+ /*
+ * Create a cursor for the board widget.
+ */
+ window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
+ XChangeWindowAttributes(xDisplay, xBoardWindow,
+ CWCursor, &window_attributes);
+
+ /*
+ * Inhibit shell resizing.
+ */
+ shellArgs[0].value = (XtArgVal) &w;
+ shellArgs[1].value = (XtArgVal) &h;
+ XtGetValues(shellWidget, shellArgs, 2);
+ shellArgs[4].value = shellArgs[2].value = w;
+ shellArgs[5].value = shellArgs[3].value = h;
+// XtSetValues(shellWidget, &shellArgs[2], 4);
+ marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
+ marginH = h - boardHeight;
+
+ CatchDeleteWindow(shellWidget, "QuitProc");
+
+ CreateAnyPieces();
+ CreateGrid();
+
+ if(appData.logoSize)
+ { // locate and read user logo
+ char buf[MSG_SIZ];
+ snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
+ ASSIGN(userLogo, buf);
+ }
+
+ if (appData.animate || appData.animateDragging)
+ CreateAnimVars();
+
+ XtAugmentTranslations(formWidget,
+ XtParseTranslationTable(globalTranslations));
+
+ XtAddEventHandler(formWidget, KeyPressMask, False,
+ (XtEventHandler) MoveTypeInProc, NULL);
+ XtAddEventHandler(shellWidget, StructureNotifyMask, False,
+ (XtEventHandler) EventProc, NULL);
+
+ /* [AS] Restore layout */
+ if( wpMoveHistory.visible ) {
+ HistoryPopUp();
+ }
+
+ if( wpEvalGraph.visible )
+ {
+ EvalGraphPopUp();
+ };
+
+ if( wpEngineOutput.visible ) {
+ EngineOutputPopUp();
+ }
+
+ InitBackEnd2();
+
+ if (errorExitStatus == -1) {
+ if (appData.icsActive) {
+ /* We now wait until we see "login:" from the ICS before
+ sending the logon script (problems with timestamp otherwise) */
+ /*ICSInitScript();*/
+ if (appData.icsInputBox) ICSInputBoxPopUp();
+ }
+
+ #ifdef SIGWINCH
+ signal(SIGWINCH, TermSizeSigHandler);
+ #endif
+ signal(SIGINT, IntSigHandler);
+ signal(SIGTERM, IntSigHandler);
+ if (*appData.cmailGameName != NULLCHAR) {
+ signal(SIGUSR1, CmailSigHandler);
+ }
+ }
+
+ gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
+ InitPosition(TRUE);
+ UpdateLogos(TRUE);
+// XtSetKeyboardFocus(shellWidget, formWidget);
+ XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
+
+ XtAppMainLoop(appContext);
+ if (appData.debugMode) fclose(debugFP); // [DM] debug
+ return 0;
+}
+
+RETSIGTYPE
+TermSizeSigHandler (int sig)
+{
+ update_ics_width();
+}
+
+RETSIGTYPE
+IntSigHandler (int sig)
+{
+ ExitEvent(sig);
+}
+
+RETSIGTYPE
+CmailSigHandler (int sig)
+{
+ int dummy = 0;
+ int error;
+
+ signal(SIGUSR1, SIG_IGN); /* suspend handler */
+
+ /* Activate call-back function CmailSigHandlerCallBack() */
+ OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
+
+ signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
+}
+
+void
+CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
+{
+ BoardToTop();
+ ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
+}
+/**** end signal code ****/
+
+
+#define Abs(n) ((n)<0 ? -(n) : (n))
+
+#ifdef ENABLE_NLS
+char *
+InsertPxlSize (char *pattern, int targetPxlSize)
+{
+ char *base_fnt_lst, strInt[12], *p, *q;
+ int alternatives, i, len, strIntLen;
+
+ /*
+ * Replace the "*" (if present) in the pixel-size slot of each
+ * alternative with the targetPxlSize.
+ */
+ p = pattern;
+ alternatives = 1;
+ while ((p = strchr(p, ',')) != NULL) {
+ alternatives++;
+ p++;
+ }
+ snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
+ strIntLen = strlen(strInt);
+ base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
+
+ p = pattern;
+ q = base_fnt_lst;
+ while (alternatives--) {
+ char *comma = strchr(p, ',');
+ for (i=0; i<14; i++) {
+ char *hyphen = strchr(p, '-');
+ if (!hyphen) break;
+ if (comma && hyphen > comma) break;
+ len = hyphen + 1 - p;
+ if (i == 7 && *p == '*' && len == 2) {
+ p += len;
+ memcpy(q, strInt, strIntLen);
+ q += strIntLen;
+ *q++ = '-';
+ } else {
+ memcpy(q, p, len);
+ p += len;
+ q += len;
+ }
+ }
+ if (!comma) break;
+ len = comma + 1 - p;
+ memcpy(q, p, len);
+ p += len;
+ q += len;
+ }
+ strcpy(q, p);
+
+ return base_fnt_lst;
+}
+
+XFontSet
+CreateFontSet (char *base_fnt_lst)
+{
+ XFontSet fntSet;
+ char **missing_list;
+ int missing_count;
+ char *def_string;
+
+ fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
+ &missing_list, &missing_count, &def_string);
+ if (appData.debugMode) {
+ int i, count;
+ XFontStruct **font_struct_list;
+ char **font_name_list;
+ fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
+ if (fntSet) {
+ fprintf(debugFP, " got list %s, locale %s\n",
+ XBaseFontNameListOfFontSet(fntSet),
+ XLocaleOfFontSet(fntSet));
+ count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
+ for (i = 0; i < count; i++) {
+ fprintf(debugFP, " got charset %s\n", font_name_list[i]);
+ }
+ }
+ for (i = 0; i < missing_count; i++) {
+ fprintf(debugFP, " missing charset %s\n", missing_list[i]);
+ }
+ }
+ if (fntSet == NULL) {
+ fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
+ exit(2);
+ }
+ return fntSet;
+}
+#else // not ENABLE_NLS
+/*
+ * Find a font that matches "pattern" that is as close as
+ * possible to the targetPxlSize. Prefer fonts that are k
+ * pixels smaller to fonts that are k pixels larger. The
+ * pattern must be in the X Consortium standard format,
+ * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
+ * The return value should be freed with XtFree when no
+ * longer needed.
+ */
+char *
+FindFont (char *pattern, int targetPxlSize)
+{
+ char **fonts, *p, *best, *scalable, *scalableTail;
+ int i, j, nfonts, minerr, err, pxlSize;
+
+ fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
+ if (nfonts < 1) {
+ fprintf(stderr, _("%s: no fonts match pattern %s\n"),
+ programName, pattern);
+ exit(2);
+ }
+
+ best = fonts[0];
+ scalable = NULL;
+ minerr = 999999;
+ for (i=0; i<nfonts; i++) {
+ j = 0;
+ p = fonts[i];
+ if (*p != '-') continue;
+ while (j < 7) {
+ if (*p == NULLCHAR) break;
+ if (*p++ == '-') j++;
+ }
+ if (j < 7) continue;
+ pxlSize = atoi(p);
+ if (pxlSize == 0) {
+ scalable = fonts[i];
+ scalableTail = p;
+ } else {
+ err = pxlSize - targetPxlSize;
+ if (Abs(err) < Abs(minerr) ||
+ (minerr > 0 && err < 0 && -err == minerr)) {
+ best = fonts[i];
+ minerr = err;
+ }
+ }
+ }
+ if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
+ /* If the error is too big and there is a scalable font,
+ use the scalable font. */
+ int headlen = scalableTail - scalable;
+ p = (char *) XtMalloc(strlen(scalable) + 10);
+ while (isdigit(*scalableTail)) scalableTail++;
+ sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
+ } else {
+ p = (char *) XtMalloc(strlen(best) + 2);
+ safeStrCpy(p, best, strlen(best)+1 );
+ }
+ if (appData.debugMode) {
+ fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
+ pattern, targetPxlSize, p);
+ }
+ XFreeFontNames(fonts);
+ return p;
+}
+#endif
+
+void
+ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
+{
+ if (bits != NULL) {
+ *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
+ wreq, hreq);
+ }
+}
+
+void
+MarkMenuItem (char *menuRef, int state)
+{
+ MenuItem *item = MenuNameToItem(menuRef);
+
+ if(item) {
+ Arg args[2];
+ XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
+ XtSetValues(item->handle, args, 1);
+ }
+}
+
+void
+EnableNamedMenuItem (char *menuRef, int state)
+{
+ MenuItem *item = MenuNameToItem(menuRef);
+
+ if(item) XtSetSensitive(item->handle, state);
+}
+
+void
+EnableButtonBar (int state)
+{
+ XtSetSensitive(optList[W_BUTTON].handle, state);
+}
+
+
+void
+SetMenuEnables (Enables *enab)
+{
+ while (enab->name != NULL) {
+ EnableNamedMenuItem(enab->name, enab->value);
+ enab++;
+ }
+}
+
+void
+KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{ // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
+ MenuItem *item;
+ if(*nprms == 0) return;
+ item = MenuNameToItem(prms[0]);
+ if(item) ((MenuProc *) item->proc) ();
+}
+
+static void
+MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
+{
+ RecentEngineEvent((int) (intptr_t) addr);
+}
+
+void
+AppendMenuItem (char *msg, int n)
+{
+ CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
+}
+
+void
+SetupDropMenu ()
+{
+ int i, j, count;
+ char label[32];
+ Arg args[16];
+ Widget entry;
+ char* p;
+
+ for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
+ entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
+ p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
+ dmEnables[i].piece);
+ XtSetSensitive(entry, p != NULL || !appData.testLegality
+ /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
+ && !appData.icsActive));
+ count = 0;
+ while (p && *p++ == dmEnables[i].piece) count++;
+ snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
+ j = 0;
+ XtSetArg(args[j], XtNlabel, label); j++;
+ XtSetValues(entry, args, j);
+ }
+}
+
+static void
+do_flash_delay (unsigned long msec)
+{
+ TimeDelay(msec);
+}
+
+void
+FlashDelay (int flash_delay)
+{
+ XSync(xDisplay, False);
+ if(flash_delay) do_flash_delay(flash_delay);
+}
+
+double
+Fraction (int x, int start, int stop)
+{
+ double f = ((double) x - start)/(stop - start);
+ if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
+ return f;
+}
+
+static WindowPlacement wpNew;
+
+void
+CoDrag (Widget sh, WindowPlacement *wp)
+{
+ Arg args[16];
+ int j=0, touch=0, fudge = 2;
+ GetActualPlacement(sh, wp);
+ if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
+ if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
+ if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
+ if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
+ if(!touch ) return; // only windows that touch co-move
+ if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
+ int heightInc = wpNew.height - wpMain.height;
+ double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
+ double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
+ wp->y += fracTop * heightInc;
+ heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
+ if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
+ } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
+ int widthInc = wpNew.width - wpMain.width;
+ double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
+ double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
+ wp->y += fracLeft * widthInc;
+ widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
+ if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
+ }
+ wp->x += wpNew.x - wpMain.x;
+ wp->y += wpNew.y - wpMain.y;
+ if(touch == 1) wp->x += wpNew.width - wpMain.width; else
+ if(touch == 3) wp->y += wpNew.height - wpMain.height;
+ XtSetArg(args[j], XtNx, wp->x); j++;
+ XtSetArg(args[j], XtNy, wp->y); j++;
+ XtSetValues(sh, args, j);
+}
+
+void
+ReSize (WindowPlacement *wp)
+{
+ int sqx, sqy, w, h;
+ if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
+ sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
+ sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
+ if(sqy < sqx) sqx = sqy;
+ if(sqx != squareSize) {
+ squareSize = sqx; // adopt new square size
+ CreatePNGPieces(); // make newly scaled pieces
+ InitDrawingSizes(0, 0); // creates grid etc.
+ } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
+ w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
+ h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
+ if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
+ if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
+}
+
+static XtIntervalId delayedDragID = 0;
+
+void
+DragProc ()
+{
+ static int busy;
+ if(busy) return;
+
+ busy = 1;
+ GetActualPlacement(shellWidget, &wpNew);
+ if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
+ wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
+ busy = 0; return; // false alarm
+ }
+ ReSize(&wpNew);
+ if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
+ if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
+ if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
+ if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
+ wpMain = wpNew;
+ DrawPosition(True, NULL);
+ delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
+ busy = 0;
+}
+
+
+void
+DelayedDrag ()
+{
+ if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
+ delayedDragID =
+ XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
+}
+
+void
+EventProc (Widget widget, caddr_t unused, XEvent *event)
+{
+ if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
+ DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
+}
+
+/*
+ * event handler for redrawing the board
+ */
+void
+DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+ DrawPosition(True, NULL);
+}
+
+
+void
+HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{ // [HGM] pv: walk PV
+ MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
+}
+
+static int savedIndex; /* gross that this is global */
+
+void
+CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{
+ String val;
+ XawTextPosition index, dummy;
+ Arg arg;
+
+ XawTextGetSelectionPos(w, &index, &dummy);
+ XtSetArg(arg, XtNstring, &val);
+ XtGetValues(w, &arg, 1);
+ ReplaceComment(savedIndex, val);
+ if(savedIndex != currentMove) ToNrEvent(savedIndex);
+ LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
+}
+
+void
+EditCommentPopUp (int index, char *title, char *text)
+{
+ savedIndex = index;
+ if (text == NULL) text = "";
+ NewCommentPopup(title, text, index);
+}
+
+void
+CommentPopUp (char *title, char *text)
+{
+ savedIndex = currentMove; // [HGM] vari
+ NewCommentPopup(title, text, currentMove);
+}
+
+void
+CommentPopDown ()
+{
+ PopDown(CommentDlg);
+}
+
+
+/* Disable all user input other than deleting the window */
+static int frozen = 0;
+
+void
+FreezeUI ()
+{
+ if (frozen) return;
+ /* Grab by a widget that doesn't accept input */
+ XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
+ frozen = 1;
+}
+
+/* Undo a FreezeUI */
+void
+ThawUI ()
+{
+ if (!frozen) return;
+ XtRemoveGrab(optList[W_MESSG].handle);
+ frozen = 0;
+}
+
+void
+ModeHighlight ()
+{
+ Arg args[16];
+ static int oldPausing = FALSE;
+ static GameMode oldmode = (GameMode) -1;
+ char *wname;
+
+ if (!boardWidget || !XtIsRealized(boardWidget)) return;
+
+ if (pausing != oldPausing) {
+ oldPausing = pausing;
+ MarkMenuItem("Mode.Pause", pausing);
+
+ if (appData.showButtonBar) {
+ /* Always toggle, don't set. Previous code messes up when
+ invoked while the button is pressed, as releasing it
+ toggles the state again. */
+ {
+ Pixel oldbg, oldfg;
+ XtSetArg(args[0], XtNbackground, &oldbg);
+ XtSetArg(args[1], XtNforeground, &oldfg);
+ XtGetValues(optList[W_PAUSE].handle,
+ args, 2);
+ XtSetArg(args[0], XtNbackground, oldfg);
+ XtSetArg(args[1], XtNforeground, oldbg);
+ }
+ XtSetValues(optList[W_PAUSE].handle, args, 2);
+ }
+ }
+
+ wname = ModeToWidgetName(oldmode);
+ if (wname != NULL) {
+ MarkMenuItem(wname, False);
+ }
+ wname = ModeToWidgetName(gameMode);
+ if (wname != NULL) {
+ MarkMenuItem(wname, True);
+ }
+ oldmode = gameMode;
+ MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
+
+ /* Maybe all the enables should be handled here, not just this one */
+ EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
+
+ DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
+}
+
+
+/*
+ * Button/menu procedures
+ */
+
+/* this variable is shared between CopyPositionProc and SendPositionSelection */
+char *selected_fen_position=NULL;
+
+Boolean
+SendPositionSelection (Widget w, Atom *selection, Atom *target,
+ Atom *type_return, XtPointer *value_return,
+ unsigned long *length_return, int *format_return)
+{
+ char *selection_tmp;
+
+// if (!selected_fen_position) return False; /* should never happen */
+ if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
+ if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
+ FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
+ long len;
+ size_t count;
+ if (f == NULL) return False;
+ fseek(f, 0, 2);
+ len = ftell(f);
+ rewind(f);
+ selection_tmp = XtMalloc(len + 1);
+ count = fread(selection_tmp, 1, len, f);
+ fclose(f);
+ if (len != count) {
+ XtFree(selection_tmp);
+ return False;
+ }
+ selection_tmp[len] = NULLCHAR;
+ } else {
+ /* note: since no XtSelectionDoneProc was registered, Xt will
+ * automatically call XtFree on the value returned. So have to
+ * make a copy of it allocated with XtMalloc */
+ selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
+ safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
+ }
+
+ *value_return=selection_tmp;
+ *length_return=strlen(selection_tmp);
+ *type_return=*target;
+ *format_return = 8; /* bits per byte */
+ return True;
+ } else if (*target == XA_TARGETS(xDisplay)) {
+ Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
+ targets_tmp[0] = XA_UTF8_STRING(xDisplay);
+ targets_tmp[1] = XA_STRING;
+ *value_return = targets_tmp;
+ *type_return = XA_ATOM;
+ *length_return = 2;
+#if 0
+ // This code leads to a read of value_return out of bounds on 64-bit systems.
+ // Other code which I have seen always sets *format_return to 32 independent of
+ // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
+ // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
+ *format_return = 8 * sizeof(Atom);
+ if (*format_return > 32) {
+ *length_return *= *format_return / 32;
+ *format_return = 32;
+ }
+#else
+ *format_return = 32;
+#endif
+ return True;
+ } else {
+ return False;
+ }
+}
+
+/* note: when called from menu all parameters are NULL, so no clue what the
+ * Widget which was clicked on was, or what the click event was
+ */
+void
+CopySomething (char *src)
+{
+ selected_fen_position = src;
+ /*
+ * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
+ * have a notion of a position that is selected but not copied.
+ * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
+ */
+ XtOwnSelection(menuBarWidget, XA_PRIMARY,
+ CurrentTime,
+ SendPositionSelection,
+ NULL/* lose_ownership_proc */ ,
+ NULL/* transfer_done_proc */);
+ XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
+ CurrentTime,
+ SendPositionSelection,
+ NULL/* lose_ownership_proc */ ,
+ NULL/* transfer_done_proc */);
+}
+
+/* function called when the data to Paste is ready */
+static void
+PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
+ Atom *type, XtPointer value, unsigned long *len, int *format)
+{
+ char *fenstr=value;
+ if (value==NULL || *len==0) return; /* nothing had been selected to copy */
+ fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
+ EditPositionPasteFEN(fenstr);
+ XtFree(value);
+}
+
+/* called when Paste Position button is pressed,
+ * all parameters will be NULL */
+void
+PastePositionProc ()
+{
+ XtGetSelectionValue(menuBarWidget,
+ appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
+ /* (XtSelectionCallbackProc) */ PastePositionCB,
+ NULL, /* client_data passed to PastePositionCB */
+
+ /* better to use the time field from the event that triggered the
+ * call to this function, but that isn't trivial to get
+ */
+ CurrentTime
+ );
+ return;
+}
+
+/* note: when called from menu all parameters are NULL, so no clue what the
+ * Widget which was clicked on was, or what the click event was
+ */
+/* function called when the data to Paste is ready */
+static void
+PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
+ Atom *type, XtPointer value, unsigned long *len, int *format)
+{
+ FILE* f;
+ if (value == NULL || *len == 0) {
+ return; /* nothing had been selected to copy */
+ }
+ f = fopen(gamePasteFilename, "w");
+ if (f == NULL) {
+ DisplayError(_("Can't open temp file"), errno);
+ return;
+ }
+ fwrite(value, 1, *len, f);
+ fclose(f);
+ XtFree(value);
+ LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
+}
+
+/* called when Paste Game button is pressed,
+ * all parameters will be NULL */
+void
+PasteGameProc ()
+{
+ XtGetSelectionValue(menuBarWidget,
+ appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
+ /* (XtSelectionCallbackProc) */ PasteGameCB,
+ NULL, /* client_data passed to PasteGameCB */
+
+ /* better to use the time field from the event that triggered the
+ * call to this function, but that isn't trivial to get
+ */
+ CurrentTime
+ );
+ return;
+}
+
+
+void
+QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+ QuitProc();
+}
+
+int
+ShiftKeys ()
+{ // bassic primitive for determining if modifier keys are pressed
+ long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
+ char keys[32];
+ int i,j, k=0;
+ XQueryKeymap(xDisplay,keys);
+ for(i=0; i<6; i++) {
+ k <<= 1;
+ j = XKeysymToKeycode(xDisplay, codes[i]);
+ k += ( (keys[j>>3]&1<<(j&7)) != 0 );
+ }
+ return k;
+}
+
+static void
+MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
+{
+ char buf[10];
+ KeySym sym;
+ int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
+ if ( n == 1 && *buf >= 32 // printable
+ && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
+ ) BoxAutoPopUp (buf);
+}
+
+static void
+UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{ // [HGM] input: let up-arrow recall previous line from history
+ IcsKey(1);
+}
+
+static void
+DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{ // [HGM] input: let down-arrow recall next line from history
+ IcsKey(-1);
+}
+
+static void
+EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+ IcsKey(0);
+}
+
+void
+TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+ if (!TempBackwardActive) {
+ TempBackwardActive = True;
+ BackwardEvent();
+ }
+}
+
+void
+TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+ /* Check to see if triggered by a key release event for a repeating key.
+ * If so the next queued event will be a key press of the same key at the same time */
+ if (XEventsQueued(xDisplay, QueuedAfterReading)) {
+ XEvent next;
+ XPeekEvent(xDisplay, &next);
+ if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
+ next.xkey.keycode == event->xkey.keycode)
+ return;
+ }
+ ForwardEvent();
+ TempBackwardActive = False;
+}
+
+void
+ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{ // called as key binding
+ char buf[MSG_SIZ];
+ String name;
+ if (nprms && *nprms > 0)
+ name = prms[0];
+ else
+ name = "xboard";
+ snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
+ system(buf);
+}
+
+void
+ManProc ()
+{ // called from menu
+ ManInner(NULL, NULL, NULL, NULL);
+}
+
+void
+SetWindowTitle (char *text, char *title, char *icon)
+{
+ Arg args[16];
+ int i;
+ if (appData.titleInWindow) {
+ i = 0;
+ XtSetArg(args[i], XtNlabel, text); i++;
+ XtSetValues(titleWidget, args, i);
+ }
+ i = 0;
+ XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
+ XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
+ XtSetValues(shellWidget, args, i);
+ XSync(xDisplay, False);
+}
+
+
+static int
+NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
+{
+ return 0;
+}
+
+void
+DisplayIcsInteractionTitle (String message)
+{
+ if (oldICSInteractionTitle == NULL) {
+ /* Magic to find the old window title, adapted from vim */
+ char *wina = getenv("WINDOWID");
+ if (wina != NULL) {
+ Window win = (Window) atoi(wina);
+ Window root, parent, *children;
+ unsigned int nchildren;
+ int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
+ for (;;) {
+ if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
+ if (!XQueryTree(xDisplay, win, &root, &parent,
+ &children, &nchildren)) break;
+ if (children) XFree((void *)children);
+ if (parent == root || parent == 0) break;
+ win = parent;
+ }
+ XSetErrorHandler(oldHandler);
+ }
+ if (oldICSInteractionTitle == NULL) {
+ oldICSInteractionTitle = "xterm";
+ }
+ }
+ printf("\033]0;%s\007", message);
+ fflush(stdout);
+}
+
+
+XtIntervalId delayedEventTimerXID = 0;
+DelayedEventCallback delayedEventCallback = 0;
+
+void
+FireDelayedEvent ()
+{
+ delayedEventTimerXID = 0;
+ delayedEventCallback();
+}
+
+void
+ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
+{
+ if(delayedEventTimerXID && delayedEventCallback == cb)
+ // [HGM] alive: replace, rather than add or flush identical event
+ XtRemoveTimeOut(delayedEventTimerXID);
+ delayedEventCallback = cb;
+ delayedEventTimerXID =
+ XtAppAddTimeOut(appContext, millisec,
+ (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
+}
+
+DelayedEventCallback
+GetDelayedEvent ()
+{
+ if (delayedEventTimerXID) {
+ return delayedEventCallback;
+ } else {
+ return NULL;
+ }
+}
+
+void
+CancelDelayedEvent ()
+{
+ if (delayedEventTimerXID) {
+ XtRemoveTimeOut(delayedEventTimerXID);
+ delayedEventTimerXID = 0;
+ }
+}
+
+XtIntervalId loadGameTimerXID = 0;
+
+int
+LoadGameTimerRunning ()
+{
+ return loadGameTimerXID != 0;
+}
+
+int
+StopLoadGameTimer ()
+{
+ if (loadGameTimerXID != 0) {
+ XtRemoveTimeOut(loadGameTimerXID);
+ loadGameTimerXID = 0;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+void
+LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
+{
+ loadGameTimerXID = 0;
+ AutoPlayGameLoop();
+}
+
+void
+StartLoadGameTimer (long millisec)
+{
+ loadGameTimerXID =
+ XtAppAddTimeOut(appContext, millisec,
+ (XtTimerCallbackProc) LoadGameTimerCallback,
+ (XtPointer) 0);
+}
+
+XtIntervalId analysisClockXID = 0;
+
+void
+AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
+{
+ if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
+ || appData.icsEngineAnalyze) { // [DM]
+ AnalysisPeriodicEvent(0);
+ StartAnalysisClock();
+ }
+}
+
+void
+StartAnalysisClock ()
+{
+ analysisClockXID =
+ XtAppAddTimeOut(appContext, 2000,
+ (XtTimerCallbackProc) AnalysisClockCallback,
+ (XtPointer) 0);
+}
+
+XtIntervalId clockTimerXID = 0;
+
+int
+ClockTimerRunning ()
+{
+ return clockTimerXID != 0;
+}
+
+int
+StopClockTimer ()
+{
+ if (clockTimerXID != 0) {
+ XtRemoveTimeOut(clockTimerXID);
+ clockTimerXID = 0;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+void
+ClockTimerCallback (XtPointer arg, XtIntervalId *id)
+{
+ clockTimerXID = 0;
+ DecrementClocks();
+}
+
+void
+StartClockTimer (long millisec)
+{
+ clockTimerXID =
+ XtAppAddTimeOut(appContext, millisec,
+ (XtTimerCallbackProc) ClockTimerCallback,
+ (XtPointer) 0);
+}
+
+void
+DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
+{
+ char buf[MSG_SIZ];
+ Arg args[16];
+ Widget w = (Widget) opt->handle;
+
+ /* check for low time warning */
+ Pixel foregroundOrWarningColor = timerForegroundPixel;
+
+ if (timer > 0 &&
+ appData.lowTimeWarning &&
+ (timer / 1000) < appData.icsAlarmTime)
+ foregroundOrWarningColor = lowTimeWarningColor;
+
+ if (appData.clockMode) {
+ snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
+ XtSetArg(args[0], XtNlabel, buf);
+ } else {
+ snprintf(buf, MSG_SIZ, "%s ", color);
+ XtSetArg(args[0], XtNlabel, buf);
+ }
+
+ if (highlight) {
+
+ XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
+ XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
+ } else {
+ XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
+ XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
+ }
+
+ XtSetValues(w, args, 3);
+}
+
+static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
+
+void
+SetClockIcon (int color)
+{
+ Arg args[16];
+ Pixmap pm = *clockIcons[color];
+ if (iconPixmap != pm) {
+ iconPixmap = pm;
+ XtSetArg(args[0], XtNiconPixmap, iconPixmap);
+ XtSetValues(shellWidget, args, 1);
+ }
+}
+
+void
+DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
+{
+ InputSource *is = (InputSource *) closure;
+ int count;
+ int error;
+ char *p, *q;
+
+ if (is->lineByLine) {
+ count = read(is->fd, is->unused,
+ INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
+ if (count <= 0) {
+ (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
+ return;
+ }
+ is->unused += count;
+ p = is->buf;
+ while (p < is->unused) {
+ q = memchr(p, '\n', is->unused - p);
+ if (q == NULL) break;
+ q++;
+ (is->func)(is, is->closure, p, q - p, 0);
+ p = q;
+ }
+ q = is->buf;
+ while (p < is->unused) {
+ *q++ = *p++;
+ }
+ is->unused = q;
+ } else {
+ count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
+ if (count == -1)
+ error = errno;
+ else
+ error = 0;
+ (is->func)(is, is->closure, is->buf, count, error);
+ }
+}
+
+InputSourceRef
+AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
+{
+ InputSource *is;
+ ChildProc *cp = (ChildProc *) pr;
+
+ is = (InputSource *) calloc(1, sizeof(InputSource));
+ is->lineByLine = lineByLine;
+ is->func = func;
+ if (pr == NoProc) {
+ is->kind = CPReal;
+ is->fd = fileno(stdin);
+ } else {
+ is->kind = cp->kind;
+ is->fd = cp->fdFrom;
+ }
+ if (lineByLine) {
+ is->unused = is->buf;
+ }
+
+ is->xid = XtAppAddInput(appContext, is->fd,
+ (XtPointer) (XtInputReadMask),
+ (XtInputCallbackProc) DoInputCallback,
+ (XtPointer) is);
+ is->closure = closure;
+ return (InputSourceRef) is;
+}
+
+void
+RemoveInputSource (InputSourceRef isr)
+{
+ InputSource *is = (InputSource *) isr;
+
+ if (is->xid == 0) return;
+ XtRemoveInput(is->xid);
+ is->xid = 0;
+}
+
+#ifndef HAVE_USLEEP
+
+static Boolean frameWaiting;
+
+static RETSIGTYPE
+FrameAlarm (int sig)
+{
+ frameWaiting = False;
+ /* In case System-V style signals. Needed?? */
+ signal(SIGALRM, FrameAlarm);
+}
+
+void
+FrameDelay (int time)
+{
+ struct itimerval delay;
+
+ XSync(xDisplay, False);
+
+ if (time > 0) {
+ frameWaiting = True;
+ signal(SIGALRM, FrameAlarm);
+ delay.it_interval.tv_sec =
+ delay.it_value.tv_sec = time / 1000;
+ delay.it_interval.tv_usec =
+ delay.it_value.tv_usec = (time % 1000) * 1000;
+ setitimer(ITIMER_REAL, &delay, NULL);
+ while (frameWaiting) pause();
+ delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
+ delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &delay, NULL);
+ }
+}
+
+#else
+
+void
+FrameDelay (int time)
+{
+ XSync(xDisplay, False);
+ if (time > 0)
+ usleep(time * 1000);
+}
+
+#endif
+
+static void
+LoadLogo (ChessProgramState *cps, int n, Boolean ics)
+{
+ char buf[MSG_SIZ], *logoName = buf;
+ if(appData.logo[n][0]) {
+ logoName = appData.logo[n];
+ } else if(appData.autoLogo) {
+ if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
+ sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
+ } else if(appData.directory[n] && appData.directory[n][0]) {
+ sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
+ }
+ }
+ if(logoName[0])
+ { ASSIGN(cps->programLogo, logoName); }
+}
+
+void
+UpdateLogos (int displ)
+{
+ if(optList[W_WHITE-1].handle == NULL) return;
+ LoadLogo(&first, 0, 0);
+ LoadLogo(&second, 1, appData.icsActive);
+ if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
+ return;
+}
+
--- /dev/null
+/*
+ * xboard.h -- Parameter definitions for X front end
+ *
+ * Copyright 1991 by Digital Equipment Corporation, Maynard,
+ * Massachusetts.
+ *
+ * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
+ * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * The following terms apply to Digital Equipment Corporation's copyright
+ * interest in XBoard:
+ * ------------------------------------------------------------------------
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * ------------------------------------------------------------------------
+ *
+ * The following terms apply to the enhanced version of XBoard
+ * distributed by the Free Software Foundation:
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/. *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#include <stdio.h>
+
+#define ICS_LOGON ".icsrc"
+#define MANPAGE "xboard.6"
+#if ENABLE_NLS
+#define CLOCK_FONT_NAME \
+ "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*," \
+ "-misc-fixed-bold-r-normal--*-*-*-*-*-*-*-*," \
+ "-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
+#define COORD_FONT_NAME \
+ "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*," \
+ "-misc-fixed-bold-r-normal--*-*-*-*-*-*-*-*," \
+ "-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
+#define DEFAULT_FONT_NAME \
+ "-*-helvetica-medium-r-normal--*-*-*-*-*-*-*-*," \
+ "-misc-fixed-medium-r-normal--*-*-*-*-*-*-*-*," \
+ "-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
+#else
+#define CLOCK_FONT_NAME "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*"
+#define COORD_FONT_NAME "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*"
+#define DEFAULT_FONT_NAME "-*-helvetica-medium-r-normal--*-*-*-*-*-*-*-*"
+#endif
+#define COLOR_SHOUT "green"
+#define COLOR_SSHOUT "green,black,1"
+#define COLOR_CHANNEL1 "cyan"
+#define COLOR_CHANNEL "cyan,black,1"
+#define COLOR_KIBITZ "magenta,black,1"
+#define COLOR_TELL "yellow,black,1"
+#define COLOR_CHALLENGE "red,black,1"
+#define COLOR_REQUEST "red"
+#define COLOR_SEEK "blue"
+#define COLOR_NORMAL "default"
+#define COLOR_LOWTIMEWARNING "red"
+
+typedef struct {
+ char *name;
+ int squareSize;
+ int lineGap;
+ int clockFontPxlSize;
+ int coordFontPxlSize;
+ int fontPxlSize;
+ int smallLayout;
+ int tinyLayout;
+ int minScreenSize;
+} SizeDefaults;
+
+#define SIZE_DEFAULTS \
+{ { "Titanic", 129, 4, 34, 14, 14, 0, 0, 1200 }, \
+ { "Colossal", 116, 4, 34, 14, 14, 0, 0, 1200 }, \
+ { "Giant", 108, 3, 34, 14, 14, 0, 0, 1024 }, \
+ { "Huge", 95, 3, 34, 14, 14, 0, 0, 1024 }, \
+ { "Big", 87, 3, 34, 14, 14, 0, 0, 864 }, \
+ { "Large", 80, 3, 34, 14, 14, 0, 0, 864 }, \
+ { "Bulky", 72, 3, 34, 12, 14, 0, 0, 864 }, \
+ { "Medium", 64, 3, 34, 12, 14, 1, 0, 768 }, \
+ { "Moderate", 58, 3, 34, 12, 14, 1, 0, 768 }, \
+ { "Average", 54, 2, 30, 11, 12, 1, 0, 600 }, \
+ { "Middling", 49, 2, 24, 10, 12, 1, 0, 600 }, \
+ { "Mediocre", 45, 2, 20, 10, 12, 1, 0, 600 }, \
+ { "Small", 40, 2, 20, 10, 12, 1, 0, 480 }, \
+ { "Slim", 37, 2, 20, 10, 12, 1, 0, 480 }, \
+ { "Petite", 33, 1, 15, 9, 11, 1, 0, 480 }, \
+ { "Dinky", 29, 1, 15, 9, 11, 1, 0, 480 }, \
+ { "Teeny", 25, 1, 12, 8, 11, 1, 1, 480 }, \
+ { "Tiny", 21, 1, 12, 8, 11, 1, 1, 0 }, \
+ { NULL, 0, 0, 0, 0, 0, 0, 0, 0 } }
+
+#define BORDER_X_OFFSET 3
+#define BORDER_Y_OFFSET 27
+#define FIRST_CHESS_PROGRAM "fairymax"
+#define SECOND_CHESS_PROGRAM "fairymax"
+#define FIRST_DIRECTORY "."
+#define SECOND_DIRECTORY "."
+#define SOUND_BELL ""
+#define ICS_NAMES ""
+#define FCP_NAMES ""
+#define SCP_NAMES ""
+#define ICS_TEXT_MENU_DEFAULT ""
+#define SETTINGS_FILE SYSCONFDIR"/xboard.conf"
+#define COLOR_BKGD "white"
+
+void NewTagsPopup P((char *text, char *msg));
+int AppendText P((Option *opt, char *s));
+void NewCommentPopup P((char *title, char *text, int index));
+void CatchDeleteWindow(Widget w, String procname);
+void GenericPopDown P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void InitDrawingSizes P((int i, int j));
+void SendToICS P((char *buf));
+void SendToProgram P((char *message, ChessProgramState *cps));
+void SetFocus(Widget w, XtPointer data, XEvent *event, Boolean *b); // from xoptions.c
+void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+Widget CreateMenuItem P((Widget menu, char *msg, XtCallbackProc CB, int n));
+void WheelProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void TabProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void GenericMenu P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+
+// from xengineoutput.c
+void SelectPV P((Widget w, XEvent * event, String * params, Cardinal * nParams));
+void StopPV P((Widget w, XEvent * event, String * params, Cardinal * nParams));
+
+extern char memoTranslations[];
+
+
+extern Widget shells[];
+extern int dialogError;
+extern Widget formWidget, shellWidget, boardWidget, menuBarWidget;
+extern Display *xDisplay;
+extern Window xBoardWindow;
+extern int squareSize;
+extern Pixmap xMarkPixmap, wIconPixmap, bIconPixmap;
+extern char *layoutName;
+extern Pixel timerForegroundPixel, timerBackgroundPixel, dialogColor, buttonColor;
+extern int searchTime;
+extern Atom wm_delete_window;
+extern int squareSize, lineGap, defaultLineGap, useImages, useImageSqs;
+extern int startedFromPositionFile;
+extern char *icsTextMenuString;
+extern char ICSInputTranslations[];
+extern char *selected_fen_position;
+extern GC coordGC;
+extern Dimension textHeight; // of message widget in board window
+
+
+#define TOPLEVEL 1 /* preference item; 1 = make popup windows toplevel */
+
--- /dev/null
+/*
+ * xedittags.c -- Tags edit window, part of X front end for XBoard
+ *
+ * Copyright 1995, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/. *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+# include <string.h>
+# else /* not HAVE_STRING_H */
+# include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/cursorfont.h>
+#if USE_XAW3D
+#include <X11/Xaw3d/Dialog.h>
+#include <X11/Xaw3d/Form.h>
+#include <X11/Xaw3d/List.h>
+#include <X11/Xaw3d/Label.h>
+#include <X11/Xaw3d/SimpleMenu.h>
+#include <X11/Xaw3d/SmeBSB.h>
+#include <X11/Xaw3d/SmeLine.h>
+#include <X11/Xaw3d/Box.h>
+#include <X11/Xaw3d/MenuButton.h>
+#include <X11/Xaw3d/Text.h>
+#include <X11/Xaw3d/AsciiText.h>
+#include <X11/Xaw3d/Viewport.h>
+#else
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+#endif
+
+#include "common.h"
+#include "frontend.h"
+#include "backend.h"
+#include "xboard.h"
+#include "xedittags.h"
+#include "dialogs.h"
+#include "gettext.h"
+
+#ifdef ENABLE_NLS
+# define _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define _(s) (s)
+# define N_(s) s
+#endif
+
+Position tagsX = -1, tagsY = -1;
+
+void
+TagsPopUp (char *tags, char *msg)
+{
+ NewTagsPopup(tags, cmailMsgLoaded ? msg : NULL);
+}
+
+
+void
+EditTagsPopUp (char *tags, char **dest)
+{
+ NewTagsPopup(tags, NULL);
+}
+
+void
+TagsPopDown()
+{
+ PopDown(TagsDlg);
+ bookUp = False;
+}
+
+void
+EditTagsProc ()
+{
+ if (bookUp || !PopDown(TagsDlg)) EditTagsEvent();
+}
--- /dev/null
+/*
+ * xedittags.h
+ *
+ * Copyright 1995, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/. *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#ifndef XB_XEDITTAGS
+#define XB_XEDITTAGS
+
+void EditTagsProc P((void));
+
+extern Widget editTagsShell, tagsShell;
+#endif
--- /dev/null
+/*
+ * Engine output (PV)
+ *
+ * Author: Alessandro Scotti (Dec 2005)
+ *
+ * Copyright 2005 Alessandro Scotti
+ *
+ * Enhancements Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ *
+ * ------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+# include <string.h>
+# else /* not HAVE_STRING_H */
+# include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/cursorfont.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xatom.h>
+#include <X11/Xmu/Atoms.h>
+
+#include "common.h"
+#include "frontend.h"
+#include "backend.h"
+#include "dialogs.h"
+#include "xboard.h"
+#include "engineoutput.h"
+#include "gettext.h"
+
+#ifdef ENABLE_NLS
+# define _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define _(s) (s)
+# define N_(s) s
+#endif
+
+#include <X11/xpm.h>
+
+// [HGM] pixmaps of some ICONS used in the engine-outut window
+#include "pixmaps/WHITE_14.xpm"
+#include "pixmaps/BLACK_14.xpm"
+#include "pixmaps/CLEAR_14.xpm"
+#include "pixmaps/UNKNOWN_14.xpm"
+#include "pixmaps/THINKING_14.xpm"
+#include "pixmaps/PONDER_14.xpm"
+#include "pixmaps/ANALYZING_14.xpm"
+
+
+/* Module variables */
+static int currentPV, highTextStart[2], highTextEnd[2];
+static Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
+static Widget memoWidget;
+
+
+static void
+ReadIcon (char *pixData[], int iconNr, Widget w)
+{
+ int r;
+
+ if ((r=XpmCreatePixmapFromData(xDisplay, XtWindow(w),
+ pixData,
+ &(icons[iconNr]),
+ NULL, NULL /*&attr*/)) != 0) {
+ fprintf(stderr, _("Error %d loading icon image\n"), r);
+ exit(1);
+ }
+}
+
+void
+InitEngineOutput (Option *opt, Option *memo2)
+{ // front-end, because it must have access to the pixmaps
+ Widget w = opt->handle;
+ memoWidget = memo2->handle;
+
+ ReadIcon(WHITE_14, nColorWhite, w);
+ ReadIcon(BLACK_14, nColorBlack, w);
+ ReadIcon(UNKNOWN_14, nColorUnknown, w);
+
+ ReadIcon(CLEAR_14, nClear, w);
+ ReadIcon(PONDER_14, nPondering, w);
+ ReadIcon(THINK_14, nThinking, w);
+ ReadIcon(ANALYZE_14, nAnalyzing, w);
+}
+
+void
+DrawWidgetIcon (Option *opt, int nIcon)
+{ // as we are already in X front-end, so do X-stuff here
+ Arg arg;
+ XtSetArg(arg, XtNleftBitmap, (XtArgVal) icons[nIcon]);
+ XtSetValues(opt->handle, &arg, 1);
+}
+
+void
+InsertIntoMemo (int which, char * text, int where)
+{
+ XawTextBlock t;
+ Widget edit;
+
+ /* the backend adds \r\n, which is needed for winboard,
+ * for xboard we delete them again over here */
+ if(t.ptr = strchr(text, '\r')) *t.ptr = ' ';
+
+ t.ptr = text; t.firstPos = 0; t.length = strlen(text); t.format = XawFmt8Bit;
+ edit = XtNameToWidget(shells[EngOutDlg], which ? "*paneB.text" : "*paneA.text");
+ XawTextReplace(edit, where, where, &t);
+ if(where < highTextStart[which]) { // [HGM] multiPVdisplay: move highlighting
+ int len = strlen(text);
+ highTextStart[which] += len; highTextEnd[which] += len;
+ XawTextSetSelection( edit, highTextStart[which], highTextEnd[which] );
+ }
+}
+
+//--------------------------------- PV walking ---------------------------------------
+
+char memoTranslations[] =
+":Ctrl<Key>c: CopyMemoProc() \n \
+<Btn3Motion>: HandlePV() \n \
+Shift<Btn3Down>: select-start() extend-end() SelectPV(1) \n \
+Any<Btn3Down>: select-start() extend-end() SelectPV(0) \n \
+<Btn3Up>: StopPV() \n";
+
+void
+SelectPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{ // [HGM] pv: translate click to PV line, and load it for display
+ String val;
+ int start, end;
+ XawTextPosition index, dummy;
+ int x, y;
+ Arg arg;
+
+ x = event->xmotion.x; y = event->xmotion.y;
+ currentPV = (w != memoWidget);
+ XawTextGetSelectionPos(w, &index, &dummy);
+ XtSetArg(arg, XtNstring, &val);
+ XtGetValues(w, &arg, 1);
+ shiftKey = strcmp(params[0], "0");
+ if(LoadMultiPV(x, y, val, index, &start, &end, currentPV)) {
+ XawTextSetSelection( w, start, end );
+ highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
+ }
+}
+
+void
+StopPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{ // [HGM] pv: on right-button release, stop displaying PV
+ XawTextUnsetSelection( w );
+ highTextStart[currentPV] = highTextEnd[currentPV] = 0;
+ UnLoadPV();
+ XtCallActionProc(w, "beginning-of-file", event, NULL, 0);
+}
+
+//------------------------- Ctrl-C copying of memo texts ---------------------------
+
+// Awfull code: first read our own primary selection into selected_fen_position,
+// and then transfer ownership of this to the clipboard, so that the
+// copy-position callback can fetch it there when somebody pastes it
+// Worst of all is that I only added it because I did not know how to copy primary:
+// my laptop has no middle button. Ctrl-C might not be needed at all... [HGM]
+
+// cloned from CopyPositionProc. Abuse selected_fen_position to hold selection
+
+Boolean SendPositionSelection(Widget w, Atom *selection, Atom *target,
+ Atom *type_return, XtPointer *value_return,
+ unsigned long *length_return, int *format_return); // from xboard.c
+
+static void
+MemoCB (Widget w, XtPointer client_data, Atom *selection,
+ Atom *type, XtPointer value, unsigned long *len, int *format)
+{
+ if (value==NULL || *len==0) return; /* nothing had been selected to copy */
+ selected_fen_position = value;
+ selected_fen_position[*len]='\0'; /* normally this string is terminated, but be safe */
+ XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
+ CurrentTime,
+ SendPositionSelection,
+ NULL/* lose_ownership_proc */ ,
+ NULL/* transfer_done_proc */);
+}
+
+void
+CopyMemoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+ if(appData.pasteSelection) return;
+ if (selected_fen_position) free(selected_fen_position);
+ XtGetSelectionValue(menuBarWidget,
+ XA_PRIMARY, XA_STRING,
+ /* (XtSelectionCallbackProc) */ MemoCB,
+ NULL, /* client_data passed to PastePositionCB */
+
+ /* better to use the time field from the event that triggered the
+ * call to this function, but that isn't trivial to get
+ */
+ CurrentTime
+ );
+}
+
+//------------------------------- pane switching -----------------------------------
+
+void
+ResizeWindowControls (int mode)
+{ // another hideous kludge: to have only a single pane, we resize the
+ // second to 5 pixels (which makes it too small to display anything)
+ Widget form1, form2;
+ Arg args[16];
+ int j;
+ Dimension ew_height, tmp;
+ Widget shell = shells[EngOutDlg];
+
+ form1 = XtNameToWidget(shell, "*paneA");
+ form2 = XtNameToWidget(shell, "*paneB");
+
+ j = 0;
+ XtSetArg(args[j], XtNheight, (XtArgVal) &ew_height); j++;
+ XtGetValues(form1, args, j);
+ j = 0;
+ XtSetArg(args[j], XtNheight, (XtArgVal) &tmp); j++;
+ XtGetValues(form2, args, j);
+ ew_height += tmp; // total height
+
+ if(mode==0) {
+ j = 0;
+ XtSetArg(args[j], XtNheight, (XtArgVal) 5); j++;
+ XtSetValues(form2, args, j);
+ j = 0;
+ XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height-5)); j++;
+ XtSetValues(form1, args, j);
+ } else {
+ j = 0;
+ XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
+ XtSetValues(form1, args, j);
+ j = 0;
+ XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
+ XtSetValues(form2, args, j);
+ }
+}
+
--- /dev/null
+/*
+ * Evaluation graph
+ *
+ * Author: Alessandro Scotti (Dec 2005)
+ * Translated to X by H.G.Muller (Nov 2009)
+ *
+ * Copyright 2005 Alessandro Scotti
+ *
+ * Enhancements Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ *
+ * ------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+# include <string.h>
+# else /* not HAVE_STRING_H */
+# include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/cursorfont.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+
+#include <cairo/cairo.h>
+#include <cairo/cairo-xlib.h>
+
+#include "common.h"
+#include "frontend.h"
+#include "backend.h"
+#include "dialogs.h"
+#include "menus.h"
+#include "xboard.h"
+#include "evalgraph.h"
+#include "xevalgraph.h"
+#include "draw.h"
+#include "gettext.h"
+
+#ifdef ENABLE_NLS
+# define _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define _(s) (s)
+# define N_(s) s
+#endif
+
+#include <X11/xpm.h>
+
+#ifdef SNAP
+#include "wsnap.h"
+#endif
+
+#define _LL_ 100
+
+Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
+Widget outputField[2][7]; // [HGM] front-end array to translate output field to window handle
+static char *title = N_("Evaluation graph");
+
+//extern WindowPlacement wpEvalGraph;
+
+Position evalGraphX = -1, evalGraphY = -1;
+Dimension evalGraphW, evalGraphH;
+
+/* Module variables */
+
+char *crWhite = "#FFFFB0";
+char *crBlack = "#AD5D3D";
+Option *disp;
+
+static Option *EvalCallback P((int button, int x, int y));
+
+static void
+ChoosePen(cairo_t *cr, int i)
+{
+ switch(i) {
+ case PEN_BLACK:
+ SetPen(cr, 1.0, "#000000", 0);
+ break;
+ case PEN_DOTTED:
+ SetPen(cr, 1.0, "#A0A0A0", 1);
+ break;
+ case PEN_BLUEDOTTED:
+ SetPen(cr, 1.0, "#0000FF", 1);
+ break;
+ case PEN_BOLDWHITE:
+ SetPen(cr, 3.0, crWhite, 0);
+ break;
+ case PEN_BOLDBLACK:
+ SetPen(cr, 3.0, crBlack, 0);
+ break;
+ case PEN_BACKGD:
+ SetPen(cr, 3.0, "#E0E0F0", 0);
+ break;
+ }
+}
+
+// [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end)
+void
+DrawSegment (int x, int y, int *lastX, int *lastY, enum PEN penType)
+{
+ static int curX, curY;
+
+ if(penType != PEN_NONE) {
+ cairo_t *cr = cairo_create(DRAWABLE(disp));
+ cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+ cairo_move_to (cr, curX, curY);
+ cairo_line_to (cr, x,y);
+ ChoosePen(cr, penType);
+ cairo_stroke (cr);
+ cairo_destroy (cr);
+ }
+
+ if(lastX != NULL) { *lastX = curX; *lastY = curY; }
+ curX = x; curY = y;
+}
+
+// front-end wrapper for drawing functions to do rectangles
+void
+DrawRectangle (int left, int top, int right, int bottom, int side, int style)
+{
+ cairo_t *cr;
+
+ cr = cairo_create (DRAWABLE(disp));
+ cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+ cairo_rectangle (cr, left, top, right-left, bottom-top);
+ switch(side)
+ {
+ case 0: ChoosePen(cr, PEN_BOLDWHITE); break;
+ case 1: ChoosePen(cr, PEN_BOLDBLACK); break;
+ case 2: ChoosePen(cr, PEN_BACKGD); break;
+ }
+ cairo_fill (cr);
+
+ if(style != FILLED)
+ {
+ cairo_rectangle (cr, left, top, right-left-1, bottom-top-1);
+ ChoosePen(cr, PEN_BLACK);
+ cairo_stroke (cr);
+ }
+
+ cairo_destroy(cr);
+}
+
+// front-end wrapper for putting text in graph
+void
+DrawEvalText (char *buf, int cbBuf, int y)
+{
+ // the magic constants 8 and 5 should really be derived from the font size somehow
+ cairo_text_extents_t extents;
+ cairo_t *cr = cairo_create(DRAWABLE(disp));
+
+ /* GTK-TODO this has to go into the font-selection */
+ cairo_select_font_face (cr, "Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size (cr, 12.0);
+
+
+ cairo_text_extents (cr, buf, &extents);
+
+ cairo_move_to (cr, MarginX - 2 - 8*cbBuf, y+5);
+ cairo_text_path (cr, buf);
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0);
+ cairo_fill_preserve (cr);
+ cairo_set_source_rgb (cr, 0, 1.0, 0);
+ cairo_set_line_width (cr, 0.1);
+ cairo_stroke (cr);
+
+ /* free memory */
+ cairo_destroy (cr);
+}
+
+static int initDone = FALSE;
+
+static void
+InitializeEvalGraph (Option *opt, int w, int h)
+{
+ if(w == 0) {
+ Arg args[10];
+ XtSetArg(args[0], XtNwidth, &evalGraphW);
+ XtSetArg(args[1], XtNheight, &evalGraphH);
+ XtGetValues(opt->handle, args, 2);
+ nWidthPB = evalGraphW; nHeightPB = evalGraphH;
+ } else nWidthPB = w, nHeightPB = h;
+
+ initDone = TRUE;
+}
+
+// The following stuff is really back-end (but too little to bother with a separate file)
+
+static void
+EvalClick (int x, int y)
+{
+ int index = GetMoveIndexFromPoint( x, y );
+
+ if( index >= 0 && index < currLast ) ToNrEvent( index + 1 );
+}
+
+static Option graphOptions[] = {
+{ 150, 0x9C, 300, NULL, (void*) &EvalCallback, NULL, NULL, Graph , "" },
+{ 0, 2, 0, NULL, NULL, "", NULL, EndMark , "" }
+};
+
+static void
+DisplayEvalGraph ()
+{ // back-end painting; calls back front-end primitives for lines, rectangles and text
+ char *t = MakeEvalTitle(_(title));
+ nWidthPB = disp->max; nHeightPB = disp->value;
+ if(t != title && nWidthPB < 340) t = MakeEvalTitle(nWidthPB < 240 ? "" : _("Eval"));
+ PaintEvalGraph();
+ GraphExpose(graphOptions, 0, 0, nWidthPB, nHeightPB);
+ SetDialogTitle(EvalGraphDlg, t);
+}
+
+static Option *
+EvalCallback (int button, int x, int y)
+{
+ if(!initDone) return NULL;
+
+ switch(button) {
+ case 10: // expose event
+ /* Create or recreate paint box if needed */
+ if(x != nWidthPB || y != nHeightPB) {
+ InitializeEvalGraph(&graphOptions[0], x, y);
+ }
+ nWidthPB = x;
+ nHeightPB = y;
+ DisplayEvalGraph();
+ break;
+ case 1: EvalClick(x, y); // left button
+ default: break; // other buttons ignored
+ }
+ return NULL; // no context menu!
+}
+
+void
+EvalGraphPopUp ()
+{
+ if (GenericPopUp(graphOptions, _(title), EvalGraphDlg, BoardWindow, NONMODAL, 1)) {
+ InitializeEvalGraph(&graphOptions[0], 0, 0); // first time: add callbacks and initialize pens
+ disp = graphOptions;
+ } else {
+ SetDialogTitle(EvalGraphDlg, _(title));
+ SetIconName(EvalGraphDlg, _(title));
+ }
+
+ MarkMenu("View.EvaluationGraph", EvalGraphDlg);
+
+// ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
+}
+
+void
+EvalGraphPopDown ()
+{
+ PopDown(EvalGraphDlg);
+
+// ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
+}
+
+Boolean
+EvalGraphIsUp ()
+{
+ return shellUp[EvalGraphDlg];
+}
+
+int
+EvalGraphDialogExists ()
+{
+ return DialogExists(EvalGraphDlg);
+}
+
+void
+EvalGraphProc ()
+{
+ if (!PopDown(EvalGraphDlg)) EvalGraphPopUp();
+}
+
+// This function is the interface to the back-end.
+
+void
+EvalGraphSet (int first, int last, int current, ChessProgramStats_Move * pvInfo)
+{
+ /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */
+
+ currFirst = first;
+ currLast = last;
+ currCurrent = current;
+ currPvInfo = pvInfo;
+
+ if( DialogExists(EvalGraphDlg) ) {
+ DisplayEvalGraph();
+ }
+}
+
--- /dev/null
+/*
+ * xevalgraph.h
+ *
+ * Copyright 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/. *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#ifndef XB_XEVALGRAPH
+#define XB_XEVALGRAPH
+
+void EvalGraphSet P(( int first, int last, int current, ChessProgramStats_Move * pvInfo ));
+float Color P((char *col, int n));
+void SetPen P((cairo_t *cr, float w, char *col, int dash));
+
+#endif
--- /dev/null
+/*
+ * xgamelist.c -- Game list window, part of X front end for XBoard
+ *
+ * Copyright 1995, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/. *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+# include <string.h>
+# else /* not HAVE_STRING_H */
+# include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/cursorfont.h>
+#if USE_XAW3D
+#include <X11/Xaw3d/Dialog.h>
+#include <X11/Xaw3d/Form.h>
+#include <X11/Xaw3d/List.h>
+#include <X11/Xaw3d/Label.h>
+#include <X11/Xaw3d/SimpleMenu.h>
+#include <X11/Xaw3d/SmeBSB.h>
+#include <X11/Xaw3d/SmeLine.h>
+#include <X11/Xaw3d/Box.h>
+#include <X11/Xaw3d/MenuButton.h>
+#include <X11/Xaw3d/Text.h>
+#include <X11/Xaw3d/AsciiText.h>
+#include <X11/Xaw3d/Viewport.h>
+#else
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+#endif
+
+#include "common.h"
+#include "backend.h"
+#include "xboard.h"
+#include "xgamelist.h"
+#include "dialogs.h"
+
+
+char gameListTranslations[] =
+ "<Btn4Down>: WheelProc(-3) \n \
+ <Btn5Down>: WheelProc(3) \n \
+ <Btn1Down>: LoadSelectedProc(100) Set() \n \
+ <Btn1Up>(2): LoadSelectedProc(0) \n \
+ <Key>Home: LoadSelectedProc(-2) \n \
+ <Key>End: LoadSelectedProc(2) \n \
+ Ctrl<Key>Up: LoadSelectedProc(-3) \n \
+ Ctrl<Key>Down: LoadSelectedProc(3) \n \
+ <Key>Up: LoadSelectedProc(-1) \n \
+ <Key>Down: LoadSelectedProc(1) \n \
+ <Key>Left: LoadSelectedProc(-1) \n \
+ <Key>Right: LoadSelectedProc(1) \n \
+ <Key>Prior: LoadSelectedProc(-4) \n \
+ <Key>Next: LoadSelectedProc(4) \n \
+ <Key>Return: LoadSelectedProc(0) \n";
+char filterTranslations[] =
+ "<Key>Return: SetFilterProc() \n";
+
+
+void
+LoadSelectedProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+ if(GameListClicks(atoi(prms[0]))) return; // if no game loaded, no focus transfer
+ XSync(xDisplay, False);
+ XSetInputFocus(xDisplay, XtWindow(boardWidget), RevertToPointerRoot, CurrentTime);
+}
+
+void
+SetFilterProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+ SetFilter();
+}
+
--- /dev/null
+/*
+ * xgamelist.h -- Game list window, part of X front end for XBoard
+ *
+ * Copyright 1995, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/. *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#ifndef XB_XGAMELIST
+#define XB_XGAMELIST
+
+void LoadSelectedProc P((Widget w, XEvent *event,
+ String *prms, Cardinal *nprms));
+void SetFilterProc P((Widget w, XEvent *event,
+ String *prms, Cardinal *nprms));
+
+#endif /* XB_XGAMELIST */
--- /dev/null
+/*
+ * New (WinBoard-style) Move history for XBoard
+ *
+ * Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ *
+ * ------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/cursorfont.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xatom.h>
+#include <X11/Xmu/Atoms.h>
+
+#include "common.h"
+#include "frontend.h"
+#include "backend.h"
+#include "xhistory.h"
+#include "xboard.h"
+#include "dialogs.h"
+#include "gettext.h"
+
+#ifdef ENABLE_NLS
+# define _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define _(s) (s)
+# define N_(s) s
+#endif
+
+// templates for calls into back-end (= history.c; should be moved to history.h header shared with it!)
+void RefreshMemoContent P((void));
+void MemoContentUpdated P((void));
+void FindMoveByCharIndex P(( int char_index ));
+
+// variables in xoptions.c
+extern Option historyOptions[];
+
+// ------------- low-level front-end actions called by MoveHistory back-end -----------------
+
+void
+HighlightMove (int from, int to, Boolean highlight)
+{
+ if(highlight)
+ XawTextSetSelection( historyOptions[0].handle, from, to ); // for lack of a better method, use selection for highighting
+}
+
+void
+ClearHistoryMemo ()
+{
+ SetWidgetText(&historyOptions[0], "", HistoryDlg);
+}
+
+// the bold argument says 0 = normal, 1 = bold typeface
+// the colorNr argument says 0 = font-default, 1 = gray
+int
+AppendToHistoryMemo (char * text, int bold, int colorNr)
+{
+ return AppendText(&historyOptions[0], text); // for now ignore bold & color stuff, as Xaw cannot handle that
+}
+
+void
+ScrollToCurrent (int caretPos)
+{
+ Arg args[10];
+ char *s;
+ int len;
+ GetWidgetText(&historyOptions[0], &s);
+ len = strlen(s);
+ if(caretPos < 0 || caretPos > len) caretPos = len;
+ if(caretPos > len-30) { // scroll to end, which causes no flicker
+ static XEvent event;
+ XtCallActionProc(historyOptions[0].handle, "end-of-file", &event, NULL, 0);
+ return;
+ }
+ // the following leads to a very annoying flicker, even when no scrolling is done at all.
+ XtSetArg(args[0], XtNinsertPosition, caretPos); // this triggers scrolling in Xaw
+ XtSetArg(args[1], XtNdisplayCaret, False);
+ XtSetValues(historyOptions[0].handle, args, 2);
+}
+
+
+// ------------------------------ callbacks --------------------------
+
+char *historyText;
+char historyTranslations[] =
+"<Btn3Down>: select-start() \n \
+<Btn3Up>: extend-end() SelectMove() \n";
+
+void
+SelectMove (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{
+ XawTextPosition index, dummy;
+
+ XawTextGetSelectionPos(w, &index, &dummy);
+ FindMoveByCharIndex( index ); // [HGM] also does the actual moving to it, now
+}
+
+Option historyOptions[] = {
+{ 200, T_VSCRL | T_FILL | T_WRAP | T_TOP, 400, NULL, (void*) &historyText, "", NULL, TextBox, "" },
+{ 0, NO_OK, 0, NULL, (void*) NULL, "", NULL, EndMark , "" }
+};
+
+// ------------ standard entry points into MoveHistory code -----------
+
+Boolean
+MoveHistoryIsUp ()
+{
+ return shellUp[HistoryDlg];
+}
+
+Boolean
+MoveHistoryDialogExists ()
+{
+ return DialogExists(HistoryDlg);
+}
+
+void
+HistoryPopUp ()
+{
+ if(GenericPopUp(historyOptions, _("Move list"), HistoryDlg, BoardWindow, NONMODAL, 1))
+ AddHandler(&historyOptions[0], 0);
+ MarkMenu("View.MoveHistory", HistoryDlg);
+}
+
+void
+HistoryShowProc ()
+{
+ if (!shellUp[HistoryDlg]) {
+ ASSIGN(historyText, "");
+ HistoryPopUp();
+ RefreshMemoContent();
+ MemoContentUpdated();
+ } else PopDown(HistoryDlg);
+ ToNrEvent(currentMove);
+}
--- /dev/null
+/*
+ * xhistory.h -- Eval graph window, part of X front end for XBoard
+ *
+ * Copyright 1995, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/. *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+#ifndef XB_XHISTLIST
+#define XB_XHISTLIST
+
+void HistoryShowProc P((void));
+Boolean MoveHistoryIsUp P((void));
+
+#endif /* XB_XHISTLIST */
--- /dev/null
+/*
+ * xoptions.c -- Move list window, part of X front end for XBoard
+ *
+ * Copyright 2000, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/. *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history. */
+
+// [HGM] this file is the counterpart of woptions.c, containing xboard popup menus
+// similar to those of WinBoard, to set the most common options interactively.
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+# include <string.h>
+# else /* not HAVE_STRING_H */
+# include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <stdint.h>
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xatom.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/cursorfont.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xaw/Toggle.h>
+#include <X11/Xaw/Scrollbar.h>
+
+#include <cairo/cairo.h>
+#include <cairo/cairo-xlib.h>
+
+#include "common.h"
+#include "backend.h"
+#include "xboard.h"
+#include "dialogs.h"
+#include "menus.h"
+#include "gettext.h"
+
+#ifdef ENABLE_NLS
+# define _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define _(s) (s)
+# define N_(s) s
+#endif
+
+// [HGM] the following code for makng menu popups was cloned from the FileNamePopUp routines
+
+static Widget previous = NULL;
+static Option *currentOption;
+
+void
+UnCaret ()
+{
+ Arg args[2];
+
+ if(previous) {
+ XtSetArg(args[0], XtNdisplayCaret, False);
+ XtSetValues(previous, args, 1);
+ }
+ previous = NULL;
+}
+
+void
+SetFocus (Widget w, XtPointer data, XEvent *event, Boolean *b)
+{
+ Arg args[2];
+ char *s;
+ int j;
+
+ UnCaret();
+ XtSetArg(args[0], XtNstring, &s);
+ XtGetValues(w, args, 1);
+ j = 1;
+ XtSetArg(args[0], XtNdisplayCaret, True);
+ if(!strchr(s, '\n') && strlen(s) < 80) XtSetArg(args[1], XtNinsertPosition, strlen(s)), j++;
+ XtSetValues(w, args, j);
+ XtSetKeyboardFocus((Widget) data, w);
+ previous = w;
+}
+
+void
+BoardFocus ()
+{
+ XtSetKeyboardFocus(shellWidget, formWidget);
+}
+
+//--------------------------- Engine-specific options menu ----------------------------------
+
+int dialogError;
+Option *dialogOptions[NrOfDialogs];
+
+static Arg layoutArgs[] = {
+ { XtNborderWidth, 0 },
+ { XtNdefaultDistance, 0 },
+};
+
+static Arg formArgs[] = {
+ { XtNborderWidth, 0 },
+ { XtNresizable, (XtArgVal) True },
+};
+
+void
+GetWidgetText (Option *opt, char **buf)
+{
+ Arg arg;
+ XtSetArg(arg, XtNstring, buf);
+ XtGetValues(opt->handle, &arg, 1);
+}
+
+void
+SetWidgetText (Option *opt, char *buf, int n)
+{
+ Arg arg;
+ XtSetArg(arg, XtNstring, buf);
+ XtSetValues(opt->handle, &arg, 1);
+ if(n >= 0) SetFocus(opt->handle, shells[n], NULL, False);
+}
+
+void
+GetWidgetState (Option *opt, int *state)
+{
+ Arg arg;
+ XtSetArg(arg, XtNstate, state);
+ XtGetValues(opt->handle, &arg, 1);
+}
+
+void
+SetWidgetState (Option *opt, int state)
+{
+ Arg arg;
+ XtSetArg(arg, XtNstate, state);
+ XtSetValues(opt->handle, &arg, 1);
+}
+
+void
+SetWidgetLabel (Option *opt, char *buf)
+{
+ Arg arg;
+ XtSetArg(arg, XtNlabel, (XtArgVal) buf);
+ XtSetValues(opt->handle, &arg, 1);
+}
+
+void
+SetDialogTitle (DialogClass dlg, char *title)
+{
+ Arg args[16];
+ XtSetArg(args[0], XtNtitle, title);
+ XtSetValues(shells[dlg], args, 1);
+}
+
+void
+LoadListBox (Option *opt, char *emptyText)
+{
+ static char *dummyList[2];
+ dummyList[0] = emptyText; // empty listboxes tend to crash X, so display user-supplied warning string instead
+ XawListChange(opt->handle, *(char*)opt->target ? opt->target : dummyList, 0, 0, True);
+}
+
+int
+ReadScroll (Option *opt, float *top, float *bottom)
+{ // retreives fractions of top and bottom of thumb
+ Arg args[16];
+ Widget w = XtParent(opt->handle); // viewport
+ Widget v = XtNameToWidget(w, "vertical");
+ int j=0;
+ float h;
+ if(!v) return FALSE; // no scroll bar
+ XtSetArg(args[j], XtNshown, &h); j++;
+ XtSetArg(args[j], XtNtopOfThumb, top); j++;
+ XtGetValues(v, args, j);
+ *bottom = *top + h;
+ return TRUE;
+}
+
+void
+SetScroll (Option *opt, float f)
+{ // sets top of thumb to given fraction
+ static char *params[3] = { "", "Continuous", "Proportional" };
+ static XEvent event;
+ Widget w = XtParent(opt->handle); // viewport
+ Widget v = XtNameToWidget(w, "vertical");
+ if(!v) return; // no scroll bar
+ XtCallActionProc(v, "StartScroll", &event, params+1, 1);
+ XawScrollbarSetThumb(v, f, -1.0);
+ XtCallActionProc(v, "NotifyThumb", &event, params, 0);
+// XtCallActionProc(v, "NotifyScroll", &event, params+2, 1);
+ XtCallActionProc(v, "EndScroll", &event, params, 0);
+}
+
+void
+HighlightListBoxItem (Option *opt, int nr)
+{
+ XawListHighlight(opt->handle, nr);
+}
+
+void
+HighlightWithScroll (Option *opt, int sel, int max)
+{
+ float top, bottom, f, g;
+ HighlightListBoxItem(opt, sel);
+ if(!ReadScroll(opt, &top, &bottom)) return; // no scroll bar
+ bottom = bottom*max - 1.f;
+ f = g = top;
+ top *= max;
+ if(sel > (top + 3*bottom)/4) f = (sel - 0.75f*(bottom-top))/max; else
+ if(sel < (3*top + bottom)/4) f = (sel - 0.25f*(bottom-top))/max;
+ if(f < 0.f) f = 0.; if(f + 1.f/max > 1.f) f = 1. - 1./max;
+ if(f != g) SetScroll(opt, f);
+}
+
+int
+SelectedListBoxItem (Option *opt)
+{
+ XawListReturnStruct *rs;
+ rs = XawListShowCurrent(opt->handle);
+ return rs->list_index;
+}
+
+void
+FocusOnWidget (Option *opt, DialogClass dlg)
+{
+ UnCaret();
+ XtSetKeyboardFocus(shells[dlg], opt->handle);
+}
+
+void
+SetIconName (DialogClass dlg, char *name)
+{
+ Arg args[16];
+ int j = 0;
+ XtSetArg(args[j], XtNiconName, (XtArgVal) name); j++;
+// XtSetArg(args[j], XtNtitle, (XtArgVal) name); j++;
+ XtSetValues(shells[dlg], args, j);
+}
+
+static void
+CheckCallback (Widget ww, XtPointer client_data, XEvent *event, Boolean *b)
+{
+ int s, data = (intptr_t) client_data;
+ Option *opt = dialogOptions[data >> 8] + (data & 255);
+
+ if(opt->type == Label) { ((ButtonCallback*) opt->target)(data&255); return; }
+
+ GetWidgetState(opt, &s);
+ SetWidgetState(opt, !s);
+}
+
+static void
+SpinCallback (Widget w, XtPointer client_data, XtPointer call_data)
+{
+ String name, val;
+ Arg args[16];
+ char buf[MSG_SIZ], *p;
+ int j = 0; // Initialisation is necessary because the text value may be non-numeric causing the scanf conversion to fail
+ int data = (intptr_t) client_data;
+ Option *opt = dialogOptions[data >> 8] + (data & 255);
+
+ XtSetArg(args[0], XtNlabel, &name);
+ XtGetValues(w, args, 1);
+
+ GetWidgetText(opt, &val);
+ sscanf(val, "%d", &j);
+ if (strcmp(name, _("browse")) == 0) {
+ char *q=val, *r;
+ for(r = ""; *q; q++) if(*q == '.') r = q; else if(*q == '/') r = ""; // last dot after last slash
+ if(!strcmp(r, "") && !currentCps && opt->type == FileName && opt->textValue)
+ r = opt->textValue;
+ Browse(data>>8, opt->name, NULL, r, opt->type == PathName, "", &p, (FILE**) opt);
+ return;
+ } else
+ if (strcmp(name, "+") == 0) {
+ if(++j > opt->max) return;
+ } else
+ if (strcmp(name, "-") == 0) {
+ if(--j < opt->min) return;
+ } else return;
+ snprintf(buf, MSG_SIZ, "%d", j);
+ SetWidgetText(opt, buf, TransientDlg);
+}
+
+static void
+ComboSelect (Widget w, caddr_t addr, caddr_t index) // callback for all combo items
+{
+ Arg args[16];
+ Option *opt = dialogOptions[((intptr_t)addr)>>24]; // applicable option list
+ int i = ((intptr_t)addr)>>16 & 255; // option number
+ int j = 0xFFFF & (intptr_t) addr;
+
+ values[i] = j; // store selected value in Option struct, for retrieval at OK
+
+ if(opt[i].type == Graph || opt[i].min & COMBO_CALLBACK && (!currentCps || shellUp[BrowserDlg])) {
+ ((ButtonCallback*) opt[i].target)(i);
+ return;
+ }
+
+ if(opt[i].min & NO_GETTEXT)
+ XtSetArg(args[0], XtNlabel, ((char**)opt[i].choice)[j]);
+ else
+ XtSetArg(args[0], XtNlabel, _(((char**)opt[i].choice)[j]));
+
+ XtSetValues(opt[i].handle, args, 1);
+}
+
+Widget
+CreateMenuItem (Widget menu, char *msg, XtCallbackProc CB, int n)
+{
+ int j=0;
+ Widget entry;
+ Arg args[16];
+ XtSetArg(args[j], XtNleftMargin, 20); j++;
+ XtSetArg(args[j], XtNrightMargin, 20); j++;
+ if(!strcmp(msg, "----")) { XtCreateManagedWidget(msg, smeLineObjectClass, menu, args, j); return NULL; }
+ XtSetArg(args[j], XtNlabel, msg);
+ entry = XtCreateManagedWidget("item", smeBSBObjectClass, menu, args, j+1);
+ XtAddCallback(entry, XtNcallback, CB, (caddr_t)(intptr_t) n);
+ return entry;
+}
+
+static Widget
+CreateComboPopup (Widget parent, Option *opt, int n, int fromList, int def)
+{ // fromList determines if the item texts are taken from a list of strings, or from a menu table
+ int i;
+ Widget menu, entry;
+ Arg arg;
+ MenuItem *mb = (MenuItem *) opt->choice;
+ char **list = (char **) opt->choice;
+
+ if(list[0] == NULL) return NULL; // avoid empty menus, as they cause crash
+ menu = XtCreatePopupShell(opt->name, simpleMenuWidgetClass, parent, NULL, 0);
+
+ for (i=0; 1; i++)
+ {
+ char *msg = fromList ? list[i] : mb[i].string;
+ if(!msg) break;
+ entry = CreateMenuItem(menu, opt->min & NO_GETTEXT ? msg : _(msg), (XtCallbackProc) ComboSelect, (n<<16)+i);
+ if(!fromList) mb[i].handle = (void*) entry; // save item ID, for enabling / checkmarking
+ if(i==def) {
+ XtSetArg(arg, XtNpopupOnEntry, entry);
+ XtSetValues(menu, &arg, 1);
+ }
+ }
+ return menu;
+}
+
+char moveTypeInTranslations[] =
+ "<Key>Return: TypeInProc(1) \n"
+ "<Key>Escape: TypeInProc(0) \n";
+extern char filterTranslations[];
+extern char gameListTranslations[];
+extern char memoTranslations[];
+
+
+char *translationTable[] = { // beware: order is essential!
+ historyTranslations, commentTranslations, moveTypeInTranslations, ICSInputTranslations,
+ filterTranslations, gameListTranslations, memoTranslations
+};
+
+void
+AddHandler (Option *opt, int nr)
+{
+ XtOverrideTranslations(opt->handle, XtParseTranslationTable(translationTable[nr]));
+}
+
+//----------------------------Generic dialog --------------------------------------------
+
+// cloned from Engine Settings dialog (and later merged with it)
+
+Widget shells[NrOfDialogs];
+DialogClass parents[NrOfDialogs];
+WindowPlacement *wp[NrOfDialogs] = { // Beware! Order must correspond to DialogClass enum
+ NULL, &wpComment, &wpTags, NULL, NULL, NULL, NULL, &wpMoveHistory, &wpGameList, &wpEngineOutput, &wpEvalGraph,
+ NULL, NULL, NULL, NULL, /*&wpMain*/ NULL
+};
+
+int
+DialogExists (DialogClass n)
+{ // accessor for use in back-end
+ return shells[n] != NULL;
+}
+
+void
+RaiseWindow (DialogClass dlg)
+{
+ static XEvent xev;
+ Window root = RootWindow(xDisplay, DefaultScreen(xDisplay));
+ Atom atom = XInternAtom (xDisplay, "_NET_ACTIVE_WINDOW", False);
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.display = xDisplay;
+ xev.xclient.window = XtWindow(shells[dlg]);
+ xev.xclient.message_type = atom;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 1;
+ xev.xclient.data.l[1] = CurrentTime;
+
+ XSendEvent (xDisplay,
+ root, False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &xev);
+
+ XFlush(xDisplay);
+ XSync(xDisplay, False);
+}
+
+int
+PopDown (DialogClass n)
+{ // pops down any dialog created by GenericPopUp (or returns False if it wasn't up), unmarks any associated marked menu
+ int j;
+ Arg args[10];
+ Dimension windowH, windowW; Position windowX, windowY;
+ if (!shellUp[n] || !shells[n]) return 0;
+ if(n && wp[n]) { // remember position
+ j = 0;
+ XtSetArg(args[j], XtNx, &windowX); j++;
+ XtSetArg(args[j], XtNy, &windowY); j++;
+ XtSetArg(args[j], XtNheight, &windowH); j++;
+ XtSetArg(args[j], XtNwidth, &windowW); j++;
+ XtGetValues(shells[n], args, j);
+ wp[n]->x = windowX;
+ wp[n]->x = windowY;
+ wp[n]->width = windowW;
+ wp[n]->height = windowH;
+ }
+ previous = NULL;
+ XtPopdown(shells[n]);
+ shellUp[n]--; // count rather than clear
+ if(n == 0 || n >= PromoDlg) XtDestroyWidget(shells[n]), shells[n] = NULL;
+ if(marked[n]) {
+ MarkMenuItem(marked[n], False);
+ marked[n] = NULL;
+ }
+ if(!n && n != BrowserDlg) currentCps = NULL; // if an Engine Settings dialog was up, we must be popping it down now
+ currentOption = dialogOptions[TransientDlg]; // just in case a transient dialog was up (to allow its check and combo callbacks to work)
+ RaiseWindow(parents[n]);
+ if(parents[n] == BoardWindow) XtSetKeyboardFocus(shellWidget, formWidget);
+ return 1;
+}
+
+void
+GenericPopDown (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{ // to cause popdown through a translation (Delete Window button!)
+ int dlg = atoi(prms[0]);
+ Widget sh = shells[dlg];
+ if(shellUp[BrowserDlg] && dlg != BrowserDlg || dialogError) return; // prevent closing dialog when it has an open file-browse daughter
+ shells[dlg] = w;
+ PopDown(dlg);
+ shells[dlg] = sh; // restore
+}
+
+int
+AppendText (Option *opt, char *s)
+{
+ XawTextBlock t;
+ char *v;
+ int len;
+ GetWidgetText(opt, &v);
+ len = strlen(v);
+ t.ptr = s; t.firstPos = 0; t.length = strlen(s); t.format = XawFmt8Bit;
+ XawTextReplace(opt->handle, len, len, &t);
+ return len;
+}
+
+void
+SetColor (char *colorName, Option *box)
+{ // sets the color of a widget
+ Arg args[5];
+ Pixel buttonColor;
+ XrmValue vFrom, vTo;
+ if (!appData.monoMode) {
+ vFrom.addr = (caddr_t) colorName;
+ vFrom.size = strlen(colorName);
+ XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
+ if (vTo.addr == NULL) {
+ buttonColor = (Pixel) -1;
+ } else {
+ buttonColor = *(Pixel *) vTo.addr;
+ }
+ } else buttonColor = timerBackgroundPixel;
+ XtSetArg(args[0], XtNbackground, buttonColor);;
+ XtSetValues(box->handle, args, 1);
+}
+
+void
+ColorChanged (Widget w, XtPointer data, XEvent *event, Boolean *b)
+{ // for detecting a typed change in color
+ char buf[10];
+ if ( (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) && *buf == '\r' )
+ RefreshColor((int)(intptr_t) data, 0);
+}
+
+static void
+GraphEventProc(Widget widget, caddr_t client_data, XEvent *event)
+{ // handle expose and mouse events on Graph widget
+ Dimension w, h;
+ Arg args[16];
+ int j, button=10, f=1, sizing=0;
+ Option *opt, *graph = (Option *) client_data;
+ PointerCallback *userHandler = graph->target;
+
+ if (!XtIsRealized(widget)) return;
+
+ switch(event->type) {
+ case Expose: // make handling of expose events generic, just copying from memory buffer (->choice) to display (->textValue)
+ /* Get window size */
+ j = 0;
+ XtSetArg(args[j], XtNwidth, &w); j++;
+ XtSetArg(args[j], XtNheight, &h); j++;
+ XtGetValues(widget, args, j);
+
+ if(w < graph->max || w > graph->max + 1 || h != graph->value) { // use width fudge of 1 pixel
+ if(((XExposeEvent*)event)->count >= 0) { // suppress sizing on expose for ordered redraw in response to sizing.
+ sizing = 1;
+ graph->max = w; graph->value = h; // note: old values are kept if we we don't exceed width fudge
+ }
+ } else w = graph->max;
+
+ if(sizing && ((XExposeEvent*)event)->count > 0) { graph->max = 0; return; } // don't bother if further exposure is pending during resize
+ if(!graph->textValue || sizing) { // create surfaces of new size for display widget
+ if(graph->textValue) cairo_surface_destroy((cairo_surface_t *)graph->textValue);
+ graph->textValue = (char*) cairo_xlib_surface_create(xDisplay, XtWindow(widget), DefaultVisual(xDisplay, 0), w, h);
+ }
+ if(sizing) { // the memory buffer was already created in GenericPopup(),
+ // to give drawing routines opportunity to use it before first expose event
+ // (which are only processed when main gets to the event loop, so after all init!)
+ // so only change when size is no longer good
+ if(graph->choice) cairo_surface_destroy((cairo_surface_t *) graph->choice);
+ graph->choice = (char**) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
+ break;
+ }
+ w = ((XExposeEvent*)event)->width;
+ if(((XExposeEvent*)event)->x + w > graph->max) w--; // cut off fudge pixel
+ if(w) ExposeRedraw(graph, ((XExposeEvent*)event)->x, ((XExposeEvent*)event)->y, w, ((XExposeEvent*)event)->height);
+ return;
+ case MotionNotify:
+ f = 0;
+ w = ((XButtonEvent*)event)->x; h = ((XButtonEvent*)event)->y;
+ break;
+ case ButtonRelease:
+ f = -1; // release indicated by negative button numbers
+ case ButtonPress:
+ w = ((XButtonEvent*)event)->x; h = ((XButtonEvent*)event)->y;
+ switch(((XButtonEvent*)event)->button) {
+ case Button1: button = 1; break;
+ case Button2: button = 2; break;
+ case Button3: button = 3; break;
+ case Button4: button = 4; break;
+ case Button5: button = 5; break;
+ }
+ }
+ button *= f;
+ opt = userHandler(button, w, h);
+ if(opt) { // user callback specifies a context menu; pop it up
+ XUngrabPointer(xDisplay, CurrentTime);
+ XtCallActionProc(widget, "XawPositionSimpleMenu", event, &(opt->name), 1);
+ XtPopupSpringLoaded(opt->handle);
+ }
+ XSync(xDisplay, False);
+}
+
+void
+GraphExpose (Option *opt, int x, int y, int w, int h)
+{
+ XExposeEvent e;
+ if(!opt->handle) return;
+ e.x = x; e.y = y; e.width = w; e.height = h; e.count = -1; e.type = Expose; // count = -1: kludge to suppress sizing
+ GraphEventProc(opt->handle, (caddr_t) opt, (XEvent *) &e); // fake expose event
+}
+
+static void
+GenericCallback (Widget w, XtPointer client_data, XtPointer call_data)
+{ // all Buttons in a dialog (including OK, cancel) invoke this
+ String name;
+ Arg args[16];
+ char buf[MSG_SIZ];
+ int data = (intptr_t) client_data;
+ DialogClass dlg;
+ Widget sh = XtParent(XtParent(XtParent(w))), oldSh;
+
+ currentOption = dialogOptions[dlg=data>>16]; data &= 0xFFFF;
+ oldSh = shells[dlg]; shells[dlg] = sh; // bow to reality
+ if (data == 30000) { // cancel
+ PopDown(dlg);
+ } else
+ if (data == 30001) { // save buttons imply OK
+ if(GenericReadout(currentOption, -1)) PopDown(dlg); // calls OK-proc after full readout, but no popdown if it returns false
+ } else
+
+ if(currentCps && dlg != BrowserDlg) {
+ XtSetArg(args[0], XtNlabel, &name);
+ XtGetValues(w, args, 1);
+ if(currentOption[data].type == SaveButton) GenericReadout(currentOption, -1);
+ snprintf(buf, MSG_SIZ, "option %s\n", name);
+ SendToProgram(buf, currentCps);
+ } else ((ButtonCallback*) currentOption[data].target)(data);
+
+ shells[dlg] = oldSh; // in case of multiple instances, restore previous (as this one could be popped down now)
+}
+
+void
+TabProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{ // for transfering focus to the next text-edit
+ Option *opt;
+ for(opt = currentOption; opt->type != EndMark; opt++) {
+ if(opt->handle == w) {
+ while(++opt) {
+ if(opt->type == EndMark) opt = currentOption; // wrap
+ if(opt->handle == w) return; // full circle
+ if(opt->type == TextBox || opt->type == Spin || opt->type == Fractional || opt->type == FileName || opt->type == PathName) {
+ SetFocus(opt->handle, XtParent(XtParent(XtParent(w))), NULL, 0);
+ return;
+ }
+ }
+ }
+ }
+}
+
+void
+WheelProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{ // for scrolling a widget seen through a viewport with the mouse wheel (ListBox!)
+ int j=0, n = atoi(prms[0]);
+ static char *params[3] = { "", "Continuous", "Proportional" };
+ Arg args[16];
+ float h, top;
+ Widget v;
+ if(!n) { // transient dialogs also use this for list-selection callback
+ n = prms[1][0]-'0';
+ Option *opt=dialogOptions[prms[2][0]-'A'] + n;
+ if(opt->textValue) ((ListBoxCallback*) opt->textValue)(n, SelectedListBoxItem(opt));
+ return;
+ }
+ v = XtNameToWidget(XtParent(w), "vertical");
+ if(!v) return;
+ XtSetArg(args[j], XtNshown, &h); j++;
+ XtSetArg(args[j], XtNtopOfThumb, &top); j++;
+ XtGetValues(v, args, j);
+ top += 0.1f*h*n; if(top < 0.f) top = 0.;
+ XtCallActionProc(v, "StartScroll", event, params+1, 1);
+ XawScrollbarSetThumb(v, top, -1.0);
+ XtCallActionProc(v, "NotifyThumb", event, params, 0);
+// XtCallActionProc(w, "NotifyScroll", event, params+2, 1);
+ XtCallActionProc(v, "EndScroll", event, params, 0);
+}
+
+static char *oneLiner =
+ "<Key>Return: redraw-display() \n \
+ <Key>Tab: TabProc() \n ";
+static char scrollTranslations[] =
+ "<Btn1Up>(2): WheelProc(0 0 A) \n \
+ <Btn4Down>: WheelProc(-1) \n \
+ <Btn5Down>: WheelProc(1) \n ";
+
+static void
+SqueezeIntoBox (Option *opt, int nr, int width)
+{ // size buttons in bar to fit, clipping button names where necessary
+ int i, wtot = 0;
+ Dimension widths[20], oldWidths[20];
+ Arg arg;
+ for(i=1; i<nr; i++) {
+ XtSetArg(arg, XtNwidth, &widths[i]);
+ XtGetValues(opt[i].handle, &arg, 1);
+ wtot += oldWidths[i] = widths[i];
+ }
+ opt->min = wtot;
+ if(width <= 0) return;
+ while(wtot > width) {
+ int wmax=0, imax=0;
+ for(i=1; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
+ widths[imax]--;
+ wtot--;
+ }
+ for(i=1; i<nr; i++) if(widths[i] != oldWidths[i]) {
+ XtSetArg(arg, XtNwidth, widths[i]);
+ XtSetValues(opt[i].handle, &arg, 1);
+ }
+ opt->min = wtot;
+}
+
+int
+SetPositionAndSize (Arg *args, Widget leftNeigbor, Widget topNeigbor, int b, int w, int h, int chaining)
+{ // sizing and positioning most widgets have in common
+ int j = 0;
+ // first position the widget w.r.t. earlier ones
+ if(chaining & 1) { // same row: position w.r.t. last (on current row) and lastrow
+ XtSetArg(args[j], XtNfromVert, topNeigbor); j++;
+ XtSetArg(args[j], XtNfromHoriz, leftNeigbor); j++;
+ } else // otherwise it goes at left margin (which is default), below the previous element
+ XtSetArg(args[j], XtNfromVert, leftNeigbor), j++;
+ // arrange chaining ('2'-bit indicates top and bottom chain the same)
+ if((chaining & 14) == 6) XtSetArg(args[j], XtNtop, XtChainBottom), j++;
+ if((chaining & 14) == 10) XtSetArg(args[j], XtNbottom, XtChainTop ), j++;
+ if(chaining & 4) XtSetArg(args[j], XtNbottom, XtChainBottom ), j++;
+ if(chaining & 8) XtSetArg(args[j], XtNtop, XtChainTop), j++;
+ if(chaining & 0x10) XtSetArg(args[j], XtNright, XtChainRight), j++;
+ if(chaining & 0x20) XtSetArg(args[j], XtNleft, XtChainRight), j++;
+ if(chaining & 0x40) XtSetArg(args[j], XtNright, XtChainLeft ), j++;
+ if(chaining & 0x80) XtSetArg(args[j], XtNleft, XtChainLeft ), j++;
+ // set size (if given)
+ if(w) XtSetArg(args[j], XtNwidth, w), j++;
+ if(h) XtSetArg(args[j], XtNheight, h), j++;
+ // color
+ if(!appData.monoMode) {
+ if(!b && appData.dialogColor[0]) XtSetArg(args[j], XtNbackground, dialogColor), j++;
+ if(b == 3 && appData.buttonColor[0]) XtSetArg(args[j], XtNbackground, buttonColor), j++;
+ }
+ if(b == 3) b = 1;
+ // border
+ XtSetArg(args[j], XtNborderWidth, b); j++;
+ return j;
+}
+
+int
+GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent, int modal, int top)
+{
+ Arg args[24];
+ Widget popup, layout, dialog=NULL, edit=NULL, form, last, b_ok, b_cancel, previousPane = NULL, textField = NULL, oldForm, oldLastRow, oldForeLast;
+ Window root, child;
+ int x, y, i, j, height=999, width=1, h, c, w, shrink=FALSE, stack = 0, box, chain;
+ int win_x, win_y, maxWidth, maxTextWidth;
+ unsigned int mask;
+ char def[MSG_SIZ], *msg, engineDlg = (currentCps != NULL && dlgNr != BrowserDlg);
+ static char pane[6] = "paneX";
+ Widget texts[100], forelast = NULL, anchor, widest, lastrow = NULL, browse = NULL;
+ Dimension bWidth = 50;
+
+ if(dlgNr < PromoDlg && shellUp[dlgNr]) return 0; // already up
+ if(dlgNr && dlgNr < PromoDlg && shells[dlgNr]) { // reusable, and used before (but popped down)
+ XtPopup(shells[dlgNr], XtGrabNone);
+ shellUp[dlgNr] = True;
+ return 0;
+ }
+
+ dialogOptions[dlgNr] = option; // make available to callback
+ // post currentOption globally, so Spin and Combo callbacks can already use it
+ // WARNING: this kludge does not work for persistent dialogs, so that these cannot have spin or combo controls!
+ currentOption = option;
+
+ if(engineDlg) { // Settings popup for engine: format through heuristic
+ int n = currentCps->nrOptions;
+ if(!n) { DisplayNote(_("Engine has no options")); currentCps = NULL; return 0; }
+ if(n > 50) width = 4; else if(n>24) width = 2; else width = 1;
+ height = n / width + 1;
+ if(n && (currentOption[n-1].type == Button || currentOption[n-1].type == SaveButton)) currentOption[n].min = SAME_ROW; // OK on same line
+ currentOption[n].type = EndMark; currentOption[n].target = NULL; // delimit list by callback-less end mark
+ }
+ i = 0;
+ XtSetArg(args[i], XtNresizable, True); i++;
+ shells[BoardWindow] = shellWidget; parents[dlgNr] = parent;
+
+ if(dlgNr == BoardWindow) popup = shellWidget; else
+ popup = shells[dlgNr] =
+ XtCreatePopupShell(title, !top || !appData.topLevel ? transientShellWidgetClass : topLevelShellWidgetClass,
+ shells[parent], args, i);
+
+ layout =
+ XtCreateManagedWidget(layoutName, formWidgetClass, popup,
+ layoutArgs, XtNumber(layoutArgs));
+ if(!appData.monoMode && appData.dialogColor[0]) XtSetArg(args[0], XtNbackground, dialogColor);
+ XtSetValues(layout, args, 1);
+
+ for(c=0; c<width; c++) {
+ pane[4] = 'A'+c;
+ form =
+ XtCreateManagedWidget(pane, formWidgetClass, layout,
+ formArgs, XtNumber(formArgs));
+ j=0;
+ XtSetArg(args[j], stack ? XtNfromVert : XtNfromHoriz, previousPane); j++;
+ if(!appData.monoMode && appData.dialogColor[0]) XtSetArg(args[j], XtNbackground, dialogColor), j++;
+ XtSetValues(form, args, j);
+ lastrow = forelast = NULL;
+ previousPane = form;
+
+ last = widest = NULL; anchor = lastrow;
+ for(h=0; h<height || c == width-1; h++) {
+ i = h + c*height;
+ if(option[i].type == EndMark) break;
+ if(option[i].type == -1) continue;
+ lastrow = forelast;
+ forelast = last;
+ switch(option[i].type) {
+ case Fractional:
+ snprintf(def, MSG_SIZ, "%.2f", *(float*)option[i].target);
+ option[i].value = *(float*)option[i].target;
+ goto tBox;
+ case Spin:
+ if(!engineDlg) option[i].value = *(int*)option[i].target;
+ snprintf(def, MSG_SIZ, "%d", option[i].value);
+ case TextBox:
+ case FileName:
+ case PathName:
+ tBox:
+ if(option[i].name[0]) { // prefixed by label with option name
+ j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+ 0 /* w */, textHeight /* h */, 0xC0 /* chain to left edge */);
+ XtSetArg(args[j], XtNjustify, XtJustifyLeft); j++;
+ XtSetArg(args[j], XtNlabel, _(option[i].name)); j++;
+ texts[h] = dialog = XtCreateManagedWidget(option[i].name, labelWidgetClass, form, args, j);
+ } else texts[h] = dialog = NULL; // kludge to position from left margin
+ w = option[i].type == Spin || option[i].type == Fractional ? 70 : option[i].max ? option[i].max : 205;
+ if(option[i].type == FileName || option[i].type == PathName) w -= 55;
+ j = SetPositionAndSize(args, dialog, last, 1 /* border */,
+ w /* w */, option[i].type == TextBox ? option[i].value : 0 /* h */, 0x91 /* chain full width */);
+ if(option[i].type == TextBox) { // decorations for multi-line text-edits
+ if(option[i].min & T_VSCRL) { XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++; }
+ if(option[i].min & T_HSCRL) { XtSetArg(args[j], XtNscrollHorizontal, XawtextScrollAlways); j++; }
+ if(option[i].min & T_FILL) { XtSetArg(args[j], XtNautoFill, True); j++; }
+ if(option[i].min & T_WRAP) { XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++; }
+ if(option[i].min & T_TOP) { XtSetArg(args[j], XtNtop, XtChainTop); j++;
+ if(!option[i].value) { XtSetArg(args[j], XtNbottom, XtChainTop); j++;
+ XtSetValues(dialog, args+j-2, 2);
+ }
+ }
+ } else shrink = TRUE;
+ XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
+ XtSetArg(args[j], XtNuseStringInPlace, False); j++;
+ XtSetArg(args[j], XtNdisplayCaret, False); j++;
+ XtSetArg(args[j], XtNresizable, True); j++;
+ XtSetArg(args[j], XtNinsertPosition, 9999); j++;
+ XtSetArg(args[j], XtNstring, option[i].type==Spin || option[i].type==Fractional ? def :
+ engineDlg ? option[i].textValue : *(char**)option[i].target); j++;
+ edit = last;
+ option[i].handle = (void*)
+ (textField = last = XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j));
+ XtAddEventHandler(last, ButtonPressMask, False, SetFocus, (XtPointer) popup); // gets focus on mouse click
+ if(option[i].min == 0 || option[i].type != TextBox)
+ XtOverrideTranslations(last, XtParseTranslationTable(oneLiner)); // standard handler for <Enter> and <Tab>
+
+ if(option[i].type == TextBox || option[i].type == Fractional) break;
+
+ // add increment and decrement controls for spin
+ if(option[i].type == FileName || option[i].type == PathName) {
+ msg = _("browse"); w = 0; // automatically scale to width of text
+ j = textHeight ? textHeight : 0;
+ } else {
+ w = 20; msg = "+"; j = textHeight/2; // spin button
+ }
+ j = SetPositionAndSize(args, last, edit, 3 /* border */,
+ w /* w */, j /* h */, 0x31 /* chain to right edge */);
+ edit = XtCreateManagedWidget(msg, commandWidgetClass, form, args, j);
+ XtAddCallback(edit, XtNcallback, SpinCallback, (XtPointer)(intptr_t) i + 256*dlgNr);
+ if(w == 0) browse = edit;
+
+ if(option[i].type != Spin) break;
+
+ j = SetPositionAndSize(args, last, edit, 3 /* border */,
+ 20 /* w */, textHeight/2 /* h */, 0x31 /* chain to right edge */);
+ XtSetArg(args[j], XtNvertDistance, -1); j++;
+ last = XtCreateManagedWidget("-", commandWidgetClass, form, args, j);
+ XtAddCallback(last, XtNcallback, SpinCallback, (XtPointer)(intptr_t) i + 256*dlgNr);
+ break;
+ case CheckBox:
+ if(!engineDlg) option[i].value = *(Boolean*)option[i].target; // where checkbox callback uses it
+ j = SetPositionAndSize(args, last, lastrow, 1 /* border */,
+ textHeight/2 /* w */, textHeight/2 /* h */, 0xC0 /* chain both to left edge */);
+ XtSetArg(args[j], XtNvertDistance, (textHeight+2)/4 + 3); j++;
+ XtSetArg(args[j], XtNstate, option[i].value); j++;
+ lastrow = last;
+ option[i].handle = (void*)
+ (last = XtCreateManagedWidget(" ", toggleWidgetClass, form, args, j));
+ j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+ option[i].max /* w */, textHeight /* h */, 0xC1 /* chain */);
+ XtSetArg(args[j], XtNjustify, XtJustifyLeft); j++;
+ XtSetArg(args[j], XtNlabel, _(option[i].name)); j++;
+ last = XtCreateManagedWidget("label", commandWidgetClass, form, args, j);
+ // make clicking the text toggle checkbox
+ XtAddEventHandler(last, ButtonPressMask, False, CheckCallback, (XtPointer)(intptr_t) i + 256*dlgNr);
+ shrink = TRUE; // following buttons must get text height
+ break;
+ case Label:
+ msg = option[i].name;
+ if(!msg) break;
+ chain = option[i].min;
+ if(chain & SAME_ROW) forelast = lastrow; else shrink = FALSE;
+ j = SetPositionAndSize(args, last, lastrow, (chain & 2) != 0 /* border */,
+ option[i].max /* w */, shrink ? textHeight : 0 /* h */, chain | 2 /* chain */);
+#if ENABLE_NLS
+ if(option[i].choice) XtSetArg(args[j], XtNfontSet, *(XFontSet*)option[i].choice), j++;
+#else
+ if(option[i].choice) XtSetArg(args[j], XtNfont, (XFontStruct*)option[i].choice), j++;
+#endif
+ XtSetArg(args[j], XtNresizable, False); j++;
+ XtSetArg(args[j], XtNjustify, XtJustifyLeft); j++;
+ XtSetArg(args[j], XtNlabel, _(msg)); j++;
+ option[i].handle = (void*) (last = XtCreateManagedWidget("label", labelWidgetClass, form, args, j));
+ if(option[i].target) // allow user to specify event handler for button presses
+ XtAddEventHandler(last, ButtonPressMask, False, CheckCallback, (XtPointer)(intptr_t) i + 256*dlgNr);
+ break;
+ case SaveButton:
+ case Button:
+ if(option[i].min & SAME_ROW) {
+ chain = 0x31; // 0011.0001 = both left and right side to right edge
+ forelast = lastrow;
+ } else chain = 0, shrink = FALSE;
+ j = SetPositionAndSize(args, last, lastrow, 3 /* border */,
+ option[i].max /* w */, shrink ? textHeight : 0 /* h */, option[i].min & 0xE | chain /* chain */);
+ XtSetArg(args[j], XtNlabel, _(option[i].name)); j++;
+ if(option[i].textValue) { // special for buttons of New Variant dialog
+ XtSetArg(args[j], XtNsensitive, appData.noChessProgram || option[i].value < 0
+ || strstr(first.variants, VariantName(option[i].value))); j++;
+ XtSetArg(args[j], XtNborderWidth, (gameInfo.variant == option[i].value)+1); j++;
+ }
+ option[i].handle = (void*)
+ (dialog = last = XtCreateManagedWidget(option[i].name, commandWidgetClass, form, args, j));
+ if(option[i].choice && ((char*)option[i].choice)[0] == '#' && !engineDlg) { // for the color picker default-reset
+ SetColor( *(char**) option[i-1].target, &option[i]);
+ XtAddEventHandler(option[i-1].handle, KeyReleaseMask, False, ColorChanged, (XtPointer)(intptr_t) i-1);
+ }
+ XtAddCallback(last, XtNcallback, GenericCallback, (XtPointer)(intptr_t) i + (dlgNr<<16)); // invokes user callback
+ if(option[i].textValue) SetColor( option[i].textValue, &option[i]); // for new-variant buttons
+ break;
+ case ComboBox:
+ j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+ 0 /* w */, textHeight /* h */, 0xC0 /* chain both sides to left edge */);
+ XtSetArg(args[j], XtNjustify, XtJustifyLeft); j++;
+ XtSetArg(args[j], XtNlabel, _(option[i].name)); j++;
+ texts[h] = dialog = XtCreateManagedWidget(option[i].name, labelWidgetClass, form, args, j);
+
+ if(option[i].min & COMBO_CALLBACK) msg = _(option[i].name); else {
+ if(!engineDlg) SetCurrentComboSelection(option+i);
+ msg=_(((char**)option[i].choice)[option[i].value]);
+ }
+
+ j = SetPositionAndSize(args, dialog, last, (option[i].min & 2) == 0 /* border */,
+ option[i].max && !engineDlg ? option[i].max : 100 /* w */,
+ textHeight /* h */, 0x91 /* chain */); // same row as its label!
+ XtSetArg(args[j], XtNmenuName, XtNewString(option[i].name)); j++;
+ XtSetArg(args[j], XtNlabel, msg); j++;
+ shrink = TRUE;
+ option[i].handle = (void*)
+ (last = XtCreateManagedWidget(" ", menuButtonWidgetClass, form, args, j));
+ CreateComboPopup(last, option + i, i + 256*dlgNr, TRUE, -1);
+ values[i] = option[i].value;
+ break;
+ case ListBox:
+ // Listbox goes in viewport, as needed for game list
+ if(option[i].min & SAME_ROW) forelast = lastrow;
+ j = SetPositionAndSize(args, last, lastrow, 1 /* border */,
+ option[i].max /* w */, option[i].value /* h */, option[i].min /* chain */);
+ XtSetArg(args[j], XtNresizable, False); j++;
+ XtSetArg(args[j], XtNallowVert, True); j++; // scoll direction
+ last =
+ XtCreateManagedWidget("viewport", viewportWidgetClass, form, args, j);
+ j = 0; // now list itself
+ XtSetArg(args[j], XtNdefaultColumns, 1); j++;
+ XtSetArg(args[j], XtNforceColumns, True); j++;
+ XtSetArg(args[j], XtNverticalList, True); j++;
+ option[i].handle = (void*)
+ (edit = XtCreateManagedWidget("list", listWidgetClass, last, args, j));
+ XawListChange(option[i].handle, option[i].target, 0, 0, True);
+ XawListHighlight(option[i].handle, 0);
+ scrollTranslations[25] = '0' + i;
+ scrollTranslations[27] = 'A' + dlgNr;
+ XtOverrideTranslations(edit, XtParseTranslationTable(scrollTranslations)); // for mouse-wheel
+ break;
+ case Graph:
+ j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+ option[i].max /* w */, option[i].value /* h */, option[i].min /* chain */);
+ option[i].handle = (void*)
+ (last = XtCreateManagedWidget("graph", widgetClass, form, args, j));
+ XtAddEventHandler(last, ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask, False,
+ (XtEventHandler) GraphEventProc, &option[i]); // mandatory user-supplied expose handler
+ if(option[i].min & SAME_ROW) last = forelast, forelast = lastrow;
+ option[i].choice = (char**) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, option[i].max, option[i].value); // image buffer
+ break;
+ case PopUp: // note: used only after Graph, so 'last' refers to the Graph widget
+ option[i].handle = (void*) CreateComboPopup(last, option + i, i + 256*dlgNr, TRUE, option[i].value);
+ break;
+ case BoxBegin:
+ if(option[i].min & SAME_ROW) forelast = lastrow;
+ j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+ 0 /* w */, 0 /* h */, option[i].min /* chain */);
+ XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
+ XtSetArg(args[j], XtNvSpace, 0); j++;
+ option[box=i].handle = (void*)
+ (last = XtCreateWidget("box", boxWidgetClass, form, args, j));
+ oldForm = form; form = last; oldLastRow = lastrow; oldForeLast = forelast;
+ lastrow = NULL; last = NULL;
+ break;
+ case DropDown:
+ j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+ 0 /* w */, 0 /* h */, 1 /* chain (always on same row) */);
+ forelast = lastrow;
+ msg = _(option[i].name); // write name on the menu button
+ XtSetArg(args[j], XtNmenuName, XtNewString(option[i].name)); j++;
+ XtSetArg(args[j], XtNlabel, msg); j++;
+ option[i].handle = (void*)
+ (last = XtCreateManagedWidget(option[i].name, menuButtonWidgetClass, form, args, j));
+ option[i].textValue = (char*) CreateComboPopup(last, option + i, i + 256*dlgNr, FALSE, -1);
+ break;
+ case BoxEnd:
+ XtManageChildren(&form, 1);
+ SqueezeIntoBox(&option[box], i-box, option[box].max);
+ if(option[i].target) ((ButtonCallback*)option[i].target)(box); // callback that can make sizing decisions
+ last = form; lastrow = oldLastRow; form = oldForm; forelast = oldForeLast;
+ break;
+ case Break:
+ width++;
+ height = i+1;
+ stack = !(option[i].min & SAME_ROW);
+ break;
+ default:
+ printf("GenericPopUp: unexpected case in switch.\n");
+ break;
+ }
+ }
+
+ // make an attempt to align all spins and textbox controls
+ maxWidth = maxTextWidth = 0;
+ if(browse != NULL) {
+ j=0;
+ XtSetArg(args[j], XtNwidth, &bWidth); j++;
+ XtGetValues(browse, args, j);
+ }
+ for(h=0; h<height || c == width-1; h++) {
+ i = h + c*height;
+ if(option[i].type == EndMark) break;
+ if(option[i].type == Spin || option[i].type == TextBox || option[i].type == ComboBox
+ || option[i].type == PathName || option[i].type == FileName) {
+ Dimension w;
+ if(!texts[h]) continue;
+ j=0;
+ XtSetArg(args[j], XtNwidth, &w); j++;
+ XtGetValues(texts[h], args, j);
+ if(option[i].type == Spin) {
+ if(w > maxWidth) maxWidth = w;
+ widest = texts[h];
+ } else {
+ if(w > maxTextWidth) maxTextWidth = w;
+ if(!widest) widest = texts[h];
+ }
+ }
+ }
+ if(maxTextWidth + 110 < maxWidth)
+ maxTextWidth = maxWidth - 110;
+ else maxWidth = maxTextWidth + 110;
+ for(h=0; h<height || c == width-1; h++) {
+ i = h + c*height;
+ if(option[i].type == EndMark) break;
+ if(!texts[h]) continue; // Note: texts[h] can be undefined (giving errors in valgrind), but then both if's below will be false.
+ j=0;
+ if(option[i].type == Spin) {
+ XtSetArg(args[j], XtNwidth, maxWidth); j++;
+ XtSetValues(texts[h], args, j);
+ } else
+ if(option[i].type == TextBox || option[i].type == ComboBox || option[i].type == PathName || option[i].type == FileName) {
+ XtSetArg(args[j], XtNwidth, maxTextWidth); j++;
+ XtSetValues(texts[h], args, j);
+ if(bWidth != 50 && (option[i].type == FileName || option[i].type == PathName)) {
+ int tWidth = (option[i].max ? option[i].max : 205) - 5 - bWidth;
+ j = 0;
+ XtSetArg(args[j], XtNwidth, tWidth); j++;
+ XtSetValues(option[i].handle, args, j);
+ }
+ }
+ }
+ }
+
+ if(option[i].min & SAME_ROW) { // even when OK suppressed this EndMark bit can request chaining of last row to bottom
+ for(j=i-1; option[j+1].min & SAME_ROW; j--) {
+ XtSetArg(args[0], XtNtop, XtChainBottom);
+ XtSetArg(args[1], XtNbottom, XtChainBottom);
+ XtSetValues(option[j].handle, args, 2);
+ }
+ if((option[j].type == TextBox || option[j].type == ListBox) && option[j].name[0] == NULLCHAR) {
+ Widget w = option[j].handle;
+ if(option[j].type == ListBox) w = XtParent(w); // for listbox we must chain viewport
+ XtSetArg(args[0], XtNbottom, XtChainBottom);
+ XtSetValues(w, args, 1);
+ }
+ lastrow = forelast;
+ } else shrink = FALSE, lastrow = last, last = widest ? widest : dialog;
+ j = SetPositionAndSize(args, last, anchor ? anchor : lastrow, 3 /* border */,
+ 0 /* w */, shrink ? textHeight : 0 /* h */, 0x37 /* chain: right, bottom and use both neighbors */);
+
+ if(!(option[i].min & NO_OK)) {
+ option[i].handle = b_ok = XtCreateManagedWidget(_("OK"), commandWidgetClass, form, args, j);
+ XtAddCallback(b_ok, XtNcallback, GenericCallback, (XtPointer)(intptr_t) (30001 + (dlgNr<<16)));
+ if(!(option[i].min & NO_CANCEL)) {
+ XtSetArg(args[1], XtNfromHoriz, b_ok); // overwrites!
+ b_cancel = XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
+ XtAddCallback(b_cancel, XtNcallback, GenericCallback, (XtPointer)(intptr_t) (30000 + (dlgNr<<16)));
+ }
+ }
+
+ XtRealizeWidget(popup);
+ if(dlgNr != BoardWindow) { // assign close button, and position w.r.t. pointer, if not main window
+ XSetWMProtocols(xDisplay, XtWindow(popup), &wm_delete_window, 1);
+ snprintf(def, MSG_SIZ, "<Message>WM_PROTOCOLS: GenericPopDown(\"%d\") \n", dlgNr);
+ XtAugmentTranslations(popup, XtParseTranslationTable(def));
+ XQueryPointer(xDisplay, xBoardWindow, &root, &child,
+ &x, &y, &win_x, &win_y, &mask);
+
+ XtSetArg(args[0], XtNx, x - 10);
+ XtSetArg(args[1], XtNy, y - 30);
+ XtSetValues(popup, args, 2);
+ }
+ XtPopup(popup, modal ? XtGrabExclusive : XtGrabNone);
+ shellUp[dlgNr]++; // count rather than flag
+ previous = NULL;
+ if(textField) SetFocus(textField, popup, (XEvent*) NULL, False);
+ if(dlgNr && wp[dlgNr] && wp[dlgNr]->width > 0) { // if persistent window-info available, reposition
+ j = 0;
+ XtSetArg(args[j], XtNheight, (Dimension) (wp[dlgNr]->height)); j++;
+ XtSetArg(args[j], XtNwidth, (Dimension) (wp[dlgNr]->width)); j++;
+ XtSetArg(args[j], XtNx, (Position) (wp[dlgNr]->x)); j++;
+ XtSetArg(args[j], XtNy, (Position) (wp[dlgNr]->y)); j++;
+ XtSetValues(popup, args, j);
+ }
+ RaiseWindow(dlgNr);
+ return 1; // tells caller he must do initialization (e.g. add specific event handlers)
+}
+
+
+/* function called when the data to Paste is ready */
+static void
+SendTextCB (Widget w, XtPointer client_data, Atom *selection,
+ Atom *type, XtPointer value, unsigned long *len, int *format)
+{
+ char buf[MSG_SIZ], *p = (char*) textOptions[(int)(intptr_t) client_data].choice, *name = (char*) value, *q;
+ if (value==NULL || *len==0) return; /* nothing selected, abort */
+ name[*len]='\0';
+ strncpy(buf, p, MSG_SIZ);
+ q = strstr(p, "$name");
+ snprintf(buf + (q-p), MSG_SIZ -(q-p), "%s%s", name, q+5);
+ SendString(buf);
+ XtFree(value);
+}
+
+void
+SendText (int n)
+{
+ char *p = (char*) textOptions[n].choice;
+ if(strstr(p, "$name")) {
+ XtGetSelectionValue(menuBarWidget,
+ XA_PRIMARY, XA_STRING,
+ /* (XtSelectionCallbackProc) */ SendTextCB,
+ (XtPointer) (intptr_t) n, /* client_data passed to PastePositionCB */
+ CurrentTime
+ );
+ } else SendString(p);
+}
+
+void
+SetInsertPos (Option *opt, int pos)
+{
+ Arg args[16];
+ XtSetArg(args[0], XtNinsertPosition, pos);
+ XtSetValues(opt->handle, args, 1);
+// SetFocus(opt->handle, shells[InputBoxDlg], NULL, False); // No idea why this does not work, and the following is needed:
+// XSetInputFocus(xDisplay, XtWindow(opt->handle), RevertToPointerRoot, CurrentTime);
+}
+
+void
+TypeInProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{ // can be used as handler for any text edit in any dialog (from GenericPopUp, that is)
+ int n = prms[0][0] - '0';
+ Widget sh = XtParent(XtParent(XtParent(w))); // popup shell
+
+ if(n<2) { // Enter or Esc typed from primed text widget: treat as if dialog OK or cancel button hit.
+ int dlgNr; // figure out what the dialog number is by comparing shells (because we must pass it :( )
+ for(dlgNr=0; dlgNr<NrOfDialogs; dlgNr++) if(shellUp[dlgNr] && shells[dlgNr] == sh)
+ GenericCallback (w, (XtPointer)(intptr_t) (30000 + n + (dlgNr<<16)), NULL);
+ }
+}
+
+void
+HardSetFocus (Option *opt)
+{
+ XSetInputFocus(xDisplay, XtWindow(opt->handle), RevertToPointerRoot, CurrentTime);
+}
+
+