2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
69 # if HAVE_SYS_SOCKET_H
70 # include <sys/socket.h>
71 # include <netinet/in.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 # if HAVE_LAN_SOCKET_H
75 # include <lan/socket.h>
77 # include <lan/netdb.h>
78 # else /* not HAVE_LAN_SOCKET_H */
79 # define OMIT_SOCKETS 1
80 # endif /* not HAVE_LAN_SOCKET_H */
81 # endif /* not HAVE_SYS_SOCKET_H */
82 #endif /* !OMIT_SOCKETS */
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
91 # else /* not HAVE_STRING_H */
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
113 # include <sys/time.h>
124 # include <sys/wait.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
135 # include <sys/ndir.h>
136 # define HAVE_DIR_STRUCT
139 # include <sys/dir.h>
140 # define HAVE_DIR_STRUCT
144 # define HAVE_DIR_STRUCT
152 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
155 #include "frontend.h"
157 #include "backendz.h"
162 #include "xgamelist.h"
163 #include "xhistory.h"
167 #include "engineoutput.h"
177 #define usleep(t) _sleep2(((t)+500)/1000)
181 # define _(s) gettext (s)
182 # define N_(s) gettext_noop (s)
188 int main P((int argc, char **argv));
189 RETSIGTYPE CmailSigHandler P((int sig));
190 RETSIGTYPE IntSigHandler P((int sig));
191 RETSIGTYPE TermSizeSigHandler P((int sig));
193 char *InsertPxlSize P((char *pattern, int targetPxlSize));
194 XFontSet CreateFontSet P((char *base_fnt_lst));
196 char *FindFont P((char *pattern, int targetPxlSize));
198 void DelayedDrag P((void));
199 void ICSInputBoxPopUp P((void));
201 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
202 void HandlePV P((Widget w, XEvent * event,
203 String * params, Cardinal * nParams));
204 void DrawPositionProc P((Widget w, XEvent *event,
205 String *prms, Cardinal *nprms));
206 void CommentClick P((Widget w, XEvent * event,
207 String * params, Cardinal * nParams));
208 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
209 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
210 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
211 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
212 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
213 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
214 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
215 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
216 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
217 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
219 Boolean TempBackwardActive = False;
220 void DisplayMove P((int moveNumber));
221 void ICSInitScript P((void));
222 void update_ics_width P(());
223 int CopyMemoProc P(());
227 * XBoard depends on Xt R4 or higher
229 int xtVersion = XtSpecificationRelease;
234 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
235 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
236 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
238 XFontSet fontSet, clockFontSet;
241 XFontStruct *clockFontStruct;
243 Font coordFontID, countFontID;
244 XFontStruct *coordFontStruct, *countFontStruct;
245 XtAppContext appContext;
247 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
249 GtkWidget *mainwindow;
251 Option *optList; // contains all widgets of main window
254 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
256 typedef unsigned int BoardSize;
258 Boolean chessProgram;
260 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
261 int smallLayout = 0, tinyLayout = 0,
262 marginW, marginH, // [HGM] for run-time resizing
263 fromX = -1, fromY = -1, toX, toY, commentUp = False,
264 errorExitStatus = -1, defaultLineGap;
266 Dimension textHeight;
267 Pixel timerForegroundPixel, timerBackgroundPixel;
268 Pixel buttonForegroundPixel, buttonBackgroundPixel;
270 char *chessDir, *programName, *programVersion;
271 Boolean alwaysOnTop = False;
272 char *icsTextMenuString;
274 char *firstChessProgramNames;
275 char *secondChessProgramNames;
277 WindowPlacement wpMain;
278 WindowPlacement wpConsole;
279 WindowPlacement wpComment;
280 WindowPlacement wpMoveHistory;
281 WindowPlacement wpEvalGraph;
282 WindowPlacement wpEngineOutput;
283 WindowPlacement wpGameList;
284 WindowPlacement wpTags;
286 /* This magic number is the number of intermediate frames used
287 in each half of the animation. For short moves it's reduced
288 by 1. The total number of frames will be factor * 2 + 1. */
291 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
298 DropMenuEnables dmEnables[] = {
316 XtResource clientResources[] = {
317 { "flashCount", "flashCount", XtRInt, sizeof(int),
318 XtOffset(AppDataPtr, flashCount), XtRImmediate,
319 (XtPointer) FLASH_COUNT },
322 XrmOptionDescRec shellOptions[] = {
323 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
324 { "-flash", "flashCount", XrmoptionNoArg, "3" },
325 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
328 XtActionsRec boardActions[] = {
329 { "DrawPosition", DrawPositionProc },
330 { "HandlePV", HandlePV },
331 { "SelectPV", SelectPV },
332 { "StopPV", StopPV },
333 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
334 { "QuitProc", QuitWrapper },
335 { "ManProc", ManInner },
336 { "TempBackwardProc", TempBackwardProc },
337 { "TempForwardProc", TempForwardProc },
338 { "CommentClick", (XtActionProc) CommentClick },
339 { "GenericPopDown", (XtActionProc) GenericPopDown },
340 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
341 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
342 { "SelectMove", (XtActionProc) SelectMove },
343 { "LoadSelectedProc", LoadSelectedProc },
344 { "SetFilterProc", SetFilterProc },
345 { "TypeInProc", TypeInProc },
346 { "EnterKeyProc", EnterKeyProc },
347 { "UpKeyProc", UpKeyProc },
348 { "DownKeyProc", DownKeyProc },
349 { "WheelProc", WheelProc },
350 { "TabProc", TabProc },
354 char globalTranslations[] =
355 ":<Key>F9: MenuItem(Actions.Resign) \n \
356 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
357 :Meta<Key>V: MenuItem(File.NewVariant) \n \
358 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
359 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
360 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
361 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
362 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
363 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
364 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
365 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
366 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
367 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
368 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
369 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
370 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
371 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
372 :Ctrl<Key>q: MenuItem(File.Quit) \n \
373 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
374 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
375 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
376 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
377 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
378 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
379 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
380 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
381 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
382 :Meta<Key>G: MenuItem(View.GameList) \n \
383 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
384 :<Key>Pause: MenuItem(Mode.Pause) \n \
385 :<Key>F3: MenuItem(Action.Accept) \n \
386 :<Key>F4: MenuItem(Action.Decline) \n \
387 :<Key>F12: MenuItem(Action.Rematch) \n \
388 :<Key>F5: MenuItem(Action.CallFlag) \n \
389 :<Key>F6: MenuItem(Action.Draw) \n \
390 :<Key>F7: MenuItem(Action.Adjourn) \n \
391 :<Key>F8: MenuItem(Action.Abort) \n \
392 :<Key>F10: MenuItem(Action.StopObserving) \n \
393 :<Key>F11: MenuItem(Action.StopExamining) \n \
394 :Ctrl<Key>d: MenuItem(DebugProc) \n \
395 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
396 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
397 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
398 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
399 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
400 :<Key>Left: MenuItem(Edit.Backward) \n \
401 :<Key>Right: MenuItem(Edit.Forward) \n \
402 :<Key>Home: MenuItem(Edit.Revert) \n \
403 :<Key>End: MenuItem(Edit.TruncateGame) \n \
404 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
405 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
406 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
407 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
408 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
409 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
410 #ifndef OPTIONSDIALOG
412 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
413 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
414 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
415 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
416 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
419 :<Key>F1: MenuItem(Help.ManXBoard) \n \
420 :<Key>F2: MenuItem(View.FlipView) \n \
421 :<KeyDown>Return: TempBackwardProc() \n \
422 :<KeyUp>Return: TempForwardProc() \n";
424 char ICSInputTranslations[] =
425 "<Key>Up: UpKeyProc() \n "
426 "<Key>Down: DownKeyProc() \n "
427 "<Key>Return: EnterKeyProc() \n";
429 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
430 // as the widget is destroyed before the up-click can call extend-end
431 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
434 String xboardResources[] = {
435 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
440 /* Max possible square size */
441 #define MAXSQSIZE 256
443 static int xpm_avail[MAXSQSIZE];
445 #ifdef HAVE_DIR_STRUCT
447 /* Extract piece size from filename */
449 xpm_getsize (char *name, int len, char *ext)
457 if ((p=strchr(name, '.')) == NULL ||
458 StrCaseCmp(p+1, ext) != 0)
464 while (*p && isdigit(*p))
471 /* Setup xpm_avail */
473 xpm_getavail (char *dirname, char *ext)
479 for (i=0; i<MAXSQSIZE; ++i)
482 if (appData.debugMode)
483 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
485 dir = opendir(dirname);
488 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
489 programName, dirname);
493 while ((ent=readdir(dir)) != NULL) {
494 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
495 if (i > 0 && i < MAXSQSIZE)
505 xpm_print_avail (FILE *fp, char *ext)
509 fprintf(fp, _("Available `%s' sizes:\n"), ext);
510 for (i=1; i<MAXSQSIZE; ++i) {
516 /* Return XPM piecesize closest to size */
518 xpm_closest_to (char *dirname, int size, char *ext)
521 int sm_diff = MAXSQSIZE;
525 xpm_getavail(dirname, ext);
527 if (appData.debugMode)
528 xpm_print_avail(stderr, ext);
530 for (i=1; i<MAXSQSIZE; ++i) {
533 diff = (diff<0) ? -diff : diff;
534 if (diff < sm_diff) {
542 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
548 #else /* !HAVE_DIR_STRUCT */
549 /* If we are on a system without a DIR struct, we can't
550 read the directory, so we can't collect a list of
551 filenames, etc., so we can't do any size-fitting. */
553 xpm_closest_to (char *dirname, int size, char *ext)
556 Warning: No DIR structure found on this system --\n\
557 Unable to autosize for XPM/XIM pieces.\n\
558 Please report this error to %s.\n\
559 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
562 #endif /* HAVE_DIR_STRUCT */
566 /* Arrange to catch delete-window events */
567 Atom wm_delete_window;
569 CatchDeleteWindow (Widget w, String procname)
572 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
573 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
574 XtAugmentTranslations(w, XtParseTranslationTable(buf));
581 gtk_window_present(GTK_WINDOW(mainwindow));
584 //---------------------------------------------------------------------------------------------------------
585 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
588 #define CW_USEDEFAULT (1<<31)
589 #define ICS_TEXT_MENU_SIZE 90
590 #define DEBUG_FILE "xboard.debug"
591 #define SetCurrentDirectory chdir
592 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
596 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
599 // front-end part of option handling
601 // [HGM] This platform-dependent table provides the location for storing the color info
602 extern char *crWhite, * crBlack;
606 &appData.whitePieceColor,
607 &appData.blackPieceColor,
608 &appData.lightSquareColor,
609 &appData.darkSquareColor,
610 &appData.highlightSquareColor,
611 &appData.premoveHighlightColor,
612 &appData.lowTimeWarningColor,
623 // [HGM] font: keep a font for each square size, even non-stndard ones
626 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
627 char *fontTable[NUM_FONTS][MAX_SIZE];
630 ParseFont (char *name, int number)
631 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
633 if(sscanf(name, "size%d:", &size)) {
634 // [HGM] font: font is meant for specific boardSize (likely from settings file);
635 // defer processing it until we know if it matches our board size
636 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
637 fontTable[number][size] = strdup(strchr(name, ':')+1);
638 fontValid[number][size] = True;
643 case 0: // CLOCK_FONT
644 appData.clockFont = strdup(name);
646 case 1: // MESSAGE_FONT
647 appData.font = strdup(name);
649 case 2: // COORD_FONT
650 appData.coordFont = strdup(name);
655 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
660 { // only 2 fonts currently
661 appData.clockFont = CLOCK_FONT_NAME;
662 appData.coordFont = COORD_FONT_NAME;
663 appData.font = DEFAULT_FONT_NAME;
668 { // no-op, until we identify the code for this already in XBoard and move it here
672 ParseColor (int n, char *name)
673 { // in XBoard, just copy the color-name string
674 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
678 ParseTextAttribs (ColorClass cc, char *s)
680 (&appData.colorShout)[cc] = strdup(s);
684 ParseBoardSize (void *addr, char *name)
686 appData.boardSize = strdup(name);
691 { // In XBoard the sound-playing program takes care of obtaining the actual sound
695 SetCommPortDefaults ()
696 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
699 // [HGM] args: these three cases taken out to stay in front-end
701 SaveFontArg (FILE *f, ArgDescriptor *ad)
704 int i, n = (int)(intptr_t)ad->argLoc;
706 case 0: // CLOCK_FONT
707 name = appData.clockFont;
709 case 1: // MESSAGE_FONT
712 case 2: // COORD_FONT
713 name = appData.coordFont;
718 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
719 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
720 fontTable[n][squareSize] = strdup(name);
721 fontValid[n][squareSize] = True;
724 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
725 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
730 { // nothing to do, as the sounds are at all times represented by their text-string names already
734 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
735 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
736 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
740 SaveColor (FILE *f, ArgDescriptor *ad)
741 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
742 if(colorVariable[(int)(intptr_t)ad->argLoc])
743 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
747 SaveBoardSize (FILE *f, char *name, void *addr)
748 { // wrapper to shield back-end from BoardSize & sizeInfo
749 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
753 ParseCommPortSettings (char *s)
754 { // no such option in XBoard (yet)
761 GetActualPlacement (Widget wg, WindowPlacement *wp)
763 XWindowAttributes winAt;
770 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
771 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
772 wp->x = rx - winAt.x;
773 wp->y = ry - winAt.y;
774 wp->height = winAt.height;
775 wp->width = winAt.width;
776 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
782 { // wrapper to shield use of window handles from back-end (make addressible by number?)
783 // In XBoard this will have to wait until awareness of window parameters is implemented
785 GetActualPlacement(shellWidget, &wpMain);
786 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
787 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
788 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
789 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
790 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
791 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
796 PrintCommPortSettings (FILE *f, char *name)
797 { // This option does not exist in XBoard
801 EnsureOnScreen (int *x, int *y, int minX, int minY)
808 { // [HGM] args: allows testing if main window is realized from back-end
810 return xBoardWindow != 0;
817 PopUpStartupDialog ()
818 { // start menu not implemented in XBoard
822 ConvertToLine (int argc, char **argv)
824 static char line[128*1024], buf[1024];
828 for(i=1; i<argc; i++)
830 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
831 && argv[i][0] != '{' )
832 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
834 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
835 strncat(line, buf, 128*1024 - strlen(line) - 1 );
838 line[strlen(line)-1] = NULLCHAR;
842 //--------------------------------------------------------------------------------------------
845 ResizeBoardWindow (int w, int h, int inhibit)
848 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
850 shellArgs[0].value = w;
851 shellArgs[1].value = h;
852 shellArgs[4].value = shellArgs[2].value = w;
853 shellArgs[5].value = shellArgs[3].value = h;
854 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
856 XSync(xDisplay, False);
862 MakeOneColor (char *name, Pixel *color)
865 if (!appData.monoMode) {
866 vFrom.addr = (caddr_t) name;
867 vFrom.size = strlen(name);
868 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
869 if (vTo.addr == NULL) {
870 appData.monoMode = True;
873 *color = *(Pixel *) vTo.addr;
882 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
883 int forceMono = False;
886 if (appData.lowTimeWarning)
887 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
888 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
889 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
896 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
897 { // detervtomine what fonts to use, and create them
902 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
903 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
904 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
905 appData.font = fontTable[MESSAGE_FONT][squareSize];
906 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
907 appData.coordFont = fontTable[COORD_FONT][squareSize];
910 appData.font = InsertPxlSize(appData.font, fontPxlSize);
911 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
912 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
913 fontSet = CreateFontSet(appData.font);
914 clockFontSet = CreateFontSet(appData.clockFont);
916 /* For the coordFont, use the 0th font of the fontset. */
917 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
918 XFontStruct **font_struct_list;
919 XFontSetExtents *fontSize;
920 char **font_name_list;
921 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
922 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
923 coordFontStruct = XQueryFont(xDisplay, coordFontID);
924 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
925 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
928 appData.font = FindFont(appData.font, fontPxlSize);
929 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
930 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
931 clockFontID = XLoadFont(xDisplay, appData.clockFont);
932 clockFontStruct = XQueryFont(xDisplay, clockFontID);
933 coordFontID = XLoadFont(xDisplay, appData.coordFont);
934 coordFontStruct = XQueryFont(xDisplay, coordFontID);
935 // textHeight in !NLS mode!
937 countFontID = coordFontID; // [HGM] holdings
938 countFontStruct = coordFontStruct;
940 xdb = XtDatabase(xDisplay);
942 XrmPutLineResource(&xdb, "*international: True");
943 vTo.size = sizeof(XFontSet);
944 vTo.addr = (XtPointer) &fontSet;
945 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
947 XrmPutStringResource(&xdb, "*font", appData.font);
958 case ArgInt: p = " N"; break;
959 case ArgString: p = " STR"; break;
960 case ArgBoolean: p = " TF"; break;
961 case ArgSettingsFilename:
962 case ArgFilename: p = " FILE"; break;
963 case ArgX: p = " Nx"; break;
964 case ArgY: p = " Ny"; break;
965 case ArgAttribs: p = " TEXTCOL"; break;
966 case ArgColor: p = " COL"; break;
967 case ArgFont: p = " FONT"; break;
968 case ArgBoardSize: p = " SIZE"; break;
969 case ArgFloat: p = " FLOAT"; break;
974 case ArgCommSettings:
985 ArgDescriptor *q, *p = argDescriptors+5;
986 printf("\nXBoard accepts the following options:\n"
987 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
988 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
989 " SIZE = board-size spec(s)\n"
990 " Within parentheses are short forms, or options to set to true or false.\n"
991 " Persistent options (saved in the settings file) are marked with *)\n\n");
993 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
994 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
995 if(p->save) strcat(buf+len, "*");
996 for(q=p+1; q->argLoc == p->argLoc; q++) {
997 if(q->argName[0] == '-') continue;
998 strcat(buf+len, q == p+1 ? " (" : " ");
999 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1001 if(q != p+1) strcat(buf+len, ")");
1003 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1006 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1010 main (int argc, char **argv)
1012 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1014 XSetWindowAttributes window_attributes;
1016 Dimension boardWidth, boardHeight, w, h;
1019 int boardWidth, boardHeight, w, h;
1021 int forceMono = False;
1022 GError *gtkerror=NULL;
1024 srandom(time(0)); // [HGM] book: make random truly random
1026 setbuf(stdout, NULL);
1027 setbuf(stderr, NULL);
1030 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1031 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1035 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1041 gtk_init (&argc, &argv);
1043 programName = strrchr(argv[0], '/');
1044 if (programName == NULL)
1045 programName = argv[0];
1050 // if (appData.debugMode) {
1051 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1054 bindtextdomain(PACKAGE, LOCALEDIR);
1055 textdomain(PACKAGE);
1058 appData.boardSize = "";
1059 InitAppData(ConvertToLine(argc, argv));
1061 if (p == NULL) p = "/tmp";
1062 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1063 gameCopyFilename = (char*) malloc(i);
1064 gamePasteFilename = (char*) malloc(i);
1065 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1066 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1068 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1069 static char buf[MSG_SIZ];
1070 EscapeExpand(buf, appData.firstInitString);
1071 appData.firstInitString = strdup(buf);
1072 EscapeExpand(buf, appData.secondInitString);
1073 appData.secondInitString = strdup(buf);
1074 EscapeExpand(buf, appData.firstComputerString);
1075 appData.firstComputerString = strdup(buf);
1076 EscapeExpand(buf, appData.secondComputerString);
1077 appData.secondComputerString = strdup(buf);
1080 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1083 if (chdir(chessDir) != 0) {
1084 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1090 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1091 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1092 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1093 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1096 setbuf(debugFP, NULL);
1100 if (appData.debugMode) {
1101 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1105 /* [HGM,HR] make sure board size is acceptable */
1106 if(appData.NrFiles > BOARD_FILES ||
1107 appData.NrRanks > BOARD_RANKS )
1108 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1111 /* This feature does not work; animation needs a rewrite */
1112 appData.highlightDragging = FALSE;
1116 gameInfo.variant = StringToVariant(appData.variant);
1117 InitPosition(FALSE);
1121 builder = gtk_builder_new();
1122 filename = get_glade_filename ("mainboard.glade");
1123 if(! gtk_builder_add_from_file (builder, filename, >kerror) )
1126 printf ("Error: %d %s\n",gtkerror->code,gtkerror->message);
1128 mainwindow = GTK_WIDGET(gtk_builder_get_object (builder, "mainwindow"));
1131 XtAppInitialize(&appContext, "XBoard", shellOptions,
1132 XtNumber(shellOptions),
1133 &argc, argv, xboardResources, NULL, 0);
1135 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1136 clientResources, XtNumber(clientResources),
1139 xDisplay = XtDisplay(shellWidget);
1140 xScreen = DefaultScreen(xDisplay);
1141 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1145 * determine size, based on supplied or remembered -size, or screen size
1147 if (isdigit(appData.boardSize[0])) {
1148 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1149 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1150 &fontPxlSize, &smallLayout, &tinyLayout);
1152 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1153 programName, appData.boardSize);
1157 /* Find some defaults; use the nearest known size */
1158 SizeDefaults *szd, *nearest;
1159 int distance = 99999;
1160 nearest = szd = sizeDefaults;
1161 while (szd->name != NULL) {
1162 if (abs(szd->squareSize - squareSize) < distance) {
1164 distance = abs(szd->squareSize - squareSize);
1165 if (distance == 0) break;
1169 if (i < 2) lineGap = nearest->lineGap;
1170 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1171 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1172 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1173 if (i < 6) smallLayout = nearest->smallLayout;
1174 if (i < 7) tinyLayout = nearest->tinyLayout;
1177 SizeDefaults *szd = sizeDefaults;
1178 if (*appData.boardSize == NULLCHAR) {
1180 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1181 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1185 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
1186 guint screenwidth = gdk_screen_get_width(screen);
1187 guint screenheight = gdk_screen_get_height(screen);
1188 while (screenwidth < szd->minScreenSize ||
1189 screenheight < szd->minScreenSize) {
1193 if (szd->name == NULL) szd--;
1194 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1196 while (szd->name != NULL &&
1197 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1198 if (szd->name == NULL) {
1199 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1200 programName, appData.boardSize);
1204 squareSize = szd->squareSize;
1205 lineGap = szd->lineGap;
1206 clockFontPxlSize = szd->clockFontPxlSize;
1207 coordFontPxlSize = szd->coordFontPxlSize;
1208 fontPxlSize = szd->fontPxlSize;
1209 smallLayout = szd->smallLayout;
1210 tinyLayout = szd->tinyLayout;
1211 // [HGM] font: use defaults from settings file if available and not overruled
1214 defaultLineGap = lineGap;
1215 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1217 /* [HR] height treated separately (hacked) */
1218 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1219 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1222 * Determine what fonts to use.
1225 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1229 * Detect if there are not enough colors available and adapt.
1232 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1233 appData.monoMode = True;
1237 forceMono = MakeColors();
1240 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1242 appData.monoMode = True;
1245 ParseIcsTextColors();
1248 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1255 layoutName = "tinyLayout";
1256 } else if (smallLayout) {
1257 layoutName = "smallLayout";
1259 layoutName = "normalLayout";
1262 optList = BoardPopUp(squareSize, lineGap, (void*)
1272 InitDrawingHandle(optList + W_BOARD);
1273 currBoard = &optList[W_BOARD];
1274 boardWidget = optList[W_BOARD].handle;
1275 menuBarWidget = optList[W_MENU].handle;
1276 dropMenu = optList[W_DROP].handle;
1277 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1279 formWidget = XtParent(boardWidget);
1280 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1281 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1282 XtGetValues(optList[W_WHITE].handle, args, 2);
1283 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1284 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1285 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1286 XtGetValues(optList[W_PAUSE].handle, args, 2);
1291 xBoardWindow = XtWindow(boardWidget);
1294 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1295 // not need to go into InitDrawingSizes().
1303 ReadBitmap(&wIconPixmap, "icon_white.bm",
1304 icon_white_bits, icon_white_width, icon_white_height);
1305 ReadBitmap(&bIconPixmap, "icon_black.bm",
1306 icon_black_bits, icon_black_width, icon_black_height);
1307 iconPixmap = wIconPixmap;
1309 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1310 XtSetValues(shellWidget, args, i);
1314 * Create a cursor for the board widget.
1317 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1318 XChangeWindowAttributes(xDisplay, xBoardWindow,
1319 CWCursor, &window_attributes);
1323 * Inhibit shell resizing.
1326 shellArgs[0].value = (XtArgVal) &w;
1327 shellArgs[1].value = (XtArgVal) &h;
1328 XtGetValues(shellWidget, shellArgs, 2);
1329 shellArgs[4].value = shellArgs[2].value = w;
1330 shellArgs[5].value = shellArgs[3].value = h;
1331 // XtSetValues(shellWidget, &shellArgs[2], 4);
1333 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1334 marginH = h - boardHeight;
1337 CatchDeleteWindow(shellWidget, "QuitProc");
1343 if(appData.logoSize)
1344 { // locate and read user logo
1346 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1347 ASSIGN(userLogo, buf);
1350 if (appData.animate || appData.animateDragging)
1354 XtAugmentTranslations(formWidget,
1355 XtParseTranslationTable(globalTranslations));
1357 XtAddEventHandler(formWidget, KeyPressMask, False,
1358 (XtEventHandler) MoveTypeInProc, NULL);
1359 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1360 (XtEventHandler) EventProc, NULL);
1363 /* [AS] Restore layout */
1364 if( wpMoveHistory.visible ) {
1368 if( wpEvalGraph.visible )
1373 if( wpEngineOutput.visible ) {
1374 EngineOutputPopUp();
1379 if (errorExitStatus == -1) {
1380 if (appData.icsActive) {
1381 /* We now wait until we see "login:" from the ICS before
1382 sending the logon script (problems with timestamp otherwise) */
1383 /*ICSInitScript();*/
1384 if (appData.icsInputBox) ICSInputBoxPopUp();
1388 signal(SIGWINCH, TermSizeSigHandler);
1390 signal(SIGINT, IntSigHandler);
1391 signal(SIGTERM, IntSigHandler);
1392 if (*appData.cmailGameName != NULLCHAR) {
1393 signal(SIGUSR1, CmailSigHandler);
1397 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1400 // XtSetKeyboardFocus(shellWidget, formWidget);
1402 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1405 /* check for GTK events and process them */
1408 gtk_main_iteration();
1411 if (appData.debugMode) fclose(debugFP); // [DM] debug
1416 TermSizeSigHandler (int sig)
1422 IntSigHandler (int sig)
1428 CmailSigHandler (int sig)
1433 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1435 /* Activate call-back function CmailSigHandlerCallBack() */
1436 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1438 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1442 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1445 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1447 /**** end signal code ****/
1450 #define Abs(n) ((n)<0 ? -(n) : (n))
1454 InsertPxlSize (char *pattern, int targetPxlSize)
1456 char *base_fnt_lst, strInt[12], *p, *q;
1457 int alternatives, i, len, strIntLen;
1460 * Replace the "*" (if present) in the pixel-size slot of each
1461 * alternative with the targetPxlSize.
1465 while ((p = strchr(p, ',')) != NULL) {
1469 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1470 strIntLen = strlen(strInt);
1471 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1475 while (alternatives--) {
1476 char *comma = strchr(p, ',');
1477 for (i=0; i<14; i++) {
1478 char *hyphen = strchr(p, '-');
1480 if (comma && hyphen > comma) break;
1481 len = hyphen + 1 - p;
1482 if (i == 7 && *p == '*' && len == 2) {
1484 memcpy(q, strInt, strIntLen);
1494 len = comma + 1 - p;
1501 return base_fnt_lst;
1506 CreateFontSet (char *base_fnt_lst)
1509 char **missing_list;
1513 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1514 &missing_list, &missing_count, &def_string);
1515 if (appData.debugMode) {
1517 XFontStruct **font_struct_list;
1518 char **font_name_list;
1519 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1521 fprintf(debugFP, " got list %s, locale %s\n",
1522 XBaseFontNameListOfFontSet(fntSet),
1523 XLocaleOfFontSet(fntSet));
1524 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1525 for (i = 0; i < count; i++) {
1526 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1529 for (i = 0; i < missing_count; i++) {
1530 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1533 if (fntSet == NULL) {
1534 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1540 #else // not ENABLE_NLS
1542 * Find a font that matches "pattern" that is as close as
1543 * possible to the targetPxlSize. Prefer fonts that are k
1544 * pixels smaller to fonts that are k pixels larger. The
1545 * pattern must be in the X Consortium standard format,
1546 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1547 * The return value should be freed with XtFree when no
1551 FindFont (char *pattern, int targetPxlSize)
1553 char **fonts, *p, *best, *scalable, *scalableTail;
1554 int i, j, nfonts, minerr, err, pxlSize;
1557 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1559 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1560 programName, pattern);
1567 for (i=0; i<nfonts; i++) {
1570 if (*p != '-') continue;
1572 if (*p == NULLCHAR) break;
1573 if (*p++ == '-') j++;
1575 if (j < 7) continue;
1578 scalable = fonts[i];
1581 err = pxlSize - targetPxlSize;
1582 if (Abs(err) < Abs(minerr) ||
1583 (minerr > 0 && err < 0 && -err == minerr)) {
1589 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1590 /* If the error is too big and there is a scalable font,
1591 use the scalable font. */
1592 int headlen = scalableTail - scalable;
1593 p = (char *) XtMalloc(strlen(scalable) + 10);
1594 while (isdigit(*scalableTail)) scalableTail++;
1595 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1597 p = (char *) XtMalloc(strlen(best) + 2);
1598 safeStrCpy(p, best, strlen(best)+1 );
1600 if (appData.debugMode) {
1601 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1602 pattern, targetPxlSize, p);
1604 XFreeFontNames(fonts);
1611 EnableNamedMenuItem (char *menuRef, int state)
1613 MenuItem *item = MenuNameToItem(menuRef);
1615 if(item) gtk_widget_set_sensitive(item->handle, state);
1619 EnableButtonBar (int state)
1622 XtSetSensitive(optList[W_BUTTON].handle, state);
1628 SetMenuEnables (Enables *enab)
1630 while (enab->name != NULL) {
1631 EnableNamedMenuItem(enab->name, enab->value);
1637 gboolean KeyPressProc(window, eventkey, data)
1639 GdkEventKey *eventkey;
1643 MoveTypeInProc(eventkey); // pop up for typed in moves
1645 // handle shift+<number> cases
1646 if (eventkey->state & GDK_SHIFT_MASK) {
1649 gdk_keymap_translate_keyboard_state(NULL, eventkey->hardware_keycode,
1651 &keyval, NULL, NULL, NULL);
1654 AskQuestionEvent("Direct command", "Send to chess program:", "", "1");
1657 AskQuestionEvent("Direct command", "Send to second chess program:", "", "2");
1664 /* check for other key values */
1665 switch(eventkey->keyval) {
1675 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1676 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1678 if(*nprms == 0) return;
1679 item = MenuNameToItem(prms[0]);
1680 if(item) ((MenuProc *) item->proc) ();
1694 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1695 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1696 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1697 dmEnables[i].piece);
1698 XtSetSensitive(entry, p != NULL || !appData.testLegality
1699 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1700 && !appData.icsActive));
1702 while (p && *p++ == dmEnables[i].piece) count++;
1703 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1705 XtSetArg(args[j], XtNlabel, label); j++;
1706 XtSetValues(entry, args, j);
1712 do_flash_delay (unsigned long msec)
1718 FlashDelay (int flash_delay)
1720 if(flash_delay) do_flash_delay(flash_delay);
1724 Fraction (int x, int start, int stop)
1726 double f = ((double) x - start)/(stop - start);
1727 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1731 static WindowPlacement wpNew;
1735 CoDrag (Widget sh, WindowPlacement *wp)
1738 int j=0, touch=0, fudge = 2;
1739 GetActualPlacement(sh, wp);
1740 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1741 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1742 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1743 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1744 if(!touch ) return; // only windows that touch co-move
1745 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1746 int heightInc = wpNew.height - wpMain.height;
1747 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1748 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1749 wp->y += fracTop * heightInc;
1750 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1751 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1752 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1753 int widthInc = wpNew.width - wpMain.width;
1754 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1755 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1756 wp->y += fracLeft * widthInc;
1757 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1758 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1760 wp->x += wpNew.x - wpMain.x;
1761 wp->y += wpNew.y - wpMain.y;
1762 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1763 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1765 XtSetArg(args[j], XtNx, wp->x); j++;
1766 XtSetArg(args[j], XtNy, wp->y); j++;
1767 XtSetValues(sh, args, j);
1772 ReSize (WindowPlacement *wp)
1775 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1776 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1777 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1778 if(sqy < sqx) sqx = sqy;
1779 if(sqx != squareSize) {
1780 squareSize = sqx; // adopt new square size
1781 CreatePNGPieces(); // make newly scaled pieces
1782 InitDrawingSizes(0, 0); // creates grid etc.
1783 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1784 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1785 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1786 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1787 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1791 static XtIntervalId delayedDragID = 0;
1793 static int delayedDragID = 0;
1803 GetActualPlacement(shellWidget, &wpNew);
1804 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1805 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1806 busy = 0; return; // false alarm
1809 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1810 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1811 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1812 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1814 DrawPosition(True, NULL);
1815 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1824 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1826 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1832 EventProc (Widget widget, caddr_t unused, XEvent *event)
1834 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1835 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1840 * event handler for redrawing the board
1844 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1846 DrawPosition(True, NULL);
1852 /* Disable all user input other than deleting the window */
1853 static int frozen = 0;
1859 /* Grab by a widget that doesn't accept input */
1860 gtk_grab_add(optList[W_MESSG].handle);
1864 /* Undo a FreezeUI */
1868 if (!frozen) return;
1869 gtk_grab_remove(optList[W_MESSG].handle);
1876 static int oldPausing = FALSE;
1877 static GameMode oldmode = (GameMode) -1;
1879 if (!boardWidget) return;
1881 if (pausing != oldPausing) {
1882 oldPausing = pausing;
1883 MarkMenuItem("Mode.Pause", pausing);
1885 if (appData.showButtonBar) {
1886 /* Always toggle, don't set. Previous code messes up when
1887 invoked while the button is pressed, as releasing it
1888 toggles the state again. */
1890 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1891 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1895 wname = ModeToWidgetName(oldmode);
1896 if (wname != NULL) {
1897 MarkMenuItem(wname, False);
1899 wname = ModeToWidgetName(gameMode);
1900 if (wname != NULL) {
1901 MarkMenuItem(wname, True);
1904 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1906 /* Maybe all the enables should be handled here, not just this one */
1907 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1909 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1914 * Button/menu procedures
1918 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1919 char *selected_fen_position=NULL;
1922 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1923 Atom *type_return, XtPointer *value_return,
1924 unsigned long *length_return, int *format_return)
1926 char *selection_tmp;
1928 // if (!selected_fen_position) return False; /* should never happen */
1929 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1930 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1931 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1934 if (f == NULL) return False;
1938 selection_tmp = XtMalloc(len + 1);
1939 count = fread(selection_tmp, 1, len, f);
1942 XtFree(selection_tmp);
1945 selection_tmp[len] = NULLCHAR;
1947 /* note: since no XtSelectionDoneProc was registered, Xt will
1948 * automatically call XtFree on the value returned. So have to
1949 * make a copy of it allocated with XtMalloc */
1950 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1951 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1954 *value_return=selection_tmp;
1955 *length_return=strlen(selection_tmp);
1956 *type_return=*target;
1957 *format_return = 8; /* bits per byte */
1959 } else if (*target == XA_TARGETS(xDisplay)) {
1960 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1961 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1962 targets_tmp[1] = XA_STRING;
1963 *value_return = targets_tmp;
1964 *type_return = XA_ATOM;
1967 // This code leads to a read of value_return out of bounds on 64-bit systems.
1968 // Other code which I have seen always sets *format_return to 32 independent of
1969 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1970 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1971 *format_return = 8 * sizeof(Atom);
1972 if (*format_return > 32) {
1973 *length_return *= *format_return / 32;
1974 *format_return = 32;
1977 *format_return = 32;
1986 /* note: when called from menu all parameters are NULL, so no clue what the
1987 * Widget which was clicked on was, or what the click event was
1990 CopySomething (char *src)
1993 selected_fen_position = src;
1995 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1996 * have a notion of a position that is selected but not copied.
1997 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1999 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2001 SendPositionSelection,
2002 NULL/* lose_ownership_proc */ ,
2003 NULL/* transfer_done_proc */);
2004 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2006 SendPositionSelection,
2007 NULL/* lose_ownership_proc */ ,
2008 NULL/* transfer_done_proc */);
2013 /* function called when the data to Paste is ready */
2015 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2016 Atom *type, XtPointer value, unsigned long *len, int *format)
2019 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2020 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2021 EditPositionPasteFEN(fenstr);
2026 /* called when Paste Position button is pressed,
2027 * all parameters will be NULL */
2029 PastePositionProc ()
2032 XtGetSelectionValue(menuBarWidget,
2033 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2034 /* (XtSelectionCallbackProc) */ PastePositionCB,
2035 NULL, /* client_data passed to PastePositionCB */
2037 /* better to use the time field from the event that triggered the
2038 * call to this function, but that isn't trivial to get
2047 /* note: when called from menu all parameters are NULL, so no clue what the
2048 * Widget which was clicked on was, or what the click event was
2050 /* function called when the data to Paste is ready */
2052 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2053 Atom *type, XtPointer value, unsigned long *len, int *format)
2056 if (value == NULL || *len == 0) {
2057 return; /* nothing had been selected to copy */
2059 f = fopen(gamePasteFilename, "w");
2061 DisplayError(_("Can't open temp file"), errno);
2064 fwrite(value, 1, *len, f);
2067 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2071 /* called when Paste Game button is pressed,
2072 * all parameters will be NULL */
2077 XtGetSelectionValue(menuBarWidget,
2078 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2079 /* (XtSelectionCallbackProc) */ PasteGameCB,
2080 NULL, /* client_data passed to PasteGameCB */
2082 /* better to use the time field from the event that triggered the
2083 * call to this function, but that isn't trivial to get
2094 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2100 void MoveTypeInProc(eventkey)
2101 GdkEventKey *eventkey;
2105 // ingnore if ctrl or alt is pressed
2106 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
2110 buf[0]=eventkey->keyval;
2118 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2120 if (!TempBackwardActive) {
2121 TempBackwardActive = True;
2127 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2129 /* Check to see if triggered by a key release event for a repeating key.
2130 * If so the next queued event will be a key press of the same key at the same time */
2131 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2133 XPeekEvent(xDisplay, &next);
2134 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2135 next.xkey.keycode == event->xkey.keycode)
2139 TempBackwardActive = False;
2143 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2144 { // called as key binding
2147 if (nprms && *nprms > 0)
2151 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2158 { // called from menu
2160 ManInner(NULL, NULL, NULL, NULL);
2165 SetWindowTitle (char *text, char *title, char *icon)
2170 if (appData.titleInWindow) {
2172 XtSetArg(args[i], XtNlabel, text); i++;
2173 XtSetValues(titleWidget, args, i);
2176 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2177 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2178 XtSetValues(shellWidget, args, i);
2179 XSync(xDisplay, False);
2181 if (appData.titleInWindow) {
2182 SetWidgetLabel(titleWidget, text);
2184 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2189 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2195 DisplayIcsInteractionTitle (String message)
2198 if (oldICSInteractionTitle == NULL) {
2199 /* Magic to find the old window title, adapted from vim */
2200 char *wina = getenv("WINDOWID");
2202 Window win = (Window) atoi(wina);
2203 Window root, parent, *children;
2204 unsigned int nchildren;
2205 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2207 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2208 if (!XQueryTree(xDisplay, win, &root, &parent,
2209 &children, &nchildren)) break;
2210 if (children) XFree((void *)children);
2211 if (parent == root || parent == 0) break;
2214 XSetErrorHandler(oldHandler);
2216 if (oldICSInteractionTitle == NULL) {
2217 oldICSInteractionTitle = "xterm";
2220 printf("\033]0;%s\007", message);
2227 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2229 GtkWidget *w = (GtkWidget *) opt->handle;
2235 strcpy(bgcolor, "black");
2236 strcpy(fgcolor, "white");
2238 strcpy(bgcolor, "white");
2239 strcpy(fgcolor, "black");
2242 appData.lowTimeWarning &&
2243 (timer / 1000) < appData.icsAlarmTime) {
2244 strcpy(fgcolor, appData.lowTimeWarningColor);
2247 if (appData.clockMode) {
2248 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2249 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2251 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2252 bgcolor, fgcolor, color);
2254 gtk_label_set_markup(GTK_LABEL(w), markup);
2259 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2263 SetClockIcon (int color)
2267 Pixmap pm = *clockIcons[color];
2268 if (iconPixmap != pm) {
2270 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2271 XtSetValues(shellWidget, args, 1);
2276 #define INPUT_SOURCE_BUF_SIZE 8192
2285 char buf[INPUT_SOURCE_BUF_SIZE];
2290 DoInputCallback(io, cond, data)
2295 /* read input from one of the input source (for example a chess program, ICS, etc).
2296 * and call a function that will handle the input
2303 /* All information (callback function, file descriptor, etc) is
2304 * saved in an InputSource structure
2306 InputSource *is = (InputSource *) data;
2308 if (is->lineByLine) {
2309 count = read(is->fd, is->unused,
2310 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2312 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2315 is->unused += count;
2317 /* break input into lines and call the callback function on each
2320 while (p < is->unused) {
2321 q = memchr(p, '\n', is->unused - p);
2322 if (q == NULL) break;
2324 (is->func)(is, is->closure, p, q - p, 0);
2327 /* remember not yet used part of the buffer */
2329 while (p < is->unused) {
2334 /* read maximum length of input buffer and send the whole buffer
2335 * to the callback function
2337 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2342 (is->func)(is, is->closure, is->buf, count, error);
2344 return True; // Must return true or the watch will be removed
2347 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2354 GIOChannel *channel;
2355 ChildProc *cp = (ChildProc *) pr;
2357 is = (InputSource *) calloc(1, sizeof(InputSource));
2358 is->lineByLine = lineByLine;
2362 is->fd = fileno(stdin);
2364 is->kind = cp->kind;
2365 is->fd = cp->fdFrom;
2368 is->unused = is->buf;
2372 /* GTK-TODO: will this work on windows?*/
2374 channel = g_io_channel_unix_new(is->fd);
2375 g_io_channel_set_close_on_unref (channel, TRUE);
2376 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2378 is->closure = closure;
2379 return (InputSourceRef) is;
2384 RemoveInputSource(isr)
2387 InputSource *is = (InputSource *) isr;
2389 if (is->sid == 0) return;
2390 g_source_remove(is->sid);
2397 static Boolean frameWaiting;
2400 FrameAlarm (int sig)
2402 frameWaiting = False;
2403 /* In case System-V style signals. Needed?? */
2404 signal(SIGALRM, FrameAlarm);
2408 FrameDelay (int time)
2410 struct itimerval delay;
2413 frameWaiting = True;
2414 signal(SIGALRM, FrameAlarm);
2415 delay.it_interval.tv_sec =
2416 delay.it_value.tv_sec = time / 1000;
2417 delay.it_interval.tv_usec =
2418 delay.it_value.tv_usec = (time % 1000) * 1000;
2419 setitimer(ITIMER_REAL, &delay, NULL);
2420 while (frameWaiting) pause();
2421 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2422 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2423 setitimer(ITIMER_REAL, &delay, NULL);
2430 FrameDelay (int time)
2433 XSync(xDisplay, False);
2435 // gtk_main_iteration_do(False);
2438 usleep(time * 1000);
2444 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2446 char buf[MSG_SIZ], *logoName = buf;
2447 if(appData.logo[n][0]) {
2448 logoName = appData.logo[n];
2449 } else if(appData.autoLogo) {
2450 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2451 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2452 } else if(appData.directory[n] && appData.directory[n][0]) {
2453 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2457 { ASSIGN(cps->programLogo, logoName); }
2461 UpdateLogos (int displ)
2463 if(optList[W_WHITE-1].handle == NULL) return;
2464 LoadLogo(&first, 0, 0);
2465 LoadLogo(&second, 1, appData.icsActive);
2466 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2470 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2481 GtkFileFilter *gtkfilter;
2482 GtkFileFilter *gtkfilter_all;
2484 char fileext[10] = "";
2485 char *result = NULL;
2488 /* make a copy of the filter string, so that strtok can work with it*/
2489 cp = strndup(filter,strlen(filter));
2491 /* add filters for file extensions */
2492 gtkfilter = gtk_file_filter_new();
2493 gtkfilter_all = gtk_file_filter_new();
2495 /* one filter to show everything */
2496 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2497 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2499 /* add filter if present */
2500 result = strtok(cp, space);
2501 while( result != NULL ) {
2502 snprintf(fileext,10,"*%s",result);
2503 result = strtok( NULL, space );
2504 gtk_file_filter_add_pattern(gtkfilter, fileext);
2507 /* second filter to only show what's useful */
2508 gtk_file_filter_set_name (gtkfilter,filter);
2510 if (openMode[0] == 'r')
2512 dialog = gtk_file_chooser_dialog_new (label,
2514 GTK_FILE_CHOOSER_ACTION_OPEN,
2515 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2516 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2521 dialog = gtk_file_chooser_dialog_new (label,
2523 GTK_FILE_CHOOSER_ACTION_SAVE,
2524 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2525 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2527 /* add filename suggestions */
2528 if (strlen(def) > 0 )
2529 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2531 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2535 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2536 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2537 /* activate filter */
2538 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2540 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2545 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2548 f = fopen(filename, openMode);
2551 DisplayError(_("Failed to open file"), errno);
2555 /* TODO add indec */
2557 ASSIGN(*name, filename);
2558 ScheduleDelayedEvent(DelayedLoad, 50);
2563 gtk_widget_destroy (dialog);