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(());
228 * XBoard depends on Xt R4 or higher
230 int xtVersion = XtSpecificationRelease;
235 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
236 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
237 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
239 XFontSet fontSet, clockFontSet;
242 XFontStruct *clockFontStruct;
244 Font coordFontID, countFontID;
245 XFontStruct *coordFontStruct, *countFontStruct;
246 XtAppContext appContext;
248 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
250 GtkWidget *mainwindow;
252 Option *optList; // contains all widgets of main window
255 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
258 static GdkPixbuf *mainwindowIcon=NULL;
259 static GdkPixbuf *WhiteIcon=NULL;
260 static GdkPixbuf *BlackIcon=NULL;
262 typedef unsigned int BoardSize;
264 Boolean chessProgram;
266 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
267 int smallLayout = 0, tinyLayout = 0,
268 marginW, marginH, // [HGM] for run-time resizing
269 fromX = -1, fromY = -1, toX, toY, commentUp = False,
270 errorExitStatus = -1, defaultLineGap;
272 Dimension textHeight;
273 Pixel timerForegroundPixel, timerBackgroundPixel;
274 Pixel buttonForegroundPixel, buttonBackgroundPixel;
276 char *chessDir, *programName, *programVersion;
277 Boolean alwaysOnTop = False;
278 char *icsTextMenuString;
280 char *firstChessProgramNames;
281 char *secondChessProgramNames;
283 WindowPlacement wpMain;
284 WindowPlacement wpConsole;
285 WindowPlacement wpComment;
286 WindowPlacement wpMoveHistory;
287 WindowPlacement wpEvalGraph;
288 WindowPlacement wpEngineOutput;
289 WindowPlacement wpGameList;
290 WindowPlacement wpTags;
292 /* This magic number is the number of intermediate frames used
293 in each half of the animation. For short moves it's reduced
294 by 1. The total number of frames will be factor * 2 + 1. */
297 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
304 DropMenuEnables dmEnables[] = {
322 XtResource clientResources[] = {
323 { "flashCount", "flashCount", XtRInt, sizeof(int),
324 XtOffset(AppDataPtr, flashCount), XtRImmediate,
325 (XtPointer) FLASH_COUNT },
328 XrmOptionDescRec shellOptions[] = {
329 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
330 { "-flash", "flashCount", XrmoptionNoArg, "3" },
331 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
334 XtActionsRec boardActions[] = {
335 { "DrawPosition", DrawPositionProc },
336 { "HandlePV", HandlePV },
337 { "SelectPV", SelectPV },
338 { "StopPV", StopPV },
339 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
340 { "QuitProc", QuitWrapper },
341 { "ManProc", ManInner },
342 { "TempBackwardProc", TempBackwardProc },
343 { "TempForwardProc", TempForwardProc },
344 { "CommentClick", (XtActionProc) CommentClick },
345 { "GenericPopDown", (XtActionProc) GenericPopDown },
346 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
347 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
348 { "SelectMove", (XtActionProc) SelectMove },
349 { "LoadSelectedProc", LoadSelectedProc },
350 { "SetFilterProc", SetFilterProc },
351 { "TypeInProc", TypeInProc },
352 { "EnterKeyProc", EnterKeyProc },
353 { "UpKeyProc", UpKeyProc },
354 { "DownKeyProc", DownKeyProc },
355 { "WheelProc", WheelProc },
356 { "TabProc", TabProc },
360 char globalTranslations[] =
361 ":<Key>F9: MenuItem(Actions.Resign) \n \
362 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
363 :Meta<Key>V: MenuItem(File.NewVariant) \n \
364 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
365 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
366 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
367 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
368 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
369 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
370 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
371 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
372 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
373 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
374 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
375 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
376 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
377 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
378 :Ctrl<Key>q: MenuItem(File.Quit) \n \
379 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
380 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
381 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
382 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
383 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
384 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
385 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
386 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
387 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
388 :Meta<Key>G: MenuItem(View.GameList) \n \
389 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
390 :<Key>Pause: MenuItem(Mode.Pause) \n \
391 :<Key>F3: MenuItem(Action.Accept) \n \
392 :<Key>F4: MenuItem(Action.Decline) \n \
393 :<Key>F12: MenuItem(Action.Rematch) \n \
394 :<Key>F5: MenuItem(Action.CallFlag) \n \
395 :<Key>F6: MenuItem(Action.Draw) \n \
396 :<Key>F7: MenuItem(Action.Adjourn) \n \
397 :<Key>F8: MenuItem(Action.Abort) \n \
398 :<Key>F10: MenuItem(Action.StopObserving) \n \
399 :<Key>F11: MenuItem(Action.StopExamining) \n \
400 :Ctrl<Key>d: MenuItem(DebugProc) \n \
401 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
402 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
403 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
404 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
405 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
406 :<Key>Left: MenuItem(Edit.Backward) \n \
407 :<Key>Right: MenuItem(Edit.Forward) \n \
408 :<Key>Home: MenuItem(Edit.Revert) \n \
409 :<Key>End: MenuItem(Edit.TruncateGame) \n \
410 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
411 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
412 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
413 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
414 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
415 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
416 #ifndef OPTIONSDIALOG
418 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
419 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
420 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
421 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
422 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
425 :<Key>F1: MenuItem(Help.ManXBoard) \n \
426 :<Key>F2: MenuItem(View.FlipView) \n \
427 :<KeyDown>Return: TempBackwardProc() \n \
428 :<KeyUp>Return: TempForwardProc() \n";
430 char ICSInputTranslations[] =
431 "<Key>Up: UpKeyProc() \n "
432 "<Key>Down: DownKeyProc() \n "
433 "<Key>Return: EnterKeyProc() \n";
435 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
436 // as the widget is destroyed before the up-click can call extend-end
437 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
440 String xboardResources[] = {
441 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
446 /* Max possible square size */
447 #define MAXSQSIZE 256
449 static int xpm_avail[MAXSQSIZE];
451 #ifdef HAVE_DIR_STRUCT
453 /* Extract piece size from filename */
455 xpm_getsize (char *name, int len, char *ext)
463 if ((p=strchr(name, '.')) == NULL ||
464 StrCaseCmp(p+1, ext) != 0)
470 while (*p && isdigit(*p))
477 /* Setup xpm_avail */
479 xpm_getavail (char *dirname, char *ext)
485 for (i=0; i<MAXSQSIZE; ++i)
488 if (appData.debugMode)
489 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
491 dir = opendir(dirname);
494 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
495 programName, dirname);
499 while ((ent=readdir(dir)) != NULL) {
500 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
501 if (i > 0 && i < MAXSQSIZE)
511 xpm_print_avail (FILE *fp, char *ext)
515 fprintf(fp, _("Available `%s' sizes:\n"), ext);
516 for (i=1; i<MAXSQSIZE; ++i) {
522 /* Return XPM piecesize closest to size */
524 xpm_closest_to (char *dirname, int size, char *ext)
527 int sm_diff = MAXSQSIZE;
531 xpm_getavail(dirname, ext);
533 if (appData.debugMode)
534 xpm_print_avail(stderr, ext);
536 for (i=1; i<MAXSQSIZE; ++i) {
539 diff = (diff<0) ? -diff : diff;
540 if (diff < sm_diff) {
548 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
554 #else /* !HAVE_DIR_STRUCT */
555 /* If we are on a system without a DIR struct, we can't
556 read the directory, so we can't collect a list of
557 filenames, etc., so we can't do any size-fitting. */
559 xpm_closest_to (char *dirname, int size, char *ext)
562 Warning: No DIR structure found on this system --\n\
563 Unable to autosize for XPM/XIM pieces.\n\
564 Please report this error to %s.\n\
565 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
568 #endif /* HAVE_DIR_STRUCT */
572 /* Arrange to catch delete-window events */
573 Atom wm_delete_window;
575 CatchDeleteWindow (Widget w, String procname)
578 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
579 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
580 XtAugmentTranslations(w, XtParseTranslationTable(buf));
587 gtk_window_present(GTK_WINDOW(mainwindow));
590 //---------------------------------------------------------------------------------------------------------
591 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
594 #define CW_USEDEFAULT (1<<31)
595 #define ICS_TEXT_MENU_SIZE 90
596 #define DEBUG_FILE "xboard.debug"
597 #define SetCurrentDirectory chdir
598 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
602 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
605 // front-end part of option handling
607 // [HGM] This platform-dependent table provides the location for storing the color info
608 extern char *crWhite, * crBlack;
612 &appData.whitePieceColor,
613 &appData.blackPieceColor,
614 &appData.lightSquareColor,
615 &appData.darkSquareColor,
616 &appData.highlightSquareColor,
617 &appData.premoveHighlightColor,
618 &appData.lowTimeWarningColor,
629 // [HGM] font: keep a font for each square size, even non-stndard ones
632 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
633 char *fontTable[NUM_FONTS][MAX_SIZE];
636 ParseFont (char *name, int number)
637 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
639 if(sscanf(name, "size%d:", &size)) {
640 // [HGM] font: font is meant for specific boardSize (likely from settings file);
641 // defer processing it until we know if it matches our board size
642 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
643 fontTable[number][size] = strdup(strchr(name, ':')+1);
644 fontValid[number][size] = True;
649 case 0: // CLOCK_FONT
650 appData.clockFont = strdup(name);
652 case 1: // MESSAGE_FONT
653 appData.font = strdup(name);
655 case 2: // COORD_FONT
656 appData.coordFont = strdup(name);
661 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
666 { // only 2 fonts currently
667 appData.clockFont = CLOCK_FONT_NAME;
668 appData.coordFont = COORD_FONT_NAME;
669 appData.font = DEFAULT_FONT_NAME;
674 { // no-op, until we identify the code for this already in XBoard and move it here
678 ParseColor (int n, char *name)
679 { // in XBoard, just copy the color-name string
680 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
684 ParseTextAttribs (ColorClass cc, char *s)
686 (&appData.colorShout)[cc] = strdup(s);
690 ParseBoardSize (void *addr, char *name)
692 appData.boardSize = strdup(name);
697 { // In XBoard the sound-playing program takes care of obtaining the actual sound
701 SetCommPortDefaults ()
702 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
705 // [HGM] args: these three cases taken out to stay in front-end
707 SaveFontArg (FILE *f, ArgDescriptor *ad)
710 int i, n = (int)(intptr_t)ad->argLoc;
712 case 0: // CLOCK_FONT
713 name = appData.clockFont;
715 case 1: // MESSAGE_FONT
718 case 2: // COORD_FONT
719 name = appData.coordFont;
724 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
725 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
726 fontTable[n][squareSize] = strdup(name);
727 fontValid[n][squareSize] = True;
730 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
731 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
736 { // nothing to do, as the sounds are at all times represented by their text-string names already
740 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
741 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
742 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
746 SaveColor (FILE *f, ArgDescriptor *ad)
747 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
748 if(colorVariable[(int)(intptr_t)ad->argLoc])
749 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
753 SaveBoardSize (FILE *f, char *name, void *addr)
754 { // wrapper to shield back-end from BoardSize & sizeInfo
755 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
759 ParseCommPortSettings (char *s)
760 { // no such option in XBoard (yet)
767 GetActualPlacement (Widget wg, WindowPlacement *wp)
769 XWindowAttributes winAt;
776 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
777 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
778 wp->x = rx - winAt.x;
779 wp->y = ry - winAt.y;
780 wp->height = winAt.height;
781 wp->width = winAt.width;
782 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
788 { // wrapper to shield use of window handles from back-end (make addressible by number?)
789 // In XBoard this will have to wait until awareness of window parameters is implemented
791 GetActualPlacement(shellWidget, &wpMain);
792 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
793 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
794 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
795 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
796 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
797 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
802 PrintCommPortSettings (FILE *f, char *name)
803 { // This option does not exist in XBoard
807 EnsureOnScreen (int *x, int *y, int minX, int minY)
814 { // [HGM] args: allows testing if main window is realized from back-end
816 return xBoardWindow != 0;
823 PopUpStartupDialog ()
824 { // start menu not implemented in XBoard
828 ConvertToLine (int argc, char **argv)
830 static char line[128*1024], buf[1024];
834 for(i=1; i<argc; i++)
836 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
837 && argv[i][0] != '{' )
838 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
840 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
841 strncat(line, buf, 128*1024 - strlen(line) - 1 );
844 line[strlen(line)-1] = NULLCHAR;
848 //--------------------------------------------------------------------------------------------
851 ResizeBoardWindow (int w, int h, int inhibit)
854 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
856 shellArgs[0].value = w;
857 shellArgs[1].value = h;
858 shellArgs[4].value = shellArgs[2].value = w;
859 shellArgs[5].value = shellArgs[3].value = h;
860 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
862 XSync(xDisplay, False);
868 MakeOneColor (char *name, Pixel *color)
871 if (!appData.monoMode) {
872 vFrom.addr = (caddr_t) name;
873 vFrom.size = strlen(name);
874 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
875 if (vTo.addr == NULL) {
876 appData.monoMode = True;
879 *color = *(Pixel *) vTo.addr;
888 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
889 int forceMono = False;
892 if (appData.lowTimeWarning)
893 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
894 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
895 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
902 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
903 { // detervtomine what fonts to use, and create them
908 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
909 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
910 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
911 appData.font = fontTable[MESSAGE_FONT][squareSize];
912 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
913 appData.coordFont = fontTable[COORD_FONT][squareSize];
916 appData.font = InsertPxlSize(appData.font, fontPxlSize);
917 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
918 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
919 fontSet = CreateFontSet(appData.font);
920 clockFontSet = CreateFontSet(appData.clockFont);
922 /* For the coordFont, use the 0th font of the fontset. */
923 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
924 XFontStruct **font_struct_list;
925 XFontSetExtents *fontSize;
926 char **font_name_list;
927 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
928 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
929 coordFontStruct = XQueryFont(xDisplay, coordFontID);
930 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
931 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
934 appData.font = FindFont(appData.font, fontPxlSize);
935 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
936 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
937 clockFontID = XLoadFont(xDisplay, appData.clockFont);
938 clockFontStruct = XQueryFont(xDisplay, clockFontID);
939 coordFontID = XLoadFont(xDisplay, appData.coordFont);
940 coordFontStruct = XQueryFont(xDisplay, coordFontID);
941 // textHeight in !NLS mode!
943 countFontID = coordFontID; // [HGM] holdings
944 countFontStruct = coordFontStruct;
946 xdb = XtDatabase(xDisplay);
948 XrmPutLineResource(&xdb, "*international: True");
949 vTo.size = sizeof(XFontSet);
950 vTo.addr = (XtPointer) &fontSet;
951 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
953 XrmPutStringResource(&xdb, "*font", appData.font);
964 case ArgInt: p = " N"; break;
965 case ArgString: p = " STR"; break;
966 case ArgBoolean: p = " TF"; break;
967 case ArgSettingsFilename:
968 case ArgFilename: p = " FILE"; break;
969 case ArgX: p = " Nx"; break;
970 case ArgY: p = " Ny"; break;
971 case ArgAttribs: p = " TEXTCOL"; break;
972 case ArgColor: p = " COL"; break;
973 case ArgFont: p = " FONT"; break;
974 case ArgBoardSize: p = " SIZE"; break;
975 case ArgFloat: p = " FLOAT"; break;
980 case ArgCommSettings:
991 ArgDescriptor *q, *p = argDescriptors+5;
992 printf("\nXBoard accepts the following options:\n"
993 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
994 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
995 " SIZE = board-size spec(s)\n"
996 " Within parentheses are short forms, or options to set to true or false.\n"
997 " Persistent options (saved in the settings file) are marked with *)\n\n");
999 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1000 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1001 if(p->save) strcat(buf+len, "*");
1002 for(q=p+1; q->argLoc == p->argLoc; q++) {
1003 if(q->argName[0] == '-') continue;
1004 strcat(buf+len, q == p+1 ? " (" : " ");
1005 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1007 if(q != p+1) strcat(buf+len, ")");
1009 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1012 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1016 main (int argc, char **argv)
1018 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1020 XSetWindowAttributes window_attributes;
1022 Dimension boardWidth, boardHeight, w, h;
1025 int boardWidth, boardHeight, w, h;
1027 int forceMono = False;
1028 GError *gtkerror=NULL;
1030 srandom(time(0)); // [HGM] book: make random truly random
1032 setbuf(stdout, NULL);
1033 setbuf(stderr, NULL);
1036 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1037 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1041 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1047 gtk_init (&argc, &argv);
1049 programName = strrchr(argv[0], '/');
1050 if (programName == NULL)
1051 programName = argv[0];
1056 // if (appData.debugMode) {
1057 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1060 bindtextdomain(PACKAGE, LOCALEDIR);
1061 textdomain(PACKAGE);
1064 appData.boardSize = "";
1065 InitAppData(ConvertToLine(argc, argv));
1067 if (p == NULL) p = "/tmp";
1068 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1069 gameCopyFilename = (char*) malloc(i);
1070 gamePasteFilename = (char*) malloc(i);
1071 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1072 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1074 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1075 static char buf[MSG_SIZ];
1076 EscapeExpand(buf, appData.firstInitString);
1077 appData.firstInitString = strdup(buf);
1078 EscapeExpand(buf, appData.secondInitString);
1079 appData.secondInitString = strdup(buf);
1080 EscapeExpand(buf, appData.firstComputerString);
1081 appData.firstComputerString = strdup(buf);
1082 EscapeExpand(buf, appData.secondComputerString);
1083 appData.secondComputerString = strdup(buf);
1086 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1089 if (chdir(chessDir) != 0) {
1090 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1096 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1097 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1098 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1099 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1102 setbuf(debugFP, NULL);
1106 if (appData.debugMode) {
1107 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1111 /* [HGM,HR] make sure board size is acceptable */
1112 if(appData.NrFiles > BOARD_FILES ||
1113 appData.NrRanks > BOARD_RANKS )
1114 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1117 /* This feature does not work; animation needs a rewrite */
1118 appData.highlightDragging = FALSE;
1122 gameInfo.variant = StringToVariant(appData.variant);
1123 InitPosition(FALSE);
1127 builder = gtk_builder_new();
1128 filename = get_glade_filename ("mainboard.glade");
1129 if(! gtk_builder_add_from_file (builder, filename, >kerror) )
1132 printf ("Error: %d %s\n",gtkerror->code,gtkerror->message);
1134 mainwindow = GTK_WIDGET(gtk_builder_get_object (builder, "mainwindow"));
1137 XtAppInitialize(&appContext, "XBoard", shellOptions,
1138 XtNumber(shellOptions),
1139 &argc, argv, xboardResources, NULL, 0);
1141 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1142 clientResources, XtNumber(clientResources),
1145 xDisplay = XtDisplay(shellWidget);
1146 xScreen = DefaultScreen(xDisplay);
1147 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1151 * determine size, based on supplied or remembered -size, or screen size
1153 if (isdigit(appData.boardSize[0])) {
1154 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1155 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1156 &fontPxlSize, &smallLayout, &tinyLayout);
1158 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1159 programName, appData.boardSize);
1163 /* Find some defaults; use the nearest known size */
1164 SizeDefaults *szd, *nearest;
1165 int distance = 99999;
1166 nearest = szd = sizeDefaults;
1167 while (szd->name != NULL) {
1168 if (abs(szd->squareSize - squareSize) < distance) {
1170 distance = abs(szd->squareSize - squareSize);
1171 if (distance == 0) break;
1175 if (i < 2) lineGap = nearest->lineGap;
1176 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1177 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1178 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1179 if (i < 6) smallLayout = nearest->smallLayout;
1180 if (i < 7) tinyLayout = nearest->tinyLayout;
1183 SizeDefaults *szd = sizeDefaults;
1184 if (*appData.boardSize == NULLCHAR) {
1186 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1187 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1191 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
1192 guint screenwidth = gdk_screen_get_width(screen);
1193 guint screenheight = gdk_screen_get_height(screen);
1194 while (screenwidth < szd->minScreenSize ||
1195 screenheight < szd->minScreenSize) {
1199 if (szd->name == NULL) szd--;
1200 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1202 while (szd->name != NULL &&
1203 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1204 if (szd->name == NULL) {
1205 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1206 programName, appData.boardSize);
1210 squareSize = szd->squareSize;
1211 lineGap = szd->lineGap;
1212 clockFontPxlSize = szd->clockFontPxlSize;
1213 coordFontPxlSize = szd->coordFontPxlSize;
1214 fontPxlSize = szd->fontPxlSize;
1215 smallLayout = szd->smallLayout;
1216 tinyLayout = szd->tinyLayout;
1217 // [HGM] font: use defaults from settings file if available and not overruled
1220 defaultLineGap = lineGap;
1221 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1223 /* [HR] height treated separately (hacked) */
1224 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1225 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1228 * Determine what fonts to use.
1231 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1235 * Detect if there are not enough colors available and adapt.
1238 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1239 appData.monoMode = True;
1243 forceMono = MakeColors();
1246 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1248 appData.monoMode = True;
1251 ParseIcsTextColors();
1254 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1261 layoutName = "tinyLayout";
1262 } else if (smallLayout) {
1263 layoutName = "smallLayout";
1265 layoutName = "normalLayout";
1268 optList = BoardPopUp(squareSize, lineGap, (void*)
1278 InitDrawingHandle(optList + W_BOARD);
1279 currBoard = &optList[W_BOARD];
1280 boardWidget = optList[W_BOARD].handle;
1281 menuBarWidget = optList[W_MENU].handle;
1282 dropMenu = optList[W_DROP].handle;
1283 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1285 formWidget = XtParent(boardWidget);
1286 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1287 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1288 XtGetValues(optList[W_WHITE].handle, args, 2);
1289 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1290 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1291 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1292 XtGetValues(optList[W_PAUSE].handle, args, 2);
1297 xBoardWindow = XtWindow(boardWidget);
1300 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1301 // not need to go into InitDrawingSizes().
1306 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1308 WhiteIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1309 BlackIcon = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1310 mainwindowIcon = WhiteIcon;
1311 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1315 * Create a cursor for the board widget.
1318 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1319 XChangeWindowAttributes(xDisplay, xBoardWindow,
1320 CWCursor, &window_attributes);
1324 * Inhibit shell resizing.
1327 shellArgs[0].value = (XtArgVal) &w;
1328 shellArgs[1].value = (XtArgVal) &h;
1329 XtGetValues(shellWidget, shellArgs, 2);
1330 shellArgs[4].value = shellArgs[2].value = w;
1331 shellArgs[5].value = shellArgs[3].value = h;
1332 // XtSetValues(shellWidget, &shellArgs[2], 4);
1334 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1335 marginH = h - boardHeight;
1338 CatchDeleteWindow(shellWidget, "QuitProc");
1344 if(appData.logoSize)
1345 { // locate and read user logo
1347 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1348 ASSIGN(userLogo, buf);
1351 if (appData.animate || appData.animateDragging)
1355 XtAugmentTranslations(formWidget,
1356 XtParseTranslationTable(globalTranslations));
1358 XtAddEventHandler(formWidget, KeyPressMask, False,
1359 (XtEventHandler) MoveTypeInProc, NULL);
1360 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1361 (XtEventHandler) EventProc, NULL);
1363 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1365 /* [AS] Restore layout */
1366 if( wpMoveHistory.visible ) {
1370 if( wpEvalGraph.visible )
1375 if( wpEngineOutput.visible ) {
1376 EngineOutputPopUp();
1381 if (errorExitStatus == -1) {
1382 if (appData.icsActive) {
1383 /* We now wait until we see "login:" from the ICS before
1384 sending the logon script (problems with timestamp otherwise) */
1385 /*ICSInitScript();*/
1386 if (appData.icsInputBox) ICSInputBoxPopUp();
1390 signal(SIGWINCH, TermSizeSigHandler);
1392 signal(SIGINT, IntSigHandler);
1393 signal(SIGTERM, IntSigHandler);
1394 if (*appData.cmailGameName != NULLCHAR) {
1395 signal(SIGUSR1, CmailSigHandler);
1399 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1402 // XtSetKeyboardFocus(shellWidget, formWidget);
1404 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1407 /* check for GTK events and process them */
1410 gtk_main_iteration();
1413 if (appData.debugMode) fclose(debugFP); // [DM] debug
1418 TermSizeSigHandler (int sig)
1424 IntSigHandler (int sig)
1430 CmailSigHandler (int sig)
1435 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1437 /* Activate call-back function CmailSigHandlerCallBack() */
1438 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1440 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1444 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1447 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1449 /**** end signal code ****/
1452 #define Abs(n) ((n)<0 ? -(n) : (n))
1456 InsertPxlSize (char *pattern, int targetPxlSize)
1458 char *base_fnt_lst, strInt[12], *p, *q;
1459 int alternatives, i, len, strIntLen;
1462 * Replace the "*" (if present) in the pixel-size slot of each
1463 * alternative with the targetPxlSize.
1467 while ((p = strchr(p, ',')) != NULL) {
1471 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1472 strIntLen = strlen(strInt);
1473 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1477 while (alternatives--) {
1478 char *comma = strchr(p, ',');
1479 for (i=0; i<14; i++) {
1480 char *hyphen = strchr(p, '-');
1482 if (comma && hyphen > comma) break;
1483 len = hyphen + 1 - p;
1484 if (i == 7 && *p == '*' && len == 2) {
1486 memcpy(q, strInt, strIntLen);
1496 len = comma + 1 - p;
1503 return base_fnt_lst;
1508 CreateFontSet (char *base_fnt_lst)
1511 char **missing_list;
1515 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1516 &missing_list, &missing_count, &def_string);
1517 if (appData.debugMode) {
1519 XFontStruct **font_struct_list;
1520 char **font_name_list;
1521 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1523 fprintf(debugFP, " got list %s, locale %s\n",
1524 XBaseFontNameListOfFontSet(fntSet),
1525 XLocaleOfFontSet(fntSet));
1526 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1527 for (i = 0; i < count; i++) {
1528 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1531 for (i = 0; i < missing_count; i++) {
1532 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1535 if (fntSet == NULL) {
1536 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1542 #else // not ENABLE_NLS
1544 * Find a font that matches "pattern" that is as close as
1545 * possible to the targetPxlSize. Prefer fonts that are k
1546 * pixels smaller to fonts that are k pixels larger. The
1547 * pattern must be in the X Consortium standard format,
1548 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1549 * The return value should be freed with XtFree when no
1553 FindFont (char *pattern, int targetPxlSize)
1555 char **fonts, *p, *best, *scalable, *scalableTail;
1556 int i, j, nfonts, minerr, err, pxlSize;
1559 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1561 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1562 programName, pattern);
1569 for (i=0; i<nfonts; i++) {
1572 if (*p != '-') continue;
1574 if (*p == NULLCHAR) break;
1575 if (*p++ == '-') j++;
1577 if (j < 7) continue;
1580 scalable = fonts[i];
1583 err = pxlSize - targetPxlSize;
1584 if (Abs(err) < Abs(minerr) ||
1585 (minerr > 0 && err < 0 && -err == minerr)) {
1591 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1592 /* If the error is too big and there is a scalable font,
1593 use the scalable font. */
1594 int headlen = scalableTail - scalable;
1595 p = (char *) XtMalloc(strlen(scalable) + 10);
1596 while (isdigit(*scalableTail)) scalableTail++;
1597 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1599 p = (char *) XtMalloc(strlen(best) + 2);
1600 safeStrCpy(p, best, strlen(best)+1 );
1602 if (appData.debugMode) {
1603 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1604 pattern, targetPxlSize, p);
1606 XFreeFontNames(fonts);
1613 EnableNamedMenuItem (char *menuRef, int state)
1615 MenuItem *item = MenuNameToItem(menuRef);
1617 if(item) gtk_widget_set_sensitive(item->handle, state);
1621 EnableButtonBar (int state)
1624 XtSetSensitive(optList[W_BUTTON].handle, state);
1630 SetMenuEnables (Enables *enab)
1632 while (enab->name != NULL) {
1633 EnableNamedMenuItem(enab->name, enab->value);
1638 gboolean KeyPressProc(window, eventkey, data)
1640 GdkEventKey *eventkey;
1644 MoveTypeInProc(eventkey); // pop up for typed in moves
1647 /* check for other key values */
1648 switch(eventkey->keyval) {
1660 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1661 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1663 if(*nprms == 0) return;
1664 item = MenuNameToItem(prms[0]);
1665 if(item) ((MenuProc *) item->proc) ();
1679 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1680 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1681 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1682 dmEnables[i].piece);
1683 XtSetSensitive(entry, p != NULL || !appData.testLegality
1684 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1685 && !appData.icsActive));
1687 while (p && *p++ == dmEnables[i].piece) count++;
1688 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1690 XtSetArg(args[j], XtNlabel, label); j++;
1691 XtSetValues(entry, args, j);
1697 do_flash_delay (unsigned long msec)
1703 FlashDelay (int flash_delay)
1705 if(flash_delay) do_flash_delay(flash_delay);
1709 Fraction (int x, int start, int stop)
1711 double f = ((double) x - start)/(stop - start);
1712 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1716 static WindowPlacement wpNew;
1720 CoDrag (Widget sh, WindowPlacement *wp)
1723 int j=0, touch=0, fudge = 2;
1724 GetActualPlacement(sh, wp);
1725 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1726 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1727 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1728 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1729 if(!touch ) return; // only windows that touch co-move
1730 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1731 int heightInc = wpNew.height - wpMain.height;
1732 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1733 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1734 wp->y += fracTop * heightInc;
1735 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1736 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1737 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1738 int widthInc = wpNew.width - wpMain.width;
1739 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1740 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1741 wp->y += fracLeft * widthInc;
1742 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1743 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1745 wp->x += wpNew.x - wpMain.x;
1746 wp->y += wpNew.y - wpMain.y;
1747 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1748 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1750 XtSetArg(args[j], XtNx, wp->x); j++;
1751 XtSetArg(args[j], XtNy, wp->y); j++;
1752 XtSetValues(sh, args, j);
1757 ReSize (WindowPlacement *wp)
1760 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1761 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1762 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1763 if(sqy < sqx) sqx = sqy;
1764 if(sqx != squareSize) {
1765 squareSize = sqx; // adopt new square size
1766 CreatePNGPieces(); // make newly scaled pieces
1767 InitDrawingSizes(0, 0); // creates grid etc.
1768 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1769 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1770 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1771 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1772 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1776 static XtIntervalId delayedDragID = 0;
1778 static int delayedDragID = 0;
1788 GetActualPlacement(shellWidget, &wpNew);
1789 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1790 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1791 busy = 0; return; // false alarm
1794 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1795 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1796 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1797 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1799 DrawPosition(True, NULL);
1800 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1809 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1811 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1817 EventProc (Widget widget, caddr_t unused, XEvent *event)
1819 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1820 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1825 * event handler for redrawing the board
1829 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1831 DrawPosition(True, NULL);
1837 /* Disable all user input other than deleting the window */
1838 static int frozen = 0;
1844 /* Grab by a widget that doesn't accept input */
1845 gtk_grab_add(optList[W_MESSG].handle);
1849 /* Undo a FreezeUI */
1853 if (!frozen) return;
1854 gtk_grab_remove(optList[W_MESSG].handle);
1861 static int oldPausing = FALSE;
1862 static GameMode oldmode = (GameMode) -1;
1864 if (!boardWidget) return;
1866 if (pausing != oldPausing) {
1867 oldPausing = pausing;
1868 MarkMenuItem("Mode.Pause", pausing);
1870 if (appData.showButtonBar) {
1871 /* Always toggle, don't set. Previous code messes up when
1872 invoked while the button is pressed, as releasing it
1873 toggles the state again. */
1875 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1876 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1880 wname = ModeToWidgetName(oldmode);
1881 if (wname != NULL) {
1882 MarkMenuItem(wname, False);
1884 wname = ModeToWidgetName(gameMode);
1885 if (wname != NULL) {
1886 MarkMenuItem(wname, True);
1889 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1891 /* Maybe all the enables should be handled here, not just this one */
1892 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1894 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1899 * Button/menu procedures
1903 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1904 char *selected_fen_position=NULL;
1907 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1908 Atom *type_return, XtPointer *value_return,
1909 unsigned long *length_return, int *format_return)
1911 char *selection_tmp;
1913 // if (!selected_fen_position) return False; /* should never happen */
1914 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1915 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1916 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1919 if (f == NULL) return False;
1923 selection_tmp = XtMalloc(len + 1);
1924 count = fread(selection_tmp, 1, len, f);
1927 XtFree(selection_tmp);
1930 selection_tmp[len] = NULLCHAR;
1932 /* note: since no XtSelectionDoneProc was registered, Xt will
1933 * automatically call XtFree on the value returned. So have to
1934 * make a copy of it allocated with XtMalloc */
1935 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1936 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1939 *value_return=selection_tmp;
1940 *length_return=strlen(selection_tmp);
1941 *type_return=*target;
1942 *format_return = 8; /* bits per byte */
1944 } else if (*target == XA_TARGETS(xDisplay)) {
1945 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1946 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1947 targets_tmp[1] = XA_STRING;
1948 *value_return = targets_tmp;
1949 *type_return = XA_ATOM;
1952 // This code leads to a read of value_return out of bounds on 64-bit systems.
1953 // Other code which I have seen always sets *format_return to 32 independent of
1954 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1955 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1956 *format_return = 8 * sizeof(Atom);
1957 if (*format_return > 32) {
1958 *length_return *= *format_return / 32;
1959 *format_return = 32;
1962 *format_return = 32;
1971 /* note: when called from menu all parameters are NULL, so no clue what the
1972 * Widget which was clicked on was, or what the click event was
1975 CopySomething (char *src)
1978 selected_fen_position = src;
1980 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1981 * have a notion of a position that is selected but not copied.
1982 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1984 XtOwnSelection(menuBarWidget, XA_PRIMARY,
1986 SendPositionSelection,
1987 NULL/* lose_ownership_proc */ ,
1988 NULL/* transfer_done_proc */);
1989 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
1991 SendPositionSelection,
1992 NULL/* lose_ownership_proc */ ,
1993 NULL/* transfer_done_proc */);
1998 /* function called when the data to Paste is ready */
2000 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2001 Atom *type, XtPointer value, unsigned long *len, int *format)
2004 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2005 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2006 EditPositionPasteFEN(fenstr);
2011 /* called when Paste Position button is pressed,
2012 * all parameters will be NULL */
2014 PastePositionProc ()
2017 XtGetSelectionValue(menuBarWidget,
2018 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2019 /* (XtSelectionCallbackProc) */ PastePositionCB,
2020 NULL, /* client_data passed to PastePositionCB */
2022 /* better to use the time field from the event that triggered the
2023 * call to this function, but that isn't trivial to get
2032 /* note: when called from menu all parameters are NULL, so no clue what the
2033 * Widget which was clicked on was, or what the click event was
2035 /* function called when the data to Paste is ready */
2037 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2038 Atom *type, XtPointer value, unsigned long *len, int *format)
2041 if (value == NULL || *len == 0) {
2042 return; /* nothing had been selected to copy */
2044 f = fopen(gamePasteFilename, "w");
2046 DisplayError(_("Can't open temp file"), errno);
2049 fwrite(value, 1, *len, f);
2052 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2056 /* called when Paste Game button is pressed,
2057 * all parameters will be NULL */
2062 XtGetSelectionValue(menuBarWidget,
2063 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2064 /* (XtSelectionCallbackProc) */ PasteGameCB,
2065 NULL, /* client_data passed to PasteGameCB */
2067 /* better to use the time field from the event that triggered the
2068 * call to this function, but that isn't trivial to get
2079 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2085 void MoveTypeInProc(eventkey)
2086 GdkEventKey *eventkey;
2090 // ingnore if ctrl or alt is pressed
2091 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
2095 buf[0]=eventkey->keyval;
2103 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2105 if (!TempBackwardActive) {
2106 TempBackwardActive = True;
2112 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2114 /* Check to see if triggered by a key release event for a repeating key.
2115 * If so the next queued event will be a key press of the same key at the same time */
2116 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2118 XPeekEvent(xDisplay, &next);
2119 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2120 next.xkey.keycode == event->xkey.keycode)
2124 TempBackwardActive = False;
2128 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2129 { // called as key binding
2132 if (nprms && *nprms > 0)
2136 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2143 { // called from menu
2145 ManInner(NULL, NULL, NULL, NULL);
2150 SetWindowTitle (char *text, char *title, char *icon)
2155 if (appData.titleInWindow) {
2157 XtSetArg(args[i], XtNlabel, text); i++;
2158 XtSetValues(titleWidget, args, i);
2161 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2162 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2163 XtSetValues(shellWidget, args, i);
2164 XSync(xDisplay, False);
2166 if (appData.titleInWindow) {
2167 SetWidgetLabel(titleWidget, text);
2169 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2174 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2180 DisplayIcsInteractionTitle (String message)
2183 if (oldICSInteractionTitle == NULL) {
2184 /* Magic to find the old window title, adapted from vim */
2185 char *wina = getenv("WINDOWID");
2187 Window win = (Window) atoi(wina);
2188 Window root, parent, *children;
2189 unsigned int nchildren;
2190 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2192 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2193 if (!XQueryTree(xDisplay, win, &root, &parent,
2194 &children, &nchildren)) break;
2195 if (children) XFree((void *)children);
2196 if (parent == root || parent == 0) break;
2199 XSetErrorHandler(oldHandler);
2201 if (oldICSInteractionTitle == NULL) {
2202 oldICSInteractionTitle = "xterm";
2205 printf("\033]0;%s\007", message);
2212 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2214 GtkWidget *w = (GtkWidget *) opt->handle;
2220 strcpy(bgcolor, "black");
2221 strcpy(fgcolor, "white");
2223 strcpy(bgcolor, "white");
2224 strcpy(fgcolor, "black");
2227 appData.lowTimeWarning &&
2228 (timer / 1000) < appData.icsAlarmTime) {
2229 strcpy(fgcolor, appData.lowTimeWarningColor);
2232 if (appData.clockMode) {
2233 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2234 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2236 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2237 bgcolor, fgcolor, color);
2239 gtk_label_set_markup(GTK_LABEL(w), markup);
2243 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2246 SetClockIcon (int color)
2248 GdkPixbuf *pm = *clockIcons[color];
2249 if (mainwindowIcon != pm) {
2250 mainwindowIcon = pm;
2251 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2255 #define INPUT_SOURCE_BUF_SIZE 8192
2264 char buf[INPUT_SOURCE_BUF_SIZE];
2269 DoInputCallback(io, cond, data)
2274 /* read input from one of the input source (for example a chess program, ICS, etc).
2275 * and call a function that will handle the input
2282 /* All information (callback function, file descriptor, etc) is
2283 * saved in an InputSource structure
2285 InputSource *is = (InputSource *) data;
2287 if (is->lineByLine) {
2288 count = read(is->fd, is->unused,
2289 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2291 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2294 is->unused += count;
2296 /* break input into lines and call the callback function on each
2299 while (p < is->unused) {
2300 q = memchr(p, '\n', is->unused - p);
2301 if (q == NULL) break;
2303 (is->func)(is, is->closure, p, q - p, 0);
2306 /* remember not yet used part of the buffer */
2308 while (p < is->unused) {
2313 /* read maximum length of input buffer and send the whole buffer
2314 * to the callback function
2316 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2321 (is->func)(is, is->closure, is->buf, count, error);
2323 return True; // Must return true or the watch will be removed
2326 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2333 GIOChannel *channel;
2334 ChildProc *cp = (ChildProc *) pr;
2336 is = (InputSource *) calloc(1, sizeof(InputSource));
2337 is->lineByLine = lineByLine;
2341 is->fd = fileno(stdin);
2343 is->kind = cp->kind;
2344 is->fd = cp->fdFrom;
2347 is->unused = is->buf;
2351 /* GTK-TODO: will this work on windows?*/
2353 channel = g_io_channel_unix_new(is->fd);
2354 g_io_channel_set_close_on_unref (channel, TRUE);
2355 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2357 is->closure = closure;
2358 return (InputSourceRef) is;
2363 RemoveInputSource(isr)
2366 InputSource *is = (InputSource *) isr;
2368 if (is->sid == 0) return;
2369 g_source_remove(is->sid);
2376 static Boolean frameWaiting;
2379 FrameAlarm (int sig)
2381 frameWaiting = False;
2382 /* In case System-V style signals. Needed?? */
2383 signal(SIGALRM, FrameAlarm);
2387 FrameDelay (int time)
2389 struct itimerval delay;
2392 frameWaiting = True;
2393 signal(SIGALRM, FrameAlarm);
2394 delay.it_interval.tv_sec =
2395 delay.it_value.tv_sec = time / 1000;
2396 delay.it_interval.tv_usec =
2397 delay.it_value.tv_usec = (time % 1000) * 1000;
2398 setitimer(ITIMER_REAL, &delay, NULL);
2399 while (frameWaiting) pause();
2400 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2401 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2402 setitimer(ITIMER_REAL, &delay, NULL);
2409 FrameDelay (int time)
2412 XSync(xDisplay, False);
2414 // gtk_main_iteration_do(False);
2417 usleep(time * 1000);
2423 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2425 char buf[MSG_SIZ], *logoName = buf;
2426 if(appData.logo[n][0]) {
2427 logoName = appData.logo[n];
2428 } else if(appData.autoLogo) {
2429 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2430 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2431 } else if(appData.directory[n] && appData.directory[n][0]) {
2432 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2436 { ASSIGN(cps->programLogo, logoName); }
2440 UpdateLogos (int displ)
2442 if(optList[W_WHITE-1].handle == NULL) return;
2443 LoadLogo(&first, 0, 0);
2444 LoadLogo(&second, 1, appData.icsActive);
2445 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2449 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2460 GtkFileFilter *gtkfilter;
2461 GtkFileFilter *gtkfilter_all;
2463 char fileext[10] = "";
2464 char *result = NULL;
2467 /* make a copy of the filter string, so that strtok can work with it*/
2468 cp = strndup(filter,strlen(filter));
2470 /* add filters for file extensions */
2471 gtkfilter = gtk_file_filter_new();
2472 gtkfilter_all = gtk_file_filter_new();
2474 /* one filter to show everything */
2475 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2476 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2478 /* add filter if present */
2479 result = strtok(cp, space);
2480 while( result != NULL ) {
2481 snprintf(fileext,10,"*%s",result);
2482 result = strtok( NULL, space );
2483 gtk_file_filter_add_pattern(gtkfilter, fileext);
2486 /* second filter to only show what's useful */
2487 gtk_file_filter_set_name (gtkfilter,filter);
2489 if (openMode[0] == 'r')
2491 dialog = gtk_file_chooser_dialog_new (label,
2493 GTK_FILE_CHOOSER_ACTION_OPEN,
2494 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2495 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2500 dialog = gtk_file_chooser_dialog_new (label,
2502 GTK_FILE_CHOOSER_ACTION_SAVE,
2503 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2504 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2506 /* add filename suggestions */
2507 if (strlen(def) > 0 )
2508 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2510 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2514 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2515 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2516 /* activate filter */
2517 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2519 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2524 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2527 f = fopen(filename, openMode);
2530 DisplayError(_("Failed to open file"), errno);
2534 /* TODO add indec */
2536 ASSIGN(*name, filename);
2537 ScheduleDelayedEvent(DelayedLoad, 50);
2542 gtk_widget_destroy (dialog);