From: H.G. Muller Date: Thu, 25 Oct 2012 07:20:03 +0000 (+0200) Subject: Preserve copies of the X11 front-end in xboard directory X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=e46da46036e2db1411712cdadecd471a7acb8d3d;p=xboard.git Preserve copies of the X11 front-end in xboard directory The X11 front-end has been moved to the xaw directory, but copies are retained for modifying them to gtk versions. --- diff --git a/xboard.c b/xboard.c new file mode 100644 index 0000000..69079be --- /dev/null +++ b/xboard.c @@ -0,0 +1,2556 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !OMIT_SOCKETS +# if HAVE_SYS_SOCKET_H +# include +# include +# include +# else /* not HAVE_SYS_SOCKET_H */ +# if HAVE_LAN_SOCKET_H +# include +# include +# include +# 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 +# include +#else /* not STDC_HEADERS */ +extern char *getenv(); +# if HAVE_STRING_H +# include +# else /* not HAVE_STRING_H */ +# include +# endif /* not HAVE_STRING_H */ +#endif /* not STDC_HEADERS */ + +#if HAVE_SYS_FCNTL_H +# include +#else /* not HAVE_SYS_FCNTL_H */ +# if HAVE_FCNTL_H +# include +# endif /* HAVE_FCNTL_H */ +#endif /* not HAVE_SYS_FCNTL_H */ + +#if HAVE_SYS_SYSTEMINFO_H +# include +#endif /* HAVE_SYS_SYSTEMINFO_H */ + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_UNISTD_H +# include +#endif + +#if HAVE_SYS_WAIT_H +# include +#endif + +#if HAVE_DIRENT_H +# include +# 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 +# define HAVE_DIR_STRUCT +# endif +# if HAVE_SYS_DIR_H +# include +# define HAVE_DIR_STRUCT +# endif +# if HAVE_NDIR_H +# include +# define HAVE_DIR_STRUCT +# endif +#endif + +#if ENABLE_NLS +#include +#endif + +#include +#include +#include +#include +#include +#include +#if USE_XAW3D +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +// [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are. +#include "common.h" + +#if HAVE_LIBXPM +#include +#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[] = + ":F9: MenuItem(Actions.Resign) \n \ + :Ctrln: MenuItem(File.NewGame) \n \ + :MetaV: MenuItem(File.NewVariant) \n \ + :Ctrlo: MenuItem(File.LoadGame) \n \ + :MetaNext: MenuItem(LoadNextGameProc) \n \ + :MetaPrior: MenuItem(LoadPrevGameProc) \n \ + :CtrlDown: LoadSelectedProc(3) \n \ + :CtrlUp: LoadSelectedProc(-3) \n \ + :Ctrls: MenuItem(File.SaveGame) \n \ + :Ctrlc: MenuItem(Edit.CopyGame) \n \ + :Ctrlv: MenuItem(Edit.PasteGame) \n \ + :CtrlO: MenuItem(File.LoadPosition) \n \ + :ShiftNext: MenuItem(LoadNextPositionProc) \n \ + :ShiftPrior: MenuItem(LoadPrevPositionProc) \n \ + :CtrlS: MenuItem(File.SavePosition) \n \ + :CtrlC: MenuItem(Edit.CopyPosition) \n \ + :CtrlV: MenuItem(Edit.PastePosition) \n \ + :Ctrlq: MenuItem(File.Quit) \n \ + :Ctrlw: MenuItem(Mode.MachineWhite) \n \ + :Ctrlb: MenuItem(Mode.MachineBlack) \n \ + :Ctrlt: MenuItem(Mode.TwoMachines) \n \ + :Ctrla: MenuItem(Mode.AnalysisMode) \n \ + :Ctrlg: MenuItem(Mode.AnalyzeFile) \n \ + :Ctrle: MenuItem(Mode.EditGame) \n \ + :CtrlE: MenuItem(Mode.EditPosition) \n \ + :MetaO: MenuItem(View.EngineOutput) \n \ + :MetaE: MenuItem(View.EvaluationGraph) \n \ + :MetaG: MenuItem(View.GameList) \n \ + :MetaH: MenuItem(View.MoveHistory) \n \ + :Pause: MenuItem(Mode.Pause) \n \ + :F3: MenuItem(Action.Accept) \n \ + :F4: MenuItem(Action.Decline) \n \ + :F12: MenuItem(Action.Rematch) \n \ + :F5: MenuItem(Action.CallFlag) \n \ + :F6: MenuItem(Action.Draw) \n \ + :F7: MenuItem(Action.Adjourn) \n \ + :F8: MenuItem(Action.Abort) \n \ + :F10: MenuItem(Action.StopObserving) \n \ + :F11: MenuItem(Action.StopExamining) \n \ + :Ctrld: MenuItem(DebugProc) \n \ + :Meta CtrlF12: MenuItem(DebugProc) \n \ + :MetaEnd: MenuItem(Edit.ForwardtoEnd) \n \ + :MetaRight: MenuItem(Edit.Forward) \n \ + :MetaHome: MenuItem(Edit.BacktoStart) \n \ + :MetaLeft: MenuItem(Edit.Backward) \n \ + :Left: MenuItem(Edit.Backward) \n \ + :Right: MenuItem(Edit.Forward) \n \ + :Home: MenuItem(Edit.Revert) \n \ + :End: MenuItem(Edit.TruncateGame) \n \ + :Ctrlm: MenuItem(Engine.MoveNow) \n \ + :Ctrlx: MenuItem(Engine.RetractMove) \n \ + :MetaJ: MenuItem(Options.Adjudications) \n \ + :MetaU: MenuItem(Options.CommonEngine) \n \ + :MetaT: MenuItem(Options.TimeControl) \n \ + :CtrlP: MenuItem(PonderNextMove) \n " +#ifndef OPTIONSDIALOG + "\ + :CtrlQ: MenuItem(AlwaysQueenProc) \n \ + :CtrlF: MenuItem(AutoflagProc) \n \ + :CtrlA: MenuItem(AnimateMovingProc) \n \ + :CtrlL: MenuItem(TestLegalityProc) \n \ + :CtrlH: MenuItem(HideThinkingProc) \n " +#endif + "\ + :F1: MenuItem(Help.ManXBoard) \n \ + :F2: MenuItem(View.FlipView) \n \ + :Return: TempBackwardProc() \n \ + :Return: TempForwardProc() \n"; + +char ICSInputTranslations[] = + "Up: UpKeyProc() \n " + "Down: DownKeyProc() \n " + "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[] = ": extend-end() select-start() CommentClick() \n"; + +String xboardResources[] = { + "*Error*translations: #override\\n 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; id_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; iWM_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 && sizeargLoc; + 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; iargName, 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; imax_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 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 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; +} + diff --git a/xboard.h b/xboard.h new file mode 100644 index 0000000..5a6587e --- /dev/null +++ b/xboard.h @@ -0,0 +1,176 @@ +/* + * 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 + +#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 */ + diff --git a/xedittags.c b/xedittags.c new file mode 100644 index 0000000..d27aca3 --- /dev/null +++ b/xedittags.c @@ -0,0 +1,121 @@ +/* + * 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 +#include +#include +#include + +#if STDC_HEADERS +# include +# include +#else /* not STDC_HEADERS */ +extern char *getenv(); +# if HAVE_STRING_H +# include +# else /* not HAVE_STRING_H */ +# include +# endif /* not HAVE_STRING_H */ +#endif /* not STDC_HEADERS */ + +#if HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#if USE_XAW3D +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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(); +} diff --git a/xedittags.h b/xedittags.h new file mode 100644 index 0000000..6245bfd --- /dev/null +++ b/xedittags.h @@ -0,0 +1,30 @@ +/* + * 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 diff --git a/xengineoutput.c b/xengineoutput.c new file mode 100644 index 0000000..78269b8 --- /dev/null +++ b/xengineoutput.c @@ -0,0 +1,286 @@ +/* + * 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 +#include +#include +#include + +#if STDC_HEADERS +# include +# include +#else /* not STDC_HEADERS */ +extern char *getenv(); +# if HAVE_STRING_H +# include +# else /* not HAVE_STRING_H */ +# include +# endif /* not HAVE_STRING_H */ +#endif /* not STDC_HEADERS */ + +#if HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +// [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[] = +":Ctrlc: CopyMemoProc() \n \ +: HandlePV() \n \ +Shift: select-start() extend-end() SelectPV(1) \n \ +Any: select-start() extend-end() SelectPV(0) \n \ +: 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); + } +} + diff --git a/xevalgraph.c b/xevalgraph.c new file mode 100644 index 0000000..f3cac1f --- /dev/null +++ b/xevalgraph.c @@ -0,0 +1,339 @@ +/* + * 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 +#include +#include +#include + +#if STDC_HEADERS +# include +# include +#else /* not STDC_HEADERS */ +extern char *getenv(); +# if HAVE_STRING_H +# include +# else /* not HAVE_STRING_H */ +# include +# endif /* not HAVE_STRING_H */ +#endif /* not STDC_HEADERS */ + +#if HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 + +#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(); + } +} + diff --git a/xevalgraph.h b/xevalgraph.h new file mode 100644 index 0000000..e1227c6 --- /dev/null +++ b/xevalgraph.h @@ -0,0 +1,31 @@ +/* + * 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 diff --git a/xgamelist.c b/xgamelist.c new file mode 100644 index 0000000..50367b1 --- /dev/null +++ b/xgamelist.c @@ -0,0 +1,118 @@ +/* + * 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 +#include +#include +#include + +#if STDC_HEADERS +# include +# include +#else /* not STDC_HEADERS */ +extern char *getenv(); +# if HAVE_STRING_H +# include +# else /* not HAVE_STRING_H */ +# include +# endif /* not HAVE_STRING_H */ +#endif /* not STDC_HEADERS */ + +#if HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#if USE_XAW3D +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "common.h" +#include "backend.h" +#include "xboard.h" +#include "xgamelist.h" +#include "dialogs.h" + + +char gameListTranslations[] = + ": WheelProc(-3) \n \ + : WheelProc(3) \n \ + : LoadSelectedProc(100) Set() \n \ + (2): LoadSelectedProc(0) \n \ + Home: LoadSelectedProc(-2) \n \ + End: LoadSelectedProc(2) \n \ + CtrlUp: LoadSelectedProc(-3) \n \ + CtrlDown: LoadSelectedProc(3) \n \ + Up: LoadSelectedProc(-1) \n \ + Down: LoadSelectedProc(1) \n \ + Left: LoadSelectedProc(-1) \n \ + Right: LoadSelectedProc(1) \n \ + Prior: LoadSelectedProc(-4) \n \ + Next: LoadSelectedProc(4) \n \ + Return: LoadSelectedProc(0) \n"; +char filterTranslations[] = + "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(); +} + diff --git a/xgamelist.h b/xgamelist.h new file mode 100644 index 0000000..fa7b41a --- /dev/null +++ b/xgamelist.h @@ -0,0 +1,31 @@ +/* + * 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 */ diff --git a/xhistory.c b/xhistory.c new file mode 100644 index 0000000..4277f82 --- /dev/null +++ b/xhistory.c @@ -0,0 +1,169 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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[] = +": select-start() \n \ +: 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); +} diff --git a/xhistory.h b/xhistory.h new file mode 100644 index 0000000..90f0c9b --- /dev/null +++ b/xhistory.h @@ -0,0 +1,29 @@ +/* + * 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 */ diff --git a/xoptions.c b/xoptions.c new file mode 100644 index 0000000..a475c58 --- /dev/null +++ b/xoptions.c @@ -0,0 +1,1216 @@ +/* + * 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 +#include +#include +#include + +#if STDC_HEADERS +# include +# include +#else /* not STDC_HEADERS */ +extern char *getenv(); +# if HAVE_STRING_H +# include +# else /* not HAVE_STRING_H */ +# include +# endif /* not HAVE_STRING_H */ +#endif /* not STDC_HEADERS */ + +#if HAVE_UNISTD_H +# include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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[] = + "Return: TypeInProc(1) \n" + "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 = + "Return: redraw-display() \n \ + Tab: TabProc() \n "; +static char scrollTranslations[] = + "(2): WheelProc(0 0 A) \n \ + : WheelProc(-1) \n \ + : 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; imin = wtot; + if(width <= 0) return; + while(wtot > width) { + int wmax=0, imax=0; + for(i=1; i wmax) wmax = widths[imax=i]; + widths[imax]--; + wtot--; + } + for(i=1; imin = 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 and + + 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 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; hWM_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; dlgNrhandle), RevertToPointerRoot, CurrentTime); +} + +