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, 2013, 2014, 2015 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"
165 #include "engineoutput.h"
171 # include <gtkmacintegration-gtk2/gtkosxapplication.h>
172 // prevent pathname of positional file argument provided by OS X being be mistaken for option name
173 // (price is that we won't recognize Windows option format anymore).
176 // redefine some defaults
180 # undef SETTINGS_FILE
181 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
182 # define DATADIR dataDir
183 # define LOCALEDIR localeDir
184 # define SETTINGS_FILE masterSettings
185 # define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
186 char dataDir[MSG_SIZ]; // for expanding ~~
187 char localeDir[MSG_SIZ];
188 char masterSettings[MSG_SIZ];
192 # define SYNC_MENUBAR
199 #define usleep(t) _sleep2(((t)+500)/1000)
203 # define _(s) gettext (s)
204 # define N_(s) gettext_noop (s)
210 int main P((int argc, char **argv));
211 RETSIGTYPE CmailSigHandler P((int sig));
212 RETSIGTYPE IntSigHandler P((int sig));
213 RETSIGTYPE TermSizeSigHandler P((int sig));
214 char *InsertPxlSize P((char *pattern, int targetPxlSize));
216 XFontSet CreateFontSet P((char *base_fnt_lst));
218 char *FindFont P((char *pattern, int targetPxlSize));
220 void DelayedDrag P((void));
221 void ICSInputBoxPopUp P((void));
222 void MoveTypeInProc P((GdkEventKey *eventkey));
223 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
224 Boolean TempBackwardActive = False;
225 void DisplayMove P((int moveNumber));
226 void update_ics_width P(());
227 int CopyMemoProc P(());
228 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
229 static int FindLogo P((char *place, char *name, char *buf));
233 XFontSet fontSet, clockFontSet;
236 XFontStruct *clockFontStruct;
238 Font coordFontID, countFontID;
239 XFontStruct *coordFontStruct, *countFontStruct;
241 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
242 GtkWidget *mainwindow;
244 Option *optList; // contains all widgets of main window
247 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
250 static GdkPixbuf *mainwindowIcon=NULL;
251 static GdkPixbuf *WhiteIcon=NULL;
252 static GdkPixbuf *BlackIcon=NULL;
254 /* key board accelerators */
255 GtkAccelGroup *GtkAccelerators;
257 typedef unsigned int BoardSize;
259 Boolean chessProgram;
261 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
262 int smallLayout = 0, tinyLayout = 0,
263 marginW, marginH, // [HGM] for run-time resizing
264 fromX = -1, fromY = -1, toX, toY, commentUp = False,
265 errorExitStatus = -1, defaultLineGap;
267 Dimension textHeight;
269 char *chessDir, *programName, *programVersion;
270 Boolean alwaysOnTop = False;
271 char *icsTextMenuString;
273 char *firstChessProgramNames;
274 char *secondChessProgramNames;
276 WindowPlacement wpMain;
277 WindowPlacement wpConsole;
278 WindowPlacement wpComment;
279 WindowPlacement wpMoveHistory;
280 WindowPlacement wpEvalGraph;
281 WindowPlacement wpEngineOutput;
282 WindowPlacement wpGameList;
283 WindowPlacement wpTags;
284 WindowPlacement wpDualBoard;
286 /* This magic number is the number of intermediate frames used
287 in each half of the animation. For short moves it's reduced
288 by 1. The total number of frames will be factor * 2 + 1. */
291 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
298 DropMenuEnables dmEnables[] = {
307 XtResource clientResources[] = {
308 { "flashCount", "flashCount", XtRInt, sizeof(int),
309 XtOffset(AppDataPtr, flashCount), XtRImmediate,
310 (XtPointer) FLASH_COUNT },
314 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
315 char globalTranslations[] =
316 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
317 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
318 :<KeyDown>Return: TempBackwardProc() \n \
319 :<KeyUp>Return: TempForwardProc() \n";
321 char ICSInputTranslations[] =
322 "<Key>Up: UpKeyProc() \n "
323 "<Key>Down: DownKeyProc() \n "
324 "<Key>Return: EnterKeyProc() \n";
326 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
327 // as the widget is destroyed before the up-click can call extend-end
328 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
331 String xboardResources[] = {
332 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
340 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
343 //---------------------------------------------------------------------------------------------------------
344 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
347 #define CW_USEDEFAULT (1<<31)
348 #define ICS_TEXT_MENU_SIZE 90
349 #define DEBUG_FILE "xboard.debug"
350 #define SetCurrentDirectory chdir
351 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
355 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
358 // front-end part of option handling
360 // [HGM] This platform-dependent table provides the location for storing the color info
361 extern char *crWhite, * crBlack;
365 &appData.whitePieceColor,
366 &appData.blackPieceColor,
367 &appData.lightSquareColor,
368 &appData.darkSquareColor,
369 &appData.highlightSquareColor,
370 &appData.premoveHighlightColor,
371 &appData.lowTimeWarningColor,
382 // [HGM] font: keep a font for each square size, even non-stndard ones
385 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
386 char *fontTable[NUM_FONTS][MAX_SIZE];
389 ParseFont (char *name, int number)
390 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
392 if(sscanf(name, "size%d:", &size)) {
393 // [HGM] font: font is meant for specific boardSize (likely from settings file);
394 // defer processing it until we know if it matches our board size
395 if(!strstr(name, "-*-") && // ignore X-fonts
396 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
397 fontTable[number][size] = strdup(strchr(name, ':')+1);
398 fontValid[number][size] = True;
403 case 0: // CLOCK_FONT
404 appData.clockFont = strdup(name);
406 case 1: // MESSAGE_FONT
407 appData.font = strdup(name);
409 case 2: // COORD_FONT
410 appData.coordFont = strdup(name);
413 appData.icsFont = strdup(name);
416 appData.tagsFont = strdup(name);
419 appData.commentFont = strdup(name);
421 case MOVEHISTORY_FONT:
422 appData.historyFont = strdup(name);
425 appData.gameListFont = strdup(name);
430 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
435 { // only 2 fonts currently
436 appData.clockFont = strdup(CLOCK_FONT_NAME);
437 appData.coordFont = strdup(COORD_FONT_NAME);
438 appData.font = strdup(DEFAULT_FONT_NAME);
439 appData.icsFont = strdup(CONSOLE_FONT_NAME);
440 appData.tagsFont = strdup(TAGS_FONT_NAME);
441 appData.commentFont = strdup(COMMENT_FONT_NAME);
442 appData.historyFont = strdup(HISTORY_FONT_NAME);
443 appData.gameListFont = strdup(GAMELIST_FONT_NAME);
448 { // no-op, until we identify the code for this already in XBoard and move it here
452 ParseColor (int n, char *name)
453 { // in XBoard, just copy the color-name string
454 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
460 return *(char**)colorVariable[n];
464 ParseTextAttribs (ColorClass cc, char *s)
466 (&appData.colorShout)[cc] = strdup(s);
470 ParseBoardSize (void *addr, char *name)
472 appData.boardSize = strdup(name);
477 { // In XBoard the sound-playing program takes care of obtaining the actual sound
481 SetCommPortDefaults ()
482 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
485 // [HGM] args: these three cases taken out to stay in front-end
487 SaveFontArg (FILE *f, ArgDescriptor *ad)
490 int i, n = (int)(intptr_t)ad->argLoc;
492 case 0: // CLOCK_FONT
493 name = appData.clockFont;
495 case 1: // MESSAGE_FONT
498 case 2: // COORD_FONT
499 name = appData.coordFont;
502 name = appData.icsFont;
505 name = appData.tagsFont;
508 name = appData.commentFont;
510 case MOVEHISTORY_FONT:
511 name = appData.historyFont;
514 name = appData.gameListFont;
519 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
520 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
521 fontTable[n][squareSize] = strdup(name);
522 fontValid[n][squareSize] = True;
525 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
526 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
531 { // nothing to do, as the sounds are at all times represented by their text-string names already
535 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
536 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
537 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
541 SaveColor (FILE *f, ArgDescriptor *ad)
542 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
543 if(colorVariable[(int)(intptr_t)ad->argLoc])
544 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
548 SaveBoardSize (FILE *f, char *name, void *addr)
549 { // wrapper to shield back-end from BoardSize & sizeInfo
550 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
554 ParseCommPortSettings (char *s)
555 { // no such option in XBoard (yet)
561 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
565 gtk_widget_get_allocation(shell, &a);
566 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
570 wp->height = a.height;
571 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
572 frameX = 3; frameY = 3; // remember to decide if windows touch
576 GetPlacement (DialogClass dlg, WindowPlacement *wp)
577 { // wrapper to shield back-end from widget type
578 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
583 { // wrapper to shield use of window handles from back-end (make addressible by number?)
584 // In XBoard this will have to wait until awareness of window parameters is implemented
585 GetActualPlacement(shellWidget, &wpMain);
586 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
587 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
588 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
589 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
590 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
591 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
592 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
596 PrintCommPortSettings (FILE *f, char *name)
597 { // This option does not exist in XBoard
601 EnsureOnScreen (int *x, int *y, int minX, int minY)
608 { // [HGM] args: allows testing if main window is realized from back-end
609 return DialogExists(BoardWindow);
613 PopUpStartupDialog ()
614 { // start menu not implemented in XBoard
618 ConvertToLine (int argc, char **argv)
620 static char line[128*1024], buf[1024];
624 for(i=1; i<argc; i++)
626 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
627 && argv[i][0] != '{' )
628 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
630 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
631 strncat(line, buf, 128*1024 - strlen(line) - 1 );
634 line[strlen(line)-1] = NULLCHAR;
638 //--------------------------------------------------------------------------------------------
643 ResizeBoardWindow (int w, int h, int inhibit)
647 // if(clockKludge) return; // ignore as long as clock does not have final height
648 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
650 gtk_widget_get_allocation(shellWidget, &a);
651 marginW = a.width - bw;
652 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
653 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
654 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
655 // h += marginH + a.height + 1;
656 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
658 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board again
663 { // dummy, as the GTK code does not make colors in advance
668 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
669 { // determine what fonts to use, and create them
671 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
672 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
673 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
674 appData.font = fontTable[MESSAGE_FONT][squareSize];
675 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
676 appData.coordFont = fontTable[COORD_FONT][squareSize];
677 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
678 appData.icsFont = fontTable[CONSOLE_FONT][squareSize];
679 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
680 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize];
681 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
682 appData.commentFont = fontTable[COMMENT_FONT][squareSize];
683 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
684 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize];
685 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
686 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize];
688 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
689 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
690 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
691 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
692 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
693 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
694 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
695 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
701 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
702 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
703 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
704 appData.font = fontTable[MESSAGE_FONT][squareSize];
705 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
706 appData.coordFont = fontTable[COORD_FONT][squareSize];
709 appData.font = InsertPxlSize(appData.font, fontPxlSize);
710 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
711 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
712 fontSet = CreateFontSet(appData.font);
713 clockFontSet = CreateFontSet(appData.clockFont);
715 /* For the coordFont, use the 0th font of the fontset. */
716 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
717 XFontStruct **font_struct_list;
718 XFontSetExtents *fontSize;
719 char **font_name_list;
720 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
721 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
722 coordFontStruct = XQueryFont(xDisplay, coordFontID);
723 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
724 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
727 appData.font = FindFont(appData.font, fontPxlSize);
728 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
729 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
730 clockFontID = XLoadFont(xDisplay, appData.clockFont);
731 clockFontStruct = XQueryFont(xDisplay, clockFontID);
732 coordFontID = XLoadFont(xDisplay, appData.coordFont);
733 coordFontStruct = XQueryFont(xDisplay, coordFontID);
734 // textHeight in !NLS mode!
736 countFontID = coordFontID; // [HGM] holdings
737 countFontStruct = coordFontStruct;
739 xdb = XtDatabase(xDisplay);
741 XrmPutLineResource(&xdb, "*international: True");
742 vTo.size = sizeof(XFontSet);
743 vTo.addr = (XtPointer) &fontSet;
744 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
746 XrmPutStringResource(&xdb, "*font", appData.font);
757 case ArgInt: p = " N"; break;
758 case ArgString: p = " STR"; break;
759 case ArgBoolean: p = " TF"; break;
760 case ArgSettingsFilename:
761 case ArgBackupSettingsFile:
762 case ArgFilename: p = " FILE"; break;
763 case ArgX: p = " Nx"; break;
764 case ArgY: p = " Ny"; break;
765 case ArgAttribs: p = " TEXTCOL"; break;
766 case ArgColor: p = " COL"; break;
767 case ArgFont: p = " FONT"; break;
768 case ArgBoardSize: p = " SIZE"; break;
769 case ArgFloat: p = " FLOAT"; break;
774 case ArgCommSettings:
786 ArgDescriptor *q, *p = argDescriptors+5;
787 printf("\nXBoard accepts the following options:\n"
788 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
789 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
790 " SIZE = board-size spec(s)\n"
791 " Within parentheses are short forms, or options to set to true or false.\n"
792 " Persistent options (saved in the settings file) are marked with *)\n\n");
794 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
795 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
796 if(p->save) strcat(buf+len, "*");
797 for(q=p+1; q->argLoc == p->argLoc; q++) {
798 if(q->argName[0] == '-') continue;
799 strcat(buf+len, q == p+1 ? " (" : " ");
800 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
802 if(q != p+1) strcat(buf+len, ")");
804 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
807 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
811 SlaveResize (Option *opt)
813 static int slaveW, slaveH, w, h;
816 gtk_widget_get_allocation(shells[DummyDlg], &a);
817 w = a.width; h = a.height;
818 gtk_widget_get_allocation(opt->handle, &a);
819 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
820 slaveH = h - a.height + 13;
822 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
826 LoadIconFile (gchar *svgFilename)
830 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
831 return gdk_pixbuf_new_from_file(buf, NULL);
835 static char clickedFile[MSG_SIZ];
839 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
840 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
841 if(suppress) { // we just started XBoard without arguments
842 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
843 } else { // we are running something presumably useful
845 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
846 system(buf); // start new instance on this file
851 GtkosxApplication *theApp;
855 main (int argc, char **argv)
857 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
858 int boardWidth, w, h; //, boardHeight;
860 int forceMono = False;
862 srandom(time(0)); // [HGM] book: make random truly random
864 setbuf(stdout, NULL);
865 setbuf(stderr, NULL);
868 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
869 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
873 if(argc > 1 && !strcmp(argv[1], "--help" )) {
879 gtk_init (&argc, &argv);
881 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
882 char *path = gtkosx_application_get_bundle_path();
884 char *res_path = gtkosx_application_get_resource_path();
885 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
887 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
888 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
889 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
890 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
891 suppress = (argc == 1 || argc > 1 && argv[1][00] != '-'); // OSX sends signal even if name was already argv[1]!
892 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
893 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
894 // we must call application ready before we can get the signal,
895 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
896 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
897 gtkosx_application_ready(theApp);
898 if(argc == 1) { // called without args: OSX open-file signal might follow
899 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
900 usleep(10000); // wait 10 msec (and hope this is long enough).
901 while(gtk_events_pending())
902 gtk_main_iteration(); // process all events that came in upto now
903 suppress = 0; // future open-file signals should start new instance
904 if(clickedFile[0]) { // we were sent an open-file signal with filename!
905 fakeArgv[0] = argv[0];
906 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
912 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
913 typedef struct {char *name, *value; } Config;
914 static Config configList[] = {
915 { "Datadir", DATADIR },
916 { "Sysconfdir", SYSCONFDIR },
921 for(i=0; configList[i].name; i++) {
922 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
923 if(argc > 2) printf("%s", configList[i].value);
924 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
929 /* set up keyboard accelerators group */
930 GtkAccelerators = gtk_accel_group_new();
932 programName = strrchr(argv[0], '/');
933 if (programName == NULL)
934 programName = argv[0];
939 // if (appData.debugMode) {
940 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
943 bindtextdomain(PACKAGE, LOCALEDIR);
944 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
948 appData.boardSize = "";
949 InitAppData(ConvertToLine(argc, argv));
951 if (p == NULL) p = "/tmp";
952 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
953 gameCopyFilename = (char*) malloc(i);
954 gamePasteFilename = (char*) malloc(i);
955 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
956 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
958 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
959 static char buf[MSG_SIZ];
960 snprintf(buf, MSG_SIZ, appData.sysOpen, DATADIR);
961 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
962 EscapeExpand(buf, appData.firstInitString);
963 appData.firstInitString = strdup(buf);
964 EscapeExpand(buf, appData.secondInitString);
965 appData.secondInitString = strdup(buf);
966 EscapeExpand(buf, appData.firstComputerString);
967 appData.firstComputerString = strdup(buf);
968 EscapeExpand(buf, appData.secondComputerString);
969 appData.secondComputerString = strdup(buf);
972 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
975 if (chdir(chessDir) != 0) {
976 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
982 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
983 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
984 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
985 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
988 setbuf(debugFP, NULL);
992 if (appData.debugMode) {
993 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
997 /* [HGM,HR] make sure board size is acceptable */
998 if(appData.NrFiles > BOARD_FILES ||
999 appData.NrRanks > BOARD_RANKS )
1000 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1003 /* This feature does not work; animation needs a rewrite */
1004 appData.highlightDragging = FALSE;
1008 gameInfo.variant = StringToVariant(appData.variant);
1009 InitPosition(FALSE);
1012 * determine size, based on supplied or remembered -size, or screen size
1014 if (isdigit(appData.boardSize[0])) {
1015 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1016 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1017 &fontPxlSize, &smallLayout, &tinyLayout);
1019 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1020 programName, appData.boardSize);
1024 /* Find some defaults; use the nearest known size */
1025 SizeDefaults *szd, *nearest;
1026 int distance = 99999;
1027 nearest = szd = sizeDefaults;
1028 while (szd->name != NULL) {
1029 if (abs(szd->squareSize - squareSize) < distance) {
1031 distance = abs(szd->squareSize - squareSize);
1032 if (distance == 0) break;
1036 if (i < 2) lineGap = nearest->lineGap;
1037 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1038 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1039 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1040 if (i < 6) smallLayout = nearest->smallLayout;
1041 if (i < 7) tinyLayout = nearest->tinyLayout;
1044 SizeDefaults *szd = sizeDefaults;
1045 if (*appData.boardSize == NULLCHAR) {
1046 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1047 GdkScreen *screen = gdk_screen_get_default();
1048 guint screenwidth = gdk_screen_get_width(screen);
1049 guint screenheight = gdk_screen_get_height(screen);
1050 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1051 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1054 if (szd->name == NULL) szd--;
1055 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1057 while (szd->name != NULL &&
1058 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1059 if (szd->name == NULL) {
1060 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1061 programName, appData.boardSize);
1065 squareSize = szd->squareSize;
1066 lineGap = szd->lineGap;
1067 clockFontPxlSize = szd->clockFontPxlSize;
1068 coordFontPxlSize = szd->coordFontPxlSize;
1069 fontPxlSize = szd->fontPxlSize;
1070 smallLayout = szd->smallLayout;
1071 tinyLayout = szd->tinyLayout;
1072 // [HGM] font: use defaults from settings file if available and not overruled
1074 if(BOARD_WIDTH != 8) {
1075 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
1076 lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
1079 defaultLineGap = lineGap;
1080 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1082 /* [HR] height treated separately (hacked) */
1083 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1084 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1087 * Determine what fonts to use.
1089 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1092 * Detect if there are not enough colors available and adapt.
1095 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1096 appData.monoMode = True;
1100 forceMono = MakeColors();
1103 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1105 appData.monoMode = True;
1108 ParseIcsTextColors();
1114 layoutName = "tinyLayout";
1115 } else if (smallLayout) {
1116 layoutName = "smallLayout";
1118 layoutName = "normalLayout";
1121 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1122 wpMain.width = -1; // prevent popup sizes window
1123 optList = BoardPopUp(squareSize, lineGap, (void*)
1133 InitDrawingHandle(optList + W_BOARD);
1134 shellWidget = shells[BoardWindow];
1135 currBoard = &optList[W_BOARD];
1136 boardWidget = optList[W_BOARD].handle;
1137 menuBarWidget = optList[W_MENU].handle;
1138 dropMenu = optList[W_DROP].handle;
1139 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1141 formWidget = XtParent(boardWidget);
1142 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1143 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1144 XtGetValues(optList[W_WHITE].handle, args, 2);
1145 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1146 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1147 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1148 XtGetValues(optList[W_PAUSE].handle, args, 2);
1152 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1153 // not need to go into InitDrawingSizes().
1157 // add accelerators to main shell
1158 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1161 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1163 WhiteIcon = LoadIconFile("icon_white");
1164 BlackIcon = LoadIconFile("icon_black");
1165 SetClockIcon(0); // sets white icon
1169 * Create a cursor for the board widget.
1172 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1173 XChangeWindowAttributes(xDisplay, xBoardWindow,
1174 CWCursor, &window_attributes);
1178 * Inhibit shell resizing.
1181 shellArgs[0].value = (XtArgVal) &w;
1182 shellArgs[1].value = (XtArgVal) &h;
1183 XtGetValues(shellWidget, shellArgs, 2);
1184 shellArgs[4].value = shellArgs[2].value = w;
1185 shellArgs[5].value = shellArgs[3].value = h;
1186 // XtSetValues(shellWidget, &shellArgs[2], 4);
1189 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1190 // It wil only become known asynchronously, when we first write a string into it.
1191 // This will then change the clock widget height, which triggers resizing the top-level window
1192 // and a configure event. Only then can we know the total height of the top-level window,
1193 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1194 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1197 gtk_widget_get_allocation(shells[BoardWindow], &a);
1198 w = a.width; h = a.height;
1199 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1200 clockKludge = hc = a.height;
1201 gtk_widget_get_allocation(boardWidget, &a);
1202 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1203 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1209 if(appData.logoSize)
1210 { // locate and read user logo
1211 char buf[MSG_SIZ], name[MSG_SIZ];
1212 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1213 if(!FindLogo(name, ".logo", buf))
1214 FindLogo(appData.logoDir, name + 6, buf);
1215 ASSIGN(userLogo, buf);
1218 if (appData.animate || appData.animateDragging)
1221 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1222 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1224 /* [AS] Restore layout */
1225 if( wpMoveHistory.visible ) {
1229 if( wpEvalGraph.visible )
1234 if( wpEngineOutput.visible ) {
1235 EngineOutputPopUp();
1238 if( wpConsole.visible && appData.icsActive ) {
1243 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1248 if (errorExitStatus == -1) {
1249 if (appData.icsActive) {
1250 /* We now wait until we see "login:" from the ICS before
1251 sending the logon script (problems with timestamp otherwise) */
1252 /*ICSInitScript();*/
1253 if (appData.icsInputBox) ICSInputBoxPopUp();
1257 signal(SIGWINCH, TermSizeSigHandler);
1259 signal(SIGINT, IntSigHandler);
1260 signal(SIGTERM, IntSigHandler);
1261 if (*appData.cmailGameName != NULLCHAR) {
1262 signal(SIGUSR1, CmailSigHandler);
1267 // XtSetKeyboardFocus(shellWidget, formWidget);
1269 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1272 /* check for GTK events and process them */
1275 gtk_main_iteration();
1278 if (appData.debugMode) fclose(debugFP); // [DM] debug
1285 while(gtk_events_pending()) gtk_main_iteration();
1289 TermSizeSigHandler (int sig)
1295 IntSigHandler (int sig)
1301 CmailSigHandler (int sig)
1306 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1308 /* Activate call-back function CmailSigHandlerCallBack() */
1309 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1311 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1315 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1318 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1320 /**** end signal code ****/
1323 #define Abs(n) ((n)<0 ? -(n) : (n))
1326 InsertPxlSize (char *pattern, int targetPxlSize)
1329 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1336 InsertPxlSize (char *pattern, int targetPxlSize)
1338 char *base_fnt_lst, strInt[12], *p, *q;
1339 int alternatives, i, len, strIntLen;
1342 * Replace the "*" (if present) in the pixel-size slot of each
1343 * alternative with the targetPxlSize.
1347 while ((p = strchr(p, ',')) != NULL) {
1351 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1352 strIntLen = strlen(strInt);
1353 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1357 while (alternatives--) {
1358 char *comma = strchr(p, ',');
1359 for (i=0; i<14; i++) {
1360 char *hyphen = strchr(p, '-');
1362 if (comma && hyphen > comma) break;
1363 len = hyphen + 1 - p;
1364 if (i == 7 && *p == '*' && len == 2) {
1366 memcpy(q, strInt, strIntLen);
1376 len = comma + 1 - p;
1383 return base_fnt_lst;
1389 CreateFontSet (char *base_fnt_lst)
1392 char **missing_list;
1396 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1397 &missing_list, &missing_count, &def_string);
1398 if (appData.debugMode) {
1400 XFontStruct **font_struct_list;
1401 char **font_name_list;
1402 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1404 fprintf(debugFP, " got list %s, locale %s\n",
1405 XBaseFontNameListOfFontSet(fntSet),
1406 XLocaleOfFontSet(fntSet));
1407 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1408 for (i = 0; i < count; i++) {
1409 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1412 for (i = 0; i < missing_count; i++) {
1413 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1416 if (fntSet == NULL) {
1417 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1423 #else // not ENABLE_NLS
1425 * Find a font that matches "pattern" that is as close as
1426 * possible to the targetPxlSize. Prefer fonts that are k
1427 * pixels smaller to fonts that are k pixels larger. The
1428 * pattern must be in the X Consortium standard format,
1429 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1430 * The return value should be freed with XtFree when no
1435 FindFont (char *pattern, int targetPxlSize)
1437 char **fonts, *p, *best, *scalable, *scalableTail;
1438 int i, j, nfonts, minerr, err, pxlSize;
1440 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1442 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1443 programName, pattern);
1450 for (i=0; i<nfonts; i++) {
1453 if (*p != '-') continue;
1455 if (*p == NULLCHAR) break;
1456 if (*p++ == '-') j++;
1458 if (j < 7) continue;
1461 scalable = fonts[i];
1464 err = pxlSize - targetPxlSize;
1465 if (Abs(err) < Abs(minerr) ||
1466 (minerr > 0 && err < 0 && -err == minerr)) {
1472 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1473 /* If the error is too big and there is a scalable font,
1474 use the scalable font. */
1475 int headlen = scalableTail - scalable;
1476 p = (char *) XtMalloc(strlen(scalable) + 10);
1477 while (isdigit(*scalableTail)) scalableTail++;
1478 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1480 p = (char *) XtMalloc(strlen(best) + 2);
1481 safeStrCpy(p, best, strlen(best)+1 );
1483 if (appData.debugMode) {
1484 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1485 pattern, targetPxlSize, p);
1487 XFreeFontNames(fonts);
1494 MarkMenuItem (char *menuRef, int state)
1496 MenuItem *item = MenuNameToItem(menuRef);
1498 if(item && item->handle) {
1499 ((GtkCheckMenuItem *) (item->handle))->active = state;
1505 EnableNamedMenuItem (char *menuRef, int state)
1507 MenuItem *item = MenuNameToItem(menuRef);
1509 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1514 EnableButtonBar (int state)
1517 XtSetSensitive(optList[W_BUTTON].handle, state);
1523 SetMenuEnables (Enables *enab)
1525 while (enab->name != NULL) {
1526 EnableNamedMenuItem(enab->name, enab->value);
1531 gboolean KeyPressProc(window, eventkey, data)
1533 GdkEventKey *eventkey;
1537 MoveTypeInProc(eventkey); // pop up for typed in moves
1540 /* check for other key values */
1541 switch(eventkey->keyval) {
1553 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1554 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1556 if(*nprms == 0) return;
1557 item = MenuNameToItem(prms[0]);
1558 if(item) ((MenuProc *) item->proc) ();
1572 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1573 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1574 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1575 dmEnables[i].piece);
1576 XtSetSensitive(entry, p != NULL || !appData.testLegality
1577 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1578 && !appData.icsActive));
1580 while (p && *p++ == dmEnables[i].piece) count++;
1581 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1583 XtSetArg(args[j], XtNlabel, label); j++;
1584 XtSetValues(entry, args, j);
1590 do_flash_delay (unsigned long msec)
1596 FlashDelay (int flash_delay)
1598 if(flash_delay) do_flash_delay(flash_delay);
1602 Fraction (int x, int start, int stop)
1604 double f = ((double) x - start)/(stop - start);
1605 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1609 static WindowPlacement wpNew;
1612 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1614 int touch=0, fudge = 4, f = 3;
1615 GetActualPlacement(sh, wp);
1616 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1617 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1618 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1619 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1620 //printf("CoDrag: touch = %d x=%d w=%d x2=%d w2=%d fx=%d\n", touch, wpMain.x, wpMain.width, wp->x, wp->width, frameX);
1621 if(!touch ) return; // only windows that touch co-move
1622 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1623 int heightInc = wpNew.height - wpMain.height;
1624 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1625 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1626 wp->y += fracTop * heightInc;
1627 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1629 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1631 wp->height += heightInc;
1632 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1633 int widthInc = wpNew.width - wpMain.width;
1634 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1635 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1636 wp->y += fracLeft * widthInc;
1637 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1639 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1641 wp->width += widthInc;
1643 wp->x += wpNew.x - wpMain.x;
1644 wp->y += wpNew.y - wpMain.y;
1645 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1646 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1648 XtSetArg(args[j], XtNx, wp->x); j++;
1649 XtSetArg(args[j], XtNy, wp->y); j++;
1650 XtSetValues(sh, args, j);
1652 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1653 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1654 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1658 ReSize (WindowPlacement *wp)
1661 int sqx, sqy, w, h, lg = lineGap;
1662 static int first = 1;
1663 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
1664 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1665 w = a.width; h = a.height;
1666 gtk_widget_get_allocation(shellWidget, &a);
1667 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1668 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1669 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1670 w += a.width; h += a.height;
1672 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1673 w = a.width; h = a.height;
1675 sqx = (w - lg) / BOARD_WIDTH - lg;
1676 sqy = (h - lg) / BOARD_HEIGHT - lg;
1677 if(sqy < sqx) sqx = sqy;
1678 if(sqx < 20) return;
1679 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1681 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1682 sqx = (w - lg) / BOARD_WIDTH - lg;
1683 sqy = (h - lg) / BOARD_HEIGHT - lg;
1684 if(sqy < sqx) sqx = sqy;
1685 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1686 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1688 if(sqx != squareSize && !first) {
1689 squareSize = sqx; // adopt new square size
1690 CreatePNGPieces(); // make newly scaled pieces
1691 InitDrawingSizes(0, 0); // creates grid etc.
1692 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1693 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1694 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1695 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1696 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1697 first = appData.fixedSize;
1700 static guint delayedDragTag = 0;
1709 GetActualPlacement(shellWidget, &wpNew);
1710 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1711 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1712 busy = 0; return; // false alarm
1715 if(appData.useStickyWindows) {
1716 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1717 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1718 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1719 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1720 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1723 DrawPosition(True, NULL);
1724 if(delayedDragTag) g_source_remove(delayedDragTag);
1725 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1732 //printf("old timr = %d\n", delayedDragTag);
1733 if(delayedDragTag) g_source_remove(delayedDragTag);
1734 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1735 //printf("new timr = %d\n", delayedDragTag);
1739 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1741 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1743 wpNew.x = event->configure.x;
1744 wpNew.y = event->configure.y;
1745 wpNew.width = event->configure.width;
1746 wpNew.height = event->configure.height;
1747 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1753 /* Disable all user input other than deleting the window */
1754 static int frozen = 0;
1760 /* Grab by a widget that doesn't accept input */
1761 gtk_grab_add(optList[W_MESSG].handle);
1765 /* Undo a FreezeUI */
1769 if (!frozen) return;
1770 gtk_grab_remove(optList[W_MESSG].handle);
1777 static int oldPausing = FALSE;
1778 static GameMode oldMode = (GameMode) -1;
1780 if (!boardWidget) return;
1782 if (pausing != oldPausing) {
1783 oldPausing = pausing;
1784 MarkMenuItem("Mode.Pause", pausing);
1786 if (appData.showButtonBar) {
1787 /* Always toggle, don't set. Previous code messes up when
1788 invoked while the button is pressed, as releasing it
1789 toggles the state again. */
1791 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1792 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1796 wname = ModeToWidgetName(oldMode);
1797 if (wname != NULL) {
1798 MarkMenuItem(wname, False);
1800 wname = ModeToWidgetName(gameMode);
1801 if (wname != NULL) {
1802 MarkMenuItem(wname, True);
1804 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1805 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1808 /* Maybe all the enables should be handled here, not just this one */
1809 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1811 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1816 * Button/menu procedures
1819 void CopyFileToClipboard(gchar *filename)
1821 gchar *selection_tmp;
1825 FILE* f = fopen(filename, "r");
1828 if (f == NULL) return;
1832 selection_tmp = g_try_malloc(len + 1);
1833 if (selection_tmp == NULL) {
1834 printf("Malloc failed in CopyFileToClipboard\n");
1837 count = fread(selection_tmp, 1, len, f);
1840 g_free(selection_tmp);
1843 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1845 // copy selection_tmp to clipboard
1846 GdkDisplay *gdisp = gdk_display_get_default();
1848 g_free(selection_tmp);
1851 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1852 gtk_clipboard_set_text(cb, selection_tmp, -1);
1853 g_free(selection_tmp);
1857 CopySomething (char *src)
1859 GdkDisplay *gdisp = gdk_display_get_default();
1861 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1862 if (gdisp == NULL) return;
1863 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1864 gtk_clipboard_set_text(cb, src, -1);
1868 PastePositionProc ()
1870 GdkDisplay *gdisp = gdk_display_get_default();
1874 if (gdisp == NULL) return;
1875 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1876 fenstr = gtk_clipboard_wait_for_text(cb);
1877 if (fenstr==NULL) return; // nothing had been selected to copy
1878 EditPositionPasteFEN(fenstr);
1890 // get game from clipboard
1891 GdkDisplay *gdisp = gdk_display_get_default();
1892 if (gdisp == NULL) return;
1893 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1894 text = gtk_clipboard_wait_for_text(cb);
1895 if (text == NULL) return; // nothing to paste
1898 // write to temp file
1899 if (text == NULL || len == 0) {
1900 return; //nothing to paste
1902 f = fopen(gamePasteFilename, "w");
1904 DisplayError(_("Can't open temp file"), errno);
1907 fwrite(text, 1, len, f);
1911 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1918 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1924 void MoveTypeInProc(eventkey)
1925 GdkEventKey *eventkey;
1929 // ingnore if ctrl, alt, or meta is pressed
1930 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1934 buf[0]=eventkey->keyval;
1936 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1937 ConsoleAutoPopUp (buf);
1942 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1944 if (!TempBackwardActive) {
1945 TempBackwardActive = True;
1951 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1953 /* Check to see if triggered by a key release event for a repeating key.
1954 * If so the next queued event will be a key press of the same key at the same time */
1955 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1957 XPeekEvent(xDisplay, &next);
1958 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1959 next.xkey.keycode == event->xkey.keycode)
1963 TempBackwardActive = False;
1969 { // called from menu
1972 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
1975 system("xterm -e man xboard &");
1984 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"info -d %s/../info -f xboard.info\"' -e 'end tell'", dataDir);
1986 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
1994 SetWindowTitle (char *text, char *title, char *icon)
1999 if (appData.titleInWindow) {
2001 XtSetArg(args[i], XtNlabel, text); i++;
2002 XtSetValues(titleWidget, args, i);
2005 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2006 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2007 XtSetValues(shellWidget, args, i);
2008 XSync(xDisplay, False);
2010 if (appData.titleInWindow) {
2011 SetWidgetLabel(titleWidget, text);
2013 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2018 DisplayIcsInteractionTitle (String message)
2021 if (oldICSInteractionTitle == NULL) {
2022 /* Magic to find the old window title, adapted from vim */
2023 char *wina = getenv("WINDOWID");
2025 Window win = (Window) atoi(wina);
2026 Window root, parent, *children;
2027 unsigned int nchildren;
2028 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2030 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2031 if (!XQueryTree(xDisplay, win, &root, &parent,
2032 &children, &nchildren)) break;
2033 if (children) XFree((void *)children);
2034 if (parent == root || parent == 0) break;
2037 XSetErrorHandler(oldHandler);
2039 if (oldICSInteractionTitle == NULL) {
2040 oldICSInteractionTitle = "xterm";
2043 printf("\033]0;%s\007", message);
2050 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2052 GtkWidget *w = (GtkWidget *) opt->handle;
2059 strcpy(bgcolor, "black");
2060 strcpy(fgcolor, "white");
2062 strcpy(bgcolor, "white");
2063 strcpy(fgcolor, "black");
2066 appData.lowTimeWarning &&
2067 (timer / 1000) < appData.icsAlarmTime) {
2068 strcpy(fgcolor, appData.lowTimeWarningColor);
2071 gdk_color_parse( bgcolor, &col );
2072 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2074 if (appData.clockMode) {
2075 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2076 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2077 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2078 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2080 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2081 bgcolor, fgcolor, color);
2082 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2083 // bgcolor, fgcolor, color);
2085 gtk_label_set_markup(GTK_LABEL(w), markup);
2089 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2092 SetClockIcon (int color)
2094 GdkPixbuf *pm = *clockIcons[color];
2095 if (mainwindowIcon != pm) {
2096 mainwindowIcon = pm;
2098 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2100 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2105 #define INPUT_SOURCE_BUF_SIZE 8192
2114 char buf[INPUT_SOURCE_BUF_SIZE];
2119 DoInputCallback(io, cond, data)
2124 /* read input from one of the input source (for example a chess program, ICS, etc).
2125 * and call a function that will handle the input
2132 /* All information (callback function, file descriptor, etc) is
2133 * saved in an InputSource structure
2135 InputSource *is = (InputSource *) data;
2137 if (is->lineByLine) {
2138 count = read(is->fd, is->unused,
2139 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2141 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2142 RemoveInputSource(is); // cease reading stdin
2143 stdoutClosed = TRUE; // suppress future output
2146 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2149 is->unused += count;
2151 /* break input into lines and call the callback function on each
2154 while (p < is->unused) {
2155 q = memchr(p, '\n', is->unused - p);
2156 if (q == NULL) break;
2158 (is->func)(is, is->closure, p, q - p, 0);
2161 /* remember not yet used part of the buffer */
2163 while (p < is->unused) {
2168 /* read maximum length of input buffer and send the whole buffer
2169 * to the callback function
2171 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2176 (is->func)(is, is->closure, is->buf, count, error);
2178 return True; // Must return true or the watch will be removed
2181 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2188 GIOChannel *channel;
2189 ChildProc *cp = (ChildProc *) pr;
2191 is = (InputSource *) calloc(1, sizeof(InputSource));
2192 is->lineByLine = lineByLine;
2196 is->fd = fileno(stdin);
2198 is->kind = cp->kind;
2199 is->fd = cp->fdFrom;
2202 is->unused = is->buf;
2206 /* GTK-TODO: will this work on windows?*/
2208 channel = g_io_channel_unix_new(is->fd);
2209 g_io_channel_set_close_on_unref (channel, TRUE);
2210 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2212 is->closure = closure;
2213 return (InputSourceRef) is;
2218 RemoveInputSource(isr)
2221 InputSource *is = (InputSource *) isr;
2223 if (is->sid == 0) return;
2224 g_source_remove(is->sid);
2231 static Boolean frameWaiting;
2234 FrameAlarm (int sig)
2236 frameWaiting = False;
2237 /* In case System-V style signals. Needed?? */
2238 signal(SIGALRM, FrameAlarm);
2242 FrameDelay (int time)
2244 struct itimerval delay;
2247 frameWaiting = True;
2248 signal(SIGALRM, FrameAlarm);
2249 delay.it_interval.tv_sec =
2250 delay.it_value.tv_sec = time / 1000;
2251 delay.it_interval.tv_usec =
2252 delay.it_value.tv_usec = (time % 1000) * 1000;
2253 setitimer(ITIMER_REAL, &delay, NULL);
2254 while (frameWaiting) pause();
2255 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2256 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2257 setitimer(ITIMER_REAL, &delay, NULL);
2264 FrameDelay (int time)
2267 XSync(xDisplay, False);
2269 // gtk_main_iteration_do(False);
2272 usleep(time * 1000);
2278 FindLogo (char *place, char *name, char *buf)
2279 { // check if file exists in given place
2281 if(!place) return 0;
2282 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2283 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2291 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2293 char buf[MSG_SIZ], *logoName = buf;
2294 if(appData.logo[n][0]) {
2295 logoName = appData.logo[n];
2296 } else if(appData.autoLogo) {
2297 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2298 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2299 } else { // engine; cascade
2300 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2301 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2302 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2303 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2307 { ASSIGN(cps->programLogo, logoName); }
2311 UpdateLogos (int displ)
2313 if(optList[W_WHITE-1].handle == NULL) return;
2314 LoadLogo(&first, 0, 0);
2315 LoadLogo(&second, 1, appData.icsActive);
2316 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2320 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2331 GtkFileFilter *gtkfilter;
2332 GtkFileFilter *gtkfilter_all;
2334 char fileext[10] = "";
2335 char *result = NULL;
2337 char curDir[MSG_SIZ];
2339 StartDir(filter, NULL); // change to start directory for this file type
2341 if(def && *def && def[strlen(def)-1] == '/') {
2342 getcwd(curDir, MSG_SIZ);
2347 /* make a copy of the filter string, so that strtok can work with it*/
2348 cp = strdup(filter);
2350 /* add filters for file extensions */
2351 gtkfilter = gtk_file_filter_new();
2352 gtkfilter_all = gtk_file_filter_new();
2354 /* one filter to show everything */
2355 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2356 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2358 /* add filter if present */
2359 result = strtok(cp, space);
2360 while( result != NULL ) {
2361 snprintf(fileext,10,"*%s",result);
2362 result = strtok( NULL, space );
2363 gtk_file_filter_add_pattern(gtkfilter, fileext);
2366 /* second filter to only show what's useful */
2367 gtk_file_filter_set_name (gtkfilter,filter);
2369 if (openMode[0] == 'r')
2371 dialog = gtk_file_chooser_dialog_new (label,
2373 GTK_FILE_CHOOSER_ACTION_OPEN,
2374 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2375 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2380 dialog = gtk_file_chooser_dialog_new (label,
2382 GTK_FILE_CHOOSER_ACTION_SAVE,
2383 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2384 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2386 /* add filename suggestions */
2387 if (strlen(def) > 0 )
2388 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2390 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2394 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2395 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2396 /* activate filter */
2397 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2399 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2404 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2407 f = fopen(filename, openMode);
2410 DisplayError(_("Failed to open file"), errno);
2414 /* TODO add indec */
2416 ASSIGN(*name, filename);
2417 ScheduleDelayedEvent(DelayedLoad, 50);
2419 StartDir(filter, filename);
2422 else StartDir(filter, "");
2424 gtk_widget_destroy (dialog);
2427 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);