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));
200 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
202 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
203 void HandlePV P((Widget w, XEvent * event,
204 String * params, Cardinal * nParams));
205 void DrawPositionProc P((Widget w, XEvent *event,
206 String *prms, Cardinal *nprms));
207 void CommentClick P((Widget w, XEvent * event,
208 String * params, Cardinal * nParams));
209 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
210 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
211 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
212 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
213 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
214 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
215 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
216 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
217 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
218 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
220 Boolean TempBackwardActive = False;
221 void DisplayMove P((int moveNumber));
222 void ICSInitScript P((void));
223 void update_ics_width P(());
224 int CopyMemoProc P(());
225 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
229 * XBoard depends on Xt R4 or higher
231 int xtVersion = XtSpecificationRelease;
236 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
237 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
238 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
240 XFontSet fontSet, clockFontSet;
243 XFontStruct *clockFontStruct;
245 Font coordFontID, countFontID;
246 XFontStruct *coordFontStruct, *countFontStruct;
247 XtAppContext appContext;
249 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
251 GtkWidget *mainwindow;
253 Option *optList; // contains all widgets of main window
256 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
259 static GdkPixbuf *mainwindowIcon=NULL;
260 static GdkPixbuf *WhiteIcon=NULL;
261 static GdkPixbuf *BlackIcon=NULL;
263 typedef unsigned int BoardSize;
265 Boolean chessProgram;
267 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
268 int smallLayout = 0, tinyLayout = 0,
269 marginW, marginH, // [HGM] for run-time resizing
270 fromX = -1, fromY = -1, toX, toY, commentUp = False,
271 errorExitStatus = -1, defaultLineGap;
273 Dimension textHeight;
274 Pixel timerForegroundPixel, timerBackgroundPixel;
275 Pixel buttonForegroundPixel, buttonBackgroundPixel;
277 char *chessDir, *programName, *programVersion;
278 Boolean alwaysOnTop = False;
279 char *icsTextMenuString;
281 char *firstChessProgramNames;
282 char *secondChessProgramNames;
284 WindowPlacement wpMain;
285 WindowPlacement wpConsole;
286 WindowPlacement wpComment;
287 WindowPlacement wpMoveHistory;
288 WindowPlacement wpEvalGraph;
289 WindowPlacement wpEngineOutput;
290 WindowPlacement wpGameList;
291 WindowPlacement wpTags;
293 /* This magic number is the number of intermediate frames used
294 in each half of the animation. For short moves it's reduced
295 by 1. The total number of frames will be factor * 2 + 1. */
298 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
305 DropMenuEnables dmEnables[] = {
323 XtResource clientResources[] = {
324 { "flashCount", "flashCount", XtRInt, sizeof(int),
325 XtOffset(AppDataPtr, flashCount), XtRImmediate,
326 (XtPointer) FLASH_COUNT },
329 XrmOptionDescRec shellOptions[] = {
330 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
331 { "-flash", "flashCount", XrmoptionNoArg, "3" },
332 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
335 XtActionsRec boardActions[] = {
336 { "DrawPosition", DrawPositionProc },
337 { "HandlePV", HandlePV },
338 { "SelectPV", SelectPV },
339 { "StopPV", StopPV },
340 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
341 { "QuitProc", QuitWrapper },
342 { "ManProc", ManInner },
343 { "TempBackwardProc", TempBackwardProc },
344 { "TempForwardProc", TempForwardProc },
345 { "CommentClick", (XtActionProc) CommentClick },
346 { "GenericPopDown", (XtActionProc) GenericPopDown },
347 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
348 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
349 { "SelectMove", (XtActionProc) SelectMove },
350 { "LoadSelectedProc", LoadSelectedProc },
351 { "SetFilterProc", SetFilterProc },
352 { "TypeInProc", TypeInProc },
353 { "EnterKeyProc", EnterKeyProc },
354 { "UpKeyProc", UpKeyProc },
355 { "DownKeyProc", DownKeyProc },
356 { "WheelProc", WheelProc },
357 { "TabProc", TabProc },
361 char globalTranslations[] =
362 ":<Key>F9: MenuItem(Actions.Resign) \n \
363 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
364 :Meta<Key>V: MenuItem(File.NewVariant) \n \
365 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
366 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
367 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
368 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
369 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
370 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
371 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
372 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
373 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
374 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
375 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
376 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
377 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
378 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
379 :Ctrl<Key>q: MenuItem(File.Quit) \n \
380 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
381 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
382 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
383 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
384 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
385 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
386 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
387 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
388 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
389 :Meta<Key>G: MenuItem(View.GameList) \n \
390 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
391 :<Key>Pause: MenuItem(Mode.Pause) \n \
392 :<Key>F3: MenuItem(Action.Accept) \n \
393 :<Key>F4: MenuItem(Action.Decline) \n \
394 :<Key>F12: MenuItem(Action.Rematch) \n \
395 :<Key>F5: MenuItem(Action.CallFlag) \n \
396 :<Key>F6: MenuItem(Action.Draw) \n \
397 :<Key>F7: MenuItem(Action.Adjourn) \n \
398 :<Key>F8: MenuItem(Action.Abort) \n \
399 :<Key>F10: MenuItem(Action.StopObserving) \n \
400 :<Key>F11: MenuItem(Action.StopExamining) \n \
401 :Ctrl<Key>d: MenuItem(DebugProc) \n \
402 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
403 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
404 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
405 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
406 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
407 :<Key>Left: MenuItem(Edit.Backward) \n \
408 :<Key>Right: MenuItem(Edit.Forward) \n \
409 :<Key>Home: MenuItem(Edit.Revert) \n \
410 :<Key>End: MenuItem(Edit.TruncateGame) \n \
411 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
412 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
413 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
414 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
415 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
416 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
417 #ifndef OPTIONSDIALOG
419 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
420 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
421 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
422 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
423 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
426 :<Key>F1: MenuItem(Help.ManXBoard) \n \
427 :<Key>F2: MenuItem(View.FlipView) \n \
428 :<KeyDown>Return: TempBackwardProc() \n \
429 :<KeyUp>Return: TempForwardProc() \n";
431 char ICSInputTranslations[] =
432 "<Key>Up: UpKeyProc() \n "
433 "<Key>Down: DownKeyProc() \n "
434 "<Key>Return: EnterKeyProc() \n";
436 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
437 // as the widget is destroyed before the up-click can call extend-end
438 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
441 String xboardResources[] = {
442 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
447 /* Max possible square size */
448 #define MAXSQSIZE 256
450 static int xpm_avail[MAXSQSIZE];
452 #ifdef HAVE_DIR_STRUCT
454 /* Extract piece size from filename */
456 xpm_getsize (char *name, int len, char *ext)
464 if ((p=strchr(name, '.')) == NULL ||
465 StrCaseCmp(p+1, ext) != 0)
471 while (*p && isdigit(*p))
478 /* Setup xpm_avail */
480 xpm_getavail (char *dirname, char *ext)
486 for (i=0; i<MAXSQSIZE; ++i)
489 if (appData.debugMode)
490 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
492 dir = opendir(dirname);
495 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
496 programName, dirname);
500 while ((ent=readdir(dir)) != NULL) {
501 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
502 if (i > 0 && i < MAXSQSIZE)
512 xpm_print_avail (FILE *fp, char *ext)
516 fprintf(fp, _("Available `%s' sizes:\n"), ext);
517 for (i=1; i<MAXSQSIZE; ++i) {
523 /* Return XPM piecesize closest to size */
525 xpm_closest_to (char *dirname, int size, char *ext)
528 int sm_diff = MAXSQSIZE;
532 xpm_getavail(dirname, ext);
534 if (appData.debugMode)
535 xpm_print_avail(stderr, ext);
537 for (i=1; i<MAXSQSIZE; ++i) {
540 diff = (diff<0) ? -diff : diff;
541 if (diff < sm_diff) {
549 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
555 #else /* !HAVE_DIR_STRUCT */
556 /* If we are on a system without a DIR struct, we can't
557 read the directory, so we can't collect a list of
558 filenames, etc., so we can't do any size-fitting. */
560 xpm_closest_to (char *dirname, int size, char *ext)
563 Warning: No DIR structure found on this system --\n\
564 Unable to autosize for XPM/XIM pieces.\n\
565 Please report this error to %s.\n\
566 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
569 #endif /* HAVE_DIR_STRUCT */
573 /* Arrange to catch delete-window events */
574 Atom wm_delete_window;
576 CatchDeleteWindow (Widget w, String procname)
579 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
580 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
581 XtAugmentTranslations(w, XtParseTranslationTable(buf));
588 gtk_window_present(GTK_WINDOW(mainwindow));
591 //---------------------------------------------------------------------------------------------------------
592 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
595 #define CW_USEDEFAULT (1<<31)
596 #define ICS_TEXT_MENU_SIZE 90
597 #define DEBUG_FILE "xboard.debug"
598 #define SetCurrentDirectory chdir
599 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
603 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
606 // front-end part of option handling
608 // [HGM] This platform-dependent table provides the location for storing the color info
609 extern char *crWhite, * crBlack;
613 &appData.whitePieceColor,
614 &appData.blackPieceColor,
615 &appData.lightSquareColor,
616 &appData.darkSquareColor,
617 &appData.highlightSquareColor,
618 &appData.premoveHighlightColor,
619 &appData.lowTimeWarningColor,
630 // [HGM] font: keep a font for each square size, even non-stndard ones
633 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
634 char *fontTable[NUM_FONTS][MAX_SIZE];
637 ParseFont (char *name, int number)
638 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
640 if(sscanf(name, "size%d:", &size)) {
641 // [HGM] font: font is meant for specific boardSize (likely from settings file);
642 // defer processing it until we know if it matches our board size
643 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
644 fontTable[number][size] = strdup(strchr(name, ':')+1);
645 fontValid[number][size] = True;
650 case 0: // CLOCK_FONT
651 appData.clockFont = strdup(name);
653 case 1: // MESSAGE_FONT
654 appData.font = strdup(name);
656 case 2: // COORD_FONT
657 appData.coordFont = strdup(name);
662 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
667 { // only 2 fonts currently
668 appData.clockFont = CLOCK_FONT_NAME;
669 appData.coordFont = COORD_FONT_NAME;
670 appData.font = DEFAULT_FONT_NAME;
675 { // no-op, until we identify the code for this already in XBoard and move it here
679 ParseColor (int n, char *name)
680 { // in XBoard, just copy the color-name string
681 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
685 ParseTextAttribs (ColorClass cc, char *s)
687 (&appData.colorShout)[cc] = strdup(s);
691 ParseBoardSize (void *addr, char *name)
693 appData.boardSize = strdup(name);
698 { // In XBoard the sound-playing program takes care of obtaining the actual sound
702 SetCommPortDefaults ()
703 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
706 // [HGM] args: these three cases taken out to stay in front-end
708 SaveFontArg (FILE *f, ArgDescriptor *ad)
711 int i, n = (int)(intptr_t)ad->argLoc;
713 case 0: // CLOCK_FONT
714 name = appData.clockFont;
716 case 1: // MESSAGE_FONT
719 case 2: // COORD_FONT
720 name = appData.coordFont;
725 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
726 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
727 fontTable[n][squareSize] = strdup(name);
728 fontValid[n][squareSize] = True;
731 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
732 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
737 { // nothing to do, as the sounds are at all times represented by their text-string names already
741 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
742 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
743 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
747 SaveColor (FILE *f, ArgDescriptor *ad)
748 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
749 if(colorVariable[(int)(intptr_t)ad->argLoc])
750 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
754 SaveBoardSize (FILE *f, char *name, void *addr)
755 { // wrapper to shield back-end from BoardSize & sizeInfo
756 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
760 ParseCommPortSettings (char *s)
761 { // no such option in XBoard (yet)
767 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
771 gtk_widget_get_allocation(shell, &a);
775 wp->height = a.height;
776 printf("placement\n");
777 frameX = a.x; frameY = a.y; // remember to decide if windows touch
781 GetActualPlacement (Widget wg, WindowPlacement *wp)
783 XWindowAttributes winAt;
790 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
791 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
792 wp->x = rx - winAt.x;
793 wp->y = ry - winAt.y;
794 wp->height = winAt.height;
795 wp->width = winAt.width;
796 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
802 { // wrapper to shield use of window handles from back-end (make addressible by number?)
803 // In XBoard this will have to wait until awareness of window parameters is implemented
804 GetActualPlacement(shellWidget, &wpMain);
805 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
806 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
807 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
808 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
809 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
810 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
814 PrintCommPortSettings (FILE *f, char *name)
815 { // This option does not exist in XBoard
819 EnsureOnScreen (int *x, int *y, int minX, int minY)
826 { // [HGM] args: allows testing if main window is realized from back-end
828 return xBoardWindow != 0;
830 return DialogExists(BoardWindow);
835 PopUpStartupDialog ()
836 { // start menu not implemented in XBoard
840 ConvertToLine (int argc, char **argv)
842 static char line[128*1024], buf[1024];
846 for(i=1; i<argc; i++)
848 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
849 && argv[i][0] != '{' )
850 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
852 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
853 strncat(line, buf, 128*1024 - strlen(line) - 1 );
856 line[strlen(line)-1] = NULLCHAR;
860 //--------------------------------------------------------------------------------------------
863 ResizeBoardWindow (int w, int h, int inhibit)
865 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
867 // gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
869 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
871 shellArgs[0].value = w;
872 shellArgs[1].value = h;
873 shellArgs[4].value = shellArgs[2].value = w;
874 shellArgs[5].value = shellArgs[3].value = h;
875 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
877 XSync(xDisplay, False);
883 MakeOneColor (char *name, Pixel *color)
886 if (!appData.monoMode) {
887 vFrom.addr = (caddr_t) name;
888 vFrom.size = strlen(name);
889 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
890 if (vTo.addr == NULL) {
891 appData.monoMode = True;
894 *color = *(Pixel *) vTo.addr;
903 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
904 int forceMono = False;
907 if (appData.lowTimeWarning)
908 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
909 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
910 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
917 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
918 { // detervtomine what fonts to use, and create them
923 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
924 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
925 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
926 appData.font = fontTable[MESSAGE_FONT][squareSize];
927 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
928 appData.coordFont = fontTable[COORD_FONT][squareSize];
931 appData.font = InsertPxlSize(appData.font, fontPxlSize);
932 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
933 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
934 fontSet = CreateFontSet(appData.font);
935 clockFontSet = CreateFontSet(appData.clockFont);
937 /* For the coordFont, use the 0th font of the fontset. */
938 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
939 XFontStruct **font_struct_list;
940 XFontSetExtents *fontSize;
941 char **font_name_list;
942 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
943 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
944 coordFontStruct = XQueryFont(xDisplay, coordFontID);
945 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
946 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
949 appData.font = FindFont(appData.font, fontPxlSize);
950 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
951 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
952 clockFontID = XLoadFont(xDisplay, appData.clockFont);
953 clockFontStruct = XQueryFont(xDisplay, clockFontID);
954 coordFontID = XLoadFont(xDisplay, appData.coordFont);
955 coordFontStruct = XQueryFont(xDisplay, coordFontID);
956 // textHeight in !NLS mode!
958 countFontID = coordFontID; // [HGM] holdings
959 countFontStruct = coordFontStruct;
961 xdb = XtDatabase(xDisplay);
963 XrmPutLineResource(&xdb, "*international: True");
964 vTo.size = sizeof(XFontSet);
965 vTo.addr = (XtPointer) &fontSet;
966 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
968 XrmPutStringResource(&xdb, "*font", appData.font);
979 case ArgInt: p = " N"; break;
980 case ArgString: p = " STR"; break;
981 case ArgBoolean: p = " TF"; break;
982 case ArgSettingsFilename:
983 case ArgFilename: p = " FILE"; break;
984 case ArgX: p = " Nx"; break;
985 case ArgY: p = " Ny"; break;
986 case ArgAttribs: p = " TEXTCOL"; break;
987 case ArgColor: p = " COL"; break;
988 case ArgFont: p = " FONT"; break;
989 case ArgBoardSize: p = " SIZE"; break;
990 case ArgFloat: p = " FLOAT"; break;
995 case ArgCommSettings:
1006 ArgDescriptor *q, *p = argDescriptors+5;
1007 printf("\nXBoard accepts the following options:\n"
1008 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1009 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1010 " SIZE = board-size spec(s)\n"
1011 " Within parentheses are short forms, or options to set to true or false.\n"
1012 " Persistent options (saved in the settings file) are marked with *)\n\n");
1014 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1015 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1016 if(p->save) strcat(buf+len, "*");
1017 for(q=p+1; q->argLoc == p->argLoc; q++) {
1018 if(q->argName[0] == '-') continue;
1019 strcat(buf+len, q == p+1 ? " (" : " ");
1020 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1022 if(q != p+1) strcat(buf+len, ")");
1024 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1027 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1031 main (int argc, char **argv)
1033 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1035 XSetWindowAttributes window_attributes;
1037 Dimension boardWidth, boardHeight, w, h;
1040 int boardWidth, boardHeight, w, h;
1042 int forceMono = False;
1043 GError *gtkerror=NULL;
1045 srandom(time(0)); // [HGM] book: make random truly random
1047 setbuf(stdout, NULL);
1048 setbuf(stderr, NULL);
1051 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1052 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1056 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1062 gtk_init (&argc, &argv);
1064 programName = strrchr(argv[0], '/');
1065 if (programName == NULL)
1066 programName = argv[0];
1071 // if (appData.debugMode) {
1072 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1075 bindtextdomain(PACKAGE, LOCALEDIR);
1076 textdomain(PACKAGE);
1079 appData.boardSize = "";
1080 InitAppData(ConvertToLine(argc, argv));
1082 if (p == NULL) p = "/tmp";
1083 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1084 gameCopyFilename = (char*) malloc(i);
1085 gamePasteFilename = (char*) malloc(i);
1086 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1087 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1089 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1090 static char buf[MSG_SIZ];
1091 EscapeExpand(buf, appData.firstInitString);
1092 appData.firstInitString = strdup(buf);
1093 EscapeExpand(buf, appData.secondInitString);
1094 appData.secondInitString = strdup(buf);
1095 EscapeExpand(buf, appData.firstComputerString);
1096 appData.firstComputerString = strdup(buf);
1097 EscapeExpand(buf, appData.secondComputerString);
1098 appData.secondComputerString = strdup(buf);
1101 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1104 if (chdir(chessDir) != 0) {
1105 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1111 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1112 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1113 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1114 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1117 setbuf(debugFP, NULL);
1121 if (appData.debugMode) {
1122 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1126 /* [HGM,HR] make sure board size is acceptable */
1127 if(appData.NrFiles > BOARD_FILES ||
1128 appData.NrRanks > BOARD_RANKS )
1129 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1132 /* This feature does not work; animation needs a rewrite */
1133 appData.highlightDragging = FALSE;
1137 gameInfo.variant = StringToVariant(appData.variant);
1138 InitPosition(FALSE);
1142 builder = gtk_builder_new();
1143 filename = get_glade_filename ("mainboard.glade");
1144 if(! gtk_builder_add_from_file (builder, filename, >kerror) )
1147 printf ("Error: %d %s\n",gtkerror->code,gtkerror->message);
1149 mainwindow = GTK_WIDGET(gtk_builder_get_object (builder, "mainwindow"));
1152 XtAppInitialize(&appContext, "XBoard", shellOptions,
1153 XtNumber(shellOptions),
1154 &argc, argv, xboardResources, NULL, 0);
1156 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1157 clientResources, XtNumber(clientResources),
1160 xDisplay = XtDisplay(shellWidget);
1161 xScreen = DefaultScreen(xDisplay);
1162 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1166 * determine size, based on supplied or remembered -size, or screen size
1168 if (isdigit(appData.boardSize[0])) {
1169 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1170 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1171 &fontPxlSize, &smallLayout, &tinyLayout);
1173 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1174 programName, appData.boardSize);
1178 /* Find some defaults; use the nearest known size */
1179 SizeDefaults *szd, *nearest;
1180 int distance = 99999;
1181 nearest = szd = sizeDefaults;
1182 while (szd->name != NULL) {
1183 if (abs(szd->squareSize - squareSize) < distance) {
1185 distance = abs(szd->squareSize - squareSize);
1186 if (distance == 0) break;
1190 if (i < 2) lineGap = nearest->lineGap;
1191 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1192 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1193 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1194 if (i < 6) smallLayout = nearest->smallLayout;
1195 if (i < 7) tinyLayout = nearest->tinyLayout;
1198 SizeDefaults *szd = sizeDefaults;
1199 if (*appData.boardSize == NULLCHAR) {
1201 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1202 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1206 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
1207 guint screenwidth = gdk_screen_get_width(screen);
1208 guint screenheight = gdk_screen_get_height(screen);
1209 while (screenwidth < szd->minScreenSize ||
1210 screenheight < szd->minScreenSize) {
1214 if (szd->name == NULL) szd--;
1215 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1217 while (szd->name != NULL &&
1218 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1219 if (szd->name == NULL) {
1220 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1221 programName, appData.boardSize);
1225 squareSize = szd->squareSize;
1226 lineGap = szd->lineGap;
1227 clockFontPxlSize = szd->clockFontPxlSize;
1228 coordFontPxlSize = szd->coordFontPxlSize;
1229 fontPxlSize = szd->fontPxlSize;
1230 smallLayout = szd->smallLayout;
1231 tinyLayout = szd->tinyLayout;
1232 // [HGM] font: use defaults from settings file if available and not overruled
1235 defaultLineGap = lineGap;
1236 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1238 /* [HR] height treated separately (hacked) */
1239 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1240 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1243 * Determine what fonts to use.
1246 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1250 * Detect if there are not enough colors available and adapt.
1253 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1254 appData.monoMode = True;
1258 forceMono = MakeColors();
1261 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1263 appData.monoMode = True;
1266 ParseIcsTextColors();
1269 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1276 layoutName = "tinyLayout";
1277 } else if (smallLayout) {
1278 layoutName = "smallLayout";
1280 layoutName = "normalLayout";
1283 optList = BoardPopUp(squareSize, lineGap, (void*)
1293 InitDrawingHandle(optList + W_BOARD);
1294 shellWidget = shells[BoardWindow];
1295 currBoard = &optList[W_BOARD];
1296 boardWidget = optList[W_BOARD].handle;
1297 menuBarWidget = optList[W_MENU].handle;
1298 dropMenu = optList[W_DROP].handle;
1299 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1301 formWidget = XtParent(boardWidget);
1302 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1303 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1304 XtGetValues(optList[W_WHITE].handle, args, 2);
1305 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1306 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1307 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1308 XtGetValues(optList[W_PAUSE].handle, args, 2);
1313 xBoardWindow = XtWindow(boardWidget);
1316 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1317 // not need to go into InitDrawingSizes().
1322 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1324 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1325 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1326 mainwindowIcon = WhiteIcon;
1327 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1331 * Create a cursor for the board widget.
1334 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1335 XChangeWindowAttributes(xDisplay, xBoardWindow,
1336 CWCursor, &window_attributes);
1340 * Inhibit shell resizing.
1343 shellArgs[0].value = (XtArgVal) &w;
1344 shellArgs[1].value = (XtArgVal) &h;
1345 XtGetValues(shellWidget, shellArgs, 2);
1346 shellArgs[4].value = shellArgs[2].value = w;
1347 shellArgs[5].value = shellArgs[3].value = h;
1348 // XtSetValues(shellWidget, &shellArgs[2], 4);
1352 gtk_widget_get_allocation(shells[BoardWindow], &a);
1353 w = a.width; h = a.height;
1354 printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
1356 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1357 marginH = h - boardHeight;
1360 CatchDeleteWindow(shellWidget, "QuitProc");
1366 if(appData.logoSize)
1367 { // locate and read user logo
1369 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1370 ASSIGN(userLogo, buf);
1373 if (appData.animate || appData.animateDragging)
1377 XtAugmentTranslations(formWidget,
1378 XtParseTranslationTable(globalTranslations));
1380 XtAddEventHandler(formWidget, KeyPressMask, False,
1381 (XtEventHandler) MoveTypeInProc, NULL);
1382 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1383 (XtEventHandler) EventProc, NULL);
1385 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1386 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1388 /* [AS] Restore layout */
1389 if( wpMoveHistory.visible ) {
1393 if( wpEvalGraph.visible )
1398 if( wpEngineOutput.visible ) {
1399 EngineOutputPopUp();
1404 if (errorExitStatus == -1) {
1405 if (appData.icsActive) {
1406 /* We now wait until we see "login:" from the ICS before
1407 sending the logon script (problems with timestamp otherwise) */
1408 /*ICSInitScript();*/
1409 if (appData.icsInputBox) ICSInputBoxPopUp();
1413 signal(SIGWINCH, TermSizeSigHandler);
1415 signal(SIGINT, IntSigHandler);
1416 signal(SIGTERM, IntSigHandler);
1417 if (*appData.cmailGameName != NULLCHAR) {
1418 signal(SIGUSR1, CmailSigHandler);
1422 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1425 // XtSetKeyboardFocus(shellWidget, formWidget);
1427 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1430 /* check for GTK events and process them */
1433 gtk_main_iteration();
1436 if (appData.debugMode) fclose(debugFP); // [DM] debug
1441 TermSizeSigHandler (int sig)
1447 IntSigHandler (int sig)
1453 CmailSigHandler (int sig)
1458 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1460 /* Activate call-back function CmailSigHandlerCallBack() */
1461 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1463 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1467 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1470 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1472 /**** end signal code ****/
1475 #define Abs(n) ((n)<0 ? -(n) : (n))
1479 InsertPxlSize (char *pattern, int targetPxlSize)
1481 char *base_fnt_lst, strInt[12], *p, *q;
1482 int alternatives, i, len, strIntLen;
1485 * Replace the "*" (if present) in the pixel-size slot of each
1486 * alternative with the targetPxlSize.
1490 while ((p = strchr(p, ',')) != NULL) {
1494 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1495 strIntLen = strlen(strInt);
1496 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1500 while (alternatives--) {
1501 char *comma = strchr(p, ',');
1502 for (i=0; i<14; i++) {
1503 char *hyphen = strchr(p, '-');
1505 if (comma && hyphen > comma) break;
1506 len = hyphen + 1 - p;
1507 if (i == 7 && *p == '*' && len == 2) {
1509 memcpy(q, strInt, strIntLen);
1519 len = comma + 1 - p;
1526 return base_fnt_lst;
1531 CreateFontSet (char *base_fnt_lst)
1534 char **missing_list;
1538 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1539 &missing_list, &missing_count, &def_string);
1540 if (appData.debugMode) {
1542 XFontStruct **font_struct_list;
1543 char **font_name_list;
1544 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1546 fprintf(debugFP, " got list %s, locale %s\n",
1547 XBaseFontNameListOfFontSet(fntSet),
1548 XLocaleOfFontSet(fntSet));
1549 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1550 for (i = 0; i < count; i++) {
1551 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1554 for (i = 0; i < missing_count; i++) {
1555 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1558 if (fntSet == NULL) {
1559 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1565 #else // not ENABLE_NLS
1567 * Find a font that matches "pattern" that is as close as
1568 * possible to the targetPxlSize. Prefer fonts that are k
1569 * pixels smaller to fonts that are k pixels larger. The
1570 * pattern must be in the X Consortium standard format,
1571 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1572 * The return value should be freed with XtFree when no
1576 FindFont (char *pattern, int targetPxlSize)
1578 char **fonts, *p, *best, *scalable, *scalableTail;
1579 int i, j, nfonts, minerr, err, pxlSize;
1582 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1584 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1585 programName, pattern);
1592 for (i=0; i<nfonts; i++) {
1595 if (*p != '-') continue;
1597 if (*p == NULLCHAR) break;
1598 if (*p++ == '-') j++;
1600 if (j < 7) continue;
1603 scalable = fonts[i];
1606 err = pxlSize - targetPxlSize;
1607 if (Abs(err) < Abs(minerr) ||
1608 (minerr > 0 && err < 0 && -err == minerr)) {
1614 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1615 /* If the error is too big and there is a scalable font,
1616 use the scalable font. */
1617 int headlen = scalableTail - scalable;
1618 p = (char *) XtMalloc(strlen(scalable) + 10);
1619 while (isdigit(*scalableTail)) scalableTail++;
1620 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1622 p = (char *) XtMalloc(strlen(best) + 2);
1623 safeStrCpy(p, best, strlen(best)+1 );
1625 if (appData.debugMode) {
1626 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1627 pattern, targetPxlSize, p);
1629 XFreeFontNames(fonts);
1636 EnableNamedMenuItem (char *menuRef, int state)
1638 MenuItem *item = MenuNameToItem(menuRef);
1640 if(item) gtk_widget_set_sensitive(item->handle, state);
1644 EnableButtonBar (int state)
1647 XtSetSensitive(optList[W_BUTTON].handle, state);
1653 SetMenuEnables (Enables *enab)
1655 while (enab->name != NULL) {
1656 EnableNamedMenuItem(enab->name, enab->value);
1661 gboolean KeyPressProc(window, eventkey, data)
1663 GdkEventKey *eventkey;
1667 MoveTypeInProc(eventkey); // pop up for typed in moves
1670 /* check for other key values */
1671 switch(eventkey->keyval) {
1683 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1684 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1686 if(*nprms == 0) return;
1687 item = MenuNameToItem(prms[0]);
1688 if(item) ((MenuProc *) item->proc) ();
1702 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1703 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1704 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1705 dmEnables[i].piece);
1706 XtSetSensitive(entry, p != NULL || !appData.testLegality
1707 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1708 && !appData.icsActive));
1710 while (p && *p++ == dmEnables[i].piece) count++;
1711 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1713 XtSetArg(args[j], XtNlabel, label); j++;
1714 XtSetValues(entry, args, j);
1720 do_flash_delay (unsigned long msec)
1726 FlashDelay (int flash_delay)
1728 if(flash_delay) do_flash_delay(flash_delay);
1732 Fraction (int x, int start, int stop)
1734 double f = ((double) x - start)/(stop - start);
1735 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1739 static WindowPlacement wpNew;
1742 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1744 int j=0, touch=0, fudge = 2;
1745 GetActualPlacement(sh, wp);
1746 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1747 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1748 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1749 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1750 if(!touch ) return; // only windows that touch co-move
1751 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1752 int heightInc = wpNew.height - wpMain.height;
1753 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1754 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1755 wp->y += fracTop * heightInc;
1756 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1758 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1760 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1761 int widthInc = wpNew.width - wpMain.width;
1762 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1763 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1764 wp->y += fracLeft * widthInc;
1765 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1767 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1770 wp->x += wpNew.x - wpMain.x;
1771 wp->y += wpNew.y - wpMain.y;
1772 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1773 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1775 XtSetArg(args[j], XtNx, wp->x); j++;
1776 XtSetArg(args[j], XtNy, wp->y); j++;
1777 XtSetValues(sh, args, j);
1782 ReSize (WindowPlacement *wp)
1785 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1786 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1787 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1788 if(sqy < sqx) sqx = sqy;
1789 if(sqx != squareSize) {
1790 squareSize = sqx; // adopt new square size
1791 CreatePNGPieces(); // make newly scaled pieces
1792 InitDrawingSizes(0, 0); // creates grid etc.
1793 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1794 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1795 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1796 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1797 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1801 static XtIntervalId delayedDragID = 0;
1803 static guint delayedDragTag = 0;
1813 // GetActualPlacement(shellWidget, &wpNew);
1814 printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1815 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1816 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1817 busy = 0; return; // false alarm
1820 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1821 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1822 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1823 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1825 DrawPosition(True, NULL);
1826 if(delayedDragTag) g_source_remove(delayedDragTag);
1827 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1834 printf("old timr = %d\n", delayedDragTag);
1835 if(delayedDragTag) g_source_remove(delayedDragTag);
1836 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1837 printf("new timr = %d\n", delayedDragTag);
1841 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1843 printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1845 wpNew.x = event->configure.x;
1846 wpNew.y = event->configure.y;
1847 wpNew.width = event->configure.width;
1848 wpNew.height = event->configure.height;
1849 if(appData.useStickyWindows)
1850 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1855 * event handler for redrawing the board
1859 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1861 DrawPosition(True, NULL);
1867 /* Disable all user input other than deleting the window */
1868 static int frozen = 0;
1874 /* Grab by a widget that doesn't accept input */
1875 gtk_grab_add(optList[W_MESSG].handle);
1879 /* Undo a FreezeUI */
1883 if (!frozen) return;
1884 gtk_grab_remove(optList[W_MESSG].handle);
1891 static int oldPausing = FALSE;
1892 static GameMode oldmode = (GameMode) -1;
1894 if (!boardWidget) return;
1896 if (pausing != oldPausing) {
1897 oldPausing = pausing;
1898 MarkMenuItem("Mode.Pause", pausing);
1900 if (appData.showButtonBar) {
1901 /* Always toggle, don't set. Previous code messes up when
1902 invoked while the button is pressed, as releasing it
1903 toggles the state again. */
1905 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1906 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1910 wname = ModeToWidgetName(oldmode);
1911 if (wname != NULL) {
1912 MarkMenuItem(wname, False);
1914 wname = ModeToWidgetName(gameMode);
1915 if (wname != NULL) {
1916 MarkMenuItem(wname, True);
1919 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1921 /* Maybe all the enables should be handled here, not just this one */
1922 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1924 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1929 * Button/menu procedures
1932 void CopyFileToClipboard(gchar *filename)
1934 gchar *selection_tmp;
1938 FILE* f = fopen(filename, "r");
1941 if (f == NULL) return;
1945 selection_tmp = g_try_malloc(len + 1);
1946 if (selection_tmp == NULL) {
1947 printf("Malloc failed in CopyFileToClipboard\n");
1950 count = fread(selection_tmp, 1, len, f);
1953 g_free(selection_tmp);
1956 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1958 // copy selection_tmp to clipboard
1959 GdkDisplay *gdisp = gdk_display_get_default();
1961 g_free(selection_tmp);
1964 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1965 gtk_clipboard_set_text(cb, selection_tmp, -1);
1966 g_free(selection_tmp);
1970 CopySomething (char *src)
1972 GdkDisplay *gdisp = gdk_display_get_default();
1974 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1975 if (gdisp == NULL) return;
1976 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1977 gtk_clipboard_set_text(cb, src, -1);
1981 PastePositionProc ()
1983 GdkDisplay *gdisp = gdk_display_get_default();
1987 if (gdisp == NULL) return;
1988 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1989 fenstr = gtk_clipboard_wait_for_text(cb);
1990 if (fenstr==NULL) return; // nothing had been selected to copy
1991 EditPositionPasteFEN(fenstr);
2003 // get game from clipboard
2004 GdkDisplay *gdisp = gdk_display_get_default();
2005 if (gdisp == NULL) return;
2006 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
2007 text = gtk_clipboard_wait_for_text(cb);
2008 if (text == NULL) return; // nothing to paste
2011 // write to temp file
2012 if (text == NULL || len == 0) {
2013 return; //nothing to paste
2015 f = fopen(gamePasteFilename, "w");
2017 DisplayError(_("Can't open temp file"), errno);
2020 fwrite(text, 1, len, f);
2024 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2031 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2037 void MoveTypeInProc(eventkey)
2038 GdkEventKey *eventkey;
2042 // ingnore if ctrl or alt is pressed
2043 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
2047 buf[0]=eventkey->keyval;
2055 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2057 if (!TempBackwardActive) {
2058 TempBackwardActive = True;
2064 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2066 /* Check to see if triggered by a key release event for a repeating key.
2067 * If so the next queued event will be a key press of the same key at the same time */
2068 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2070 XPeekEvent(xDisplay, &next);
2071 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2072 next.xkey.keycode == event->xkey.keycode)
2076 TempBackwardActive = False;
2080 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2081 { // called as key binding
2084 if (nprms && *nprms > 0)
2088 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2095 { // called from menu
2097 ManInner(NULL, NULL, NULL, NULL);
2102 SetWindowTitle (char *text, char *title, char *icon)
2107 if (appData.titleInWindow) {
2109 XtSetArg(args[i], XtNlabel, text); i++;
2110 XtSetValues(titleWidget, args, i);
2113 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2114 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2115 XtSetValues(shellWidget, args, i);
2116 XSync(xDisplay, False);
2118 if (appData.titleInWindow) {
2119 SetWidgetLabel(titleWidget, text);
2121 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2126 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2132 DisplayIcsInteractionTitle (String message)
2135 if (oldICSInteractionTitle == NULL) {
2136 /* Magic to find the old window title, adapted from vim */
2137 char *wina = getenv("WINDOWID");
2139 Window win = (Window) atoi(wina);
2140 Window root, parent, *children;
2141 unsigned int nchildren;
2142 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2144 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2145 if (!XQueryTree(xDisplay, win, &root, &parent,
2146 &children, &nchildren)) break;
2147 if (children) XFree((void *)children);
2148 if (parent == root || parent == 0) break;
2151 XSetErrorHandler(oldHandler);
2153 if (oldICSInteractionTitle == NULL) {
2154 oldICSInteractionTitle = "xterm";
2157 printf("\033]0;%s\007", message);
2164 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2166 GtkWidget *w = (GtkWidget *) opt->handle;
2172 strcpy(bgcolor, "black");
2173 strcpy(fgcolor, "white");
2175 strcpy(bgcolor, "white");
2176 strcpy(fgcolor, "black");
2179 appData.lowTimeWarning &&
2180 (timer / 1000) < appData.icsAlarmTime) {
2181 strcpy(fgcolor, appData.lowTimeWarningColor);
2184 if (appData.clockMode) {
2185 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2186 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2188 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2189 bgcolor, fgcolor, color);
2191 gtk_label_set_markup(GTK_LABEL(w), markup);
2195 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2198 SetClockIcon (int color)
2200 GdkPixbuf *pm = *clockIcons[color];
2201 if (mainwindowIcon != pm) {
2202 mainwindowIcon = pm;
2203 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2207 #define INPUT_SOURCE_BUF_SIZE 8192
2216 char buf[INPUT_SOURCE_BUF_SIZE];
2221 DoInputCallback(io, cond, data)
2226 /* read input from one of the input source (for example a chess program, ICS, etc).
2227 * and call a function that will handle the input
2234 /* All information (callback function, file descriptor, etc) is
2235 * saved in an InputSource structure
2237 InputSource *is = (InputSource *) data;
2239 if (is->lineByLine) {
2240 count = read(is->fd, is->unused,
2241 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2243 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2246 is->unused += count;
2248 /* break input into lines and call the callback function on each
2251 while (p < is->unused) {
2252 q = memchr(p, '\n', is->unused - p);
2253 if (q == NULL) break;
2255 (is->func)(is, is->closure, p, q - p, 0);
2258 /* remember not yet used part of the buffer */
2260 while (p < is->unused) {
2265 /* read maximum length of input buffer and send the whole buffer
2266 * to the callback function
2268 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2273 (is->func)(is, is->closure, is->buf, count, error);
2275 return True; // Must return true or the watch will be removed
2278 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2285 GIOChannel *channel;
2286 ChildProc *cp = (ChildProc *) pr;
2288 is = (InputSource *) calloc(1, sizeof(InputSource));
2289 is->lineByLine = lineByLine;
2293 is->fd = fileno(stdin);
2295 is->kind = cp->kind;
2296 is->fd = cp->fdFrom;
2299 is->unused = is->buf;
2303 /* GTK-TODO: will this work on windows?*/
2305 channel = g_io_channel_unix_new(is->fd);
2306 g_io_channel_set_close_on_unref (channel, TRUE);
2307 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2309 is->closure = closure;
2310 return (InputSourceRef) is;
2315 RemoveInputSource(isr)
2318 InputSource *is = (InputSource *) isr;
2320 if (is->sid == 0) return;
2321 g_source_remove(is->sid);
2328 static Boolean frameWaiting;
2331 FrameAlarm (int sig)
2333 frameWaiting = False;
2334 /* In case System-V style signals. Needed?? */
2335 signal(SIGALRM, FrameAlarm);
2339 FrameDelay (int time)
2341 struct itimerval delay;
2344 frameWaiting = True;
2345 signal(SIGALRM, FrameAlarm);
2346 delay.it_interval.tv_sec =
2347 delay.it_value.tv_sec = time / 1000;
2348 delay.it_interval.tv_usec =
2349 delay.it_value.tv_usec = (time % 1000) * 1000;
2350 setitimer(ITIMER_REAL, &delay, NULL);
2351 while (frameWaiting) pause();
2352 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2353 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2354 setitimer(ITIMER_REAL, &delay, NULL);
2361 FrameDelay (int time)
2364 XSync(xDisplay, False);
2366 // gtk_main_iteration_do(False);
2369 usleep(time * 1000);
2375 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2377 char buf[MSG_SIZ], *logoName = buf;
2378 if(appData.logo[n][0]) {
2379 logoName = appData.logo[n];
2380 } else if(appData.autoLogo) {
2381 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2382 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2383 } else if(appData.directory[n] && appData.directory[n][0]) {
2384 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2388 { ASSIGN(cps->programLogo, logoName); }
2392 UpdateLogos (int displ)
2394 if(optList[W_WHITE-1].handle == NULL) return;
2395 LoadLogo(&first, 0, 0);
2396 LoadLogo(&second, 1, appData.icsActive);
2397 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2401 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2412 GtkFileFilter *gtkfilter;
2413 GtkFileFilter *gtkfilter_all;
2415 char fileext[10] = "";
2416 char *result = NULL;
2419 /* make a copy of the filter string, so that strtok can work with it*/
2420 cp = strndup(filter,strlen(filter));
2422 /* add filters for file extensions */
2423 gtkfilter = gtk_file_filter_new();
2424 gtkfilter_all = gtk_file_filter_new();
2426 /* one filter to show everything */
2427 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2428 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2430 /* add filter if present */
2431 result = strtok(cp, space);
2432 while( result != NULL ) {
2433 snprintf(fileext,10,"*%s",result);
2434 result = strtok( NULL, space );
2435 gtk_file_filter_add_pattern(gtkfilter, fileext);
2438 /* second filter to only show what's useful */
2439 gtk_file_filter_set_name (gtkfilter,filter);
2441 if (openMode[0] == 'r')
2443 dialog = gtk_file_chooser_dialog_new (label,
2445 GTK_FILE_CHOOSER_ACTION_OPEN,
2446 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2447 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2452 dialog = gtk_file_chooser_dialog_new (label,
2454 GTK_FILE_CHOOSER_ACTION_SAVE,
2455 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2456 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2458 /* add filename suggestions */
2459 if (strlen(def) > 0 )
2460 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2462 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2466 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2467 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2468 /* activate filter */
2469 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2471 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2476 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2479 f = fopen(filename, openMode);
2482 DisplayError(_("Failed to open file"), errno);
2486 /* TODO add indec */
2488 ASSIGN(*name, filename);
2489 ScheduleDelayedEvent(DelayedLoad, 50);
2494 gtk_widget_destroy (dialog);