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>
68 # if HAVE_SYS_SOCKET_H
69 # include <sys/socket.h>
70 # include <netinet/in.h>
72 # else /* not HAVE_SYS_SOCKET_H */
73 # if HAVE_LAN_SOCKET_H
74 # include <lan/socket.h>
76 # include <lan/netdb.h>
77 # else /* not HAVE_LAN_SOCKET_H */
78 # define OMIT_SOCKETS 1
79 # endif /* not HAVE_LAN_SOCKET_H */
80 # endif /* not HAVE_SYS_SOCKET_H */
81 #endif /* !OMIT_SOCKETS */
86 #else /* not STDC_HEADERS */
87 extern char *getenv();
90 # else /* not HAVE_STRING_H */
92 # endif /* not HAVE_STRING_H */
93 #endif /* not STDC_HEADERS */
96 # include <sys/fcntl.h>
97 #else /* not HAVE_SYS_FCNTL_H */
100 # endif /* HAVE_FCNTL_H */
101 #endif /* not HAVE_SYS_FCNTL_H */
103 #if HAVE_SYS_SYSTEMINFO_H
104 # include <sys/systeminfo.h>
105 #endif /* HAVE_SYS_SYSTEMINFO_H */
107 #if TIME_WITH_SYS_TIME
108 # include <sys/time.h>
112 # include <sys/time.h>
123 # include <sys/wait.h>
128 # define NAMLEN(dirent) strlen((dirent)->d_name)
129 # define HAVE_DIR_STRUCT
131 # define dirent direct
132 # define NAMLEN(dirent) (dirent)->d_namlen
134 # include <sys/ndir.h>
135 # define HAVE_DIR_STRUCT
138 # include <sys/dir.h>
139 # define HAVE_DIR_STRUCT
143 # define HAVE_DIR_STRUCT
151 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
154 #include "frontend.h"
156 #include "backendz.h"
164 #include "engineoutput.h"
170 # include <gtkmacintegration-gtk2/gtkosxapplication.h>
171 // prevent pathname of positional file argument provided by OS X being be mistaken for option name
172 // (price is that we won't recognize Windows option format anymore).
175 // redefine some defaults
179 # undef SETTINGS_FILE
180 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
181 # define DATADIR dataDir
182 # define LOCALEDIR localeDir
183 # define SETTINGS_FILE masterSettings
184 # define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
185 char dataDir[MSG_SIZ]; // for expanding ~~
186 char localeDir[MSG_SIZ];
187 char masterSettings[MSG_SIZ];
191 # define SYNC_MENUBAR
198 #define usleep(t) _sleep2(((t)+500)/1000)
202 # define _(s) gettext (s)
203 # define N_(s) gettext_noop (s)
209 int main P((int argc, char **argv));
210 RETSIGTYPE CmailSigHandler P((int sig));
211 RETSIGTYPE IntSigHandler P((int sig));
212 RETSIGTYPE TermSizeSigHandler P((int sig));
213 char *InsertPxlSize P((char *pattern, int targetPxlSize));
216 XFontSet CreateFontSet P((char *base_fnt_lst));
218 char *FindFont P((char *pattern, int targetPxlSize));
221 void DelayedDrag P((void));
222 void ICSInputBoxPopUp P((void));
223 void MoveTypeInProc P((GdkEventKey *eventkey));
224 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
225 Boolean TempBackwardActive = False;
226 void DisplayMove P((int moveNumber));
227 void update_ics_width P(());
228 int CopyMemoProc P(());
229 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
230 static int FindLogo P((char *place, char *name, char *buf));
234 XFontSet fontSet, clockFontSet;
237 XFontStruct *clockFontStruct;
239 Font coordFontID, countFontID;
240 XFontStruct *coordFontStruct, *countFontStruct;
242 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
243 GtkWidget *mainwindow;
245 Option *optList; // contains all widgets of main window
248 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
251 static GdkPixbuf *mainwindowIcon=NULL;
252 static GdkPixbuf *WhiteIcon=NULL;
253 static GdkPixbuf *BlackIcon=NULL;
255 /* key board accelerators */
256 GtkAccelGroup *GtkAccelerators;
258 typedef unsigned int BoardSize;
260 Boolean chessProgram;
262 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
263 int smallLayout = 0, tinyLayout = 0,
264 marginW, marginH, // [HGM] for run-time resizing
265 fromX = -1, fromY = -1, toX, toY, commentUp = False,
266 errorExitStatus = -1, defaultLineGap;
268 Dimension textHeight;
270 char *chessDir, *programName, *programVersion;
271 Boolean alwaysOnTop = False;
272 char *icsTextMenuString;
274 char *firstChessProgramNames;
275 char *secondChessProgramNames;
277 WindowPlacement wpMain;
278 WindowPlacement wpConsole;
279 WindowPlacement wpComment;
280 WindowPlacement wpMoveHistory;
281 WindowPlacement wpEvalGraph;
282 WindowPlacement wpEngineOutput;
283 WindowPlacement wpGameList;
284 WindowPlacement wpTags;
285 WindowPlacement wpDualBoard;
287 /* This magic number is the number of intermediate frames used
288 in each half of the animation. For short moves it's reduced
289 by 1. The total number of frames will be factor * 2 + 1. */
292 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
299 DropMenuEnables dmEnables[] = {
308 XtResource clientResources[] = {
309 { "flashCount", "flashCount", XtRInt, sizeof(int),
310 XtOffset(AppDataPtr, flashCount), XtRImmediate,
311 (XtPointer) FLASH_COUNT },
315 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
316 char globalTranslations[] =
317 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
318 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
319 :<KeyDown>Return: TempBackwardProc() \n \
320 :<KeyUp>Return: TempForwardProc() \n";
322 char ICSInputTranslations[] =
323 "<Key>Up: UpKeyProc() \n "
324 "<Key>Down: DownKeyProc() \n "
325 "<Key>Return: EnterKeyProc() \n";
327 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
328 // as the widget is destroyed before the up-click can call extend-end
329 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
332 String xboardResources[] = {
333 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
341 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
344 //---------------------------------------------------------------------------------------------------------
345 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
348 #define CW_USEDEFAULT (1<<31)
349 #define ICS_TEXT_MENU_SIZE 90
350 #define DEBUG_FILE "xboard.debug"
351 #define SetCurrentDirectory chdir
352 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
356 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
359 // front-end part of option handling
361 // [HGM] This platform-dependent table provides the location for storing the color info
362 extern char *crWhite, * crBlack;
366 &appData.whitePieceColor,
367 &appData.blackPieceColor,
368 &appData.lightSquareColor,
369 &appData.darkSquareColor,
370 &appData.highlightSquareColor,
371 &appData.premoveHighlightColor,
372 &appData.lowTimeWarningColor,
383 // [HGM] font: keep a font for each square size, even non-stndard ones
386 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
387 char *fontTable[NUM_FONTS][MAX_SIZE];
390 ParseFont (char *name, int number)
391 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
393 if(sscanf(name, "size%d:", &size)) {
394 // [HGM] font: font is meant for specific boardSize (likely from settings file);
395 // defer processing it until we know if it matches our board size
396 if(!strstr(name, "-*-") && // ignore X-fonts
397 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
398 fontTable[number][size] = strdup(strchr(name, ':')+1);
399 fontValid[number][size] = True;
404 case 0: // CLOCK_FONT
405 appData.clockFont = strdup(name);
407 case 1: // MESSAGE_FONT
408 appData.font = strdup(name);
410 case 2: // COORD_FONT
411 appData.coordFont = strdup(name);
414 appData.icsFont = strdup(name);
417 appData.tagsFont = strdup(name);
420 appData.commentFont = strdup(name);
422 case MOVEHISTORY_FONT:
423 appData.historyFont = strdup(name);
426 appData.gameListFont = strdup(name);
431 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
436 { // only 2 fonts currently
437 appData.clockFont = strdup(CLOCK_FONT_NAME);
438 appData.coordFont = strdup(COORD_FONT_NAME);
439 appData.font = strdup(DEFAULT_FONT_NAME);
440 appData.icsFont = strdup(CONSOLE_FONT_NAME);
441 appData.tagsFont = strdup(TAGS_FONT_NAME);
442 appData.commentFont = strdup(COMMENT_FONT_NAME);
443 appData.historyFont = strdup(HISTORY_FONT_NAME);
444 appData.gameListFont = strdup(GAMELIST_FONT_NAME);
449 { // no-op, until we identify the code for this already in XBoard and move it here
453 ParseColor (int n, char *name)
454 { // in XBoard, just copy the color-name string
455 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
461 return *(char**)colorVariable[n];
465 ParseTextAttribs (ColorClass cc, char *s)
467 (&appData.colorShout)[cc] = strdup(s);
471 ParseBoardSize (void *addr, char *name)
473 appData.boardSize = strdup(name);
478 { // In XBoard the sound-playing program takes care of obtaining the actual sound
482 SetCommPortDefaults ()
483 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
486 // [HGM] args: these three cases taken out to stay in front-end
488 SaveFontArg (FILE *f, ArgDescriptor *ad)
491 int i, n = (int)(intptr_t)ad->argLoc;
493 case 0: // CLOCK_FONT
494 name = appData.clockFont;
496 case 1: // MESSAGE_FONT
499 case 2: // COORD_FONT
500 name = appData.coordFont;
503 name = appData.icsFont;
506 name = appData.tagsFont;
509 name = appData.commentFont;
511 case MOVEHISTORY_FONT:
512 name = appData.historyFont;
515 name = appData.gameListFont;
520 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
521 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
522 fontTable[n][squareSize] = strdup(name);
523 fontValid[n][squareSize] = True;
526 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
527 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
532 { // nothing to do, as the sounds are at all times represented by their text-string names already
536 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
537 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
538 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
542 SaveColor (FILE *f, ArgDescriptor *ad)
543 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
544 if(colorVariable[(int)(intptr_t)ad->argLoc])
545 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
549 SaveBoardSize (FILE *f, char *name, void *addr)
550 { // wrapper to shield back-end from BoardSize & sizeInfo
551 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
555 ParseCommPortSettings (char *s)
556 { // no such option in XBoard (yet)
562 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
566 gtk_widget_get_allocation(shell, &a);
567 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
571 wp->height = a.height;
572 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
573 frameX = 3; frameY = 3; // remember to decide if windows touch
577 GetPlacement (DialogClass dlg, WindowPlacement *wp)
578 { // wrapper to shield back-end from widget type
579 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
584 { // wrapper to shield use of window handles from back-end (make addressible by number?)
585 // In XBoard this will have to wait until awareness of window parameters is implemented
586 GetActualPlacement(shellWidget, &wpMain);
587 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
588 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
589 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
590 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
591 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
592 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
593 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
597 PrintCommPortSettings (FILE *f, char *name)
598 { // This option does not exist in XBoard
602 EnsureOnScreen (int *x, int *y, int minX, int minY)
609 { // [HGM] args: allows testing if main window is realized from back-end
610 return DialogExists(BoardWindow);
614 PopUpStartupDialog ()
615 { // start menu not implemented in XBoard
619 ConvertToLine (int argc, char **argv)
621 static char line[128*1024], buf[1024];
625 for(i=1; i<argc; i++)
627 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
628 && argv[i][0] != '{' )
629 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
631 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
632 strncat(line, buf, 128*1024 - strlen(line) - 1 );
635 line[strlen(line)-1] = NULLCHAR;
639 //--------------------------------------------------------------------------------------------
644 ResizeBoardWindow (int w, int h, int inhibit)
648 // if(clockKludge) return; // ignore as long as clock does not have final height
649 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
651 gtk_widget_get_allocation(shellWidget, &a);
652 marginW = a.width - bw;
653 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
654 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
655 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
656 // h += marginH + a.height + 1;
657 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
659 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board again
664 { // dummy, as the GTK code does not make colors in advance
669 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
670 { // determine what fonts to use, and create them
672 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
673 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
674 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
675 appData.font = fontTable[MESSAGE_FONT][squareSize];
676 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
677 appData.coordFont = fontTable[COORD_FONT][squareSize];
678 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
679 appData.icsFont = fontTable[CONSOLE_FONT][squareSize];
680 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
681 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize];
682 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
683 appData.commentFont = fontTable[COMMENT_FONT][squareSize];
684 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
685 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize];
686 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
687 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize];
689 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
690 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
691 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
692 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
693 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
694 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
695 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
696 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
702 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
703 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
704 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
705 appData.font = fontTable[MESSAGE_FONT][squareSize];
706 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
707 appData.coordFont = fontTable[COORD_FONT][squareSize];
710 appData.font = InsertPxlSize(appData.font, fontPxlSize);
711 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
712 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
713 fontSet = CreateFontSet(appData.font);
714 clockFontSet = CreateFontSet(appData.clockFont);
716 /* For the coordFont, use the 0th font of the fontset. */
717 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
718 XFontStruct **font_struct_list;
719 XFontSetExtents *fontSize;
720 char **font_name_list;
721 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
722 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
723 coordFontStruct = XQueryFont(xDisplay, coordFontID);
724 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
725 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
728 appData.font = FindFont(appData.font, fontPxlSize);
729 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
730 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
731 clockFontID = XLoadFont(xDisplay, appData.clockFont);
732 clockFontStruct = XQueryFont(xDisplay, clockFontID);
733 coordFontID = XLoadFont(xDisplay, appData.coordFont);
734 coordFontStruct = XQueryFont(xDisplay, coordFontID);
735 // textHeight in !NLS mode!
737 countFontID = coordFontID; // [HGM] holdings
738 countFontStruct = coordFontStruct;
740 xdb = XtDatabase(xDisplay);
742 XrmPutLineResource(&xdb, "*international: True");
743 vTo.size = sizeof(XFontSet);
744 vTo.addr = (XtPointer) &fontSet;
745 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
747 XrmPutStringResource(&xdb, "*font", appData.font);
758 case ArgInt: p = " N"; break;
759 case ArgString: p = " STR"; break;
760 case ArgBoolean: p = " TF"; break;
761 case ArgSettingsFilename:
762 case ArgBackupSettingsFile:
763 case ArgFilename: p = " FILE"; break;
764 case ArgX: p = " Nx"; break;
765 case ArgY: p = " Ny"; break;
766 case ArgAttribs: p = " TEXTCOL"; break;
767 case ArgColor: p = " COL"; break;
768 case ArgFont: p = " FONT"; break;
769 case ArgBoardSize: p = " SIZE"; break;
770 case ArgFloat: p = " FLOAT"; break;
775 case ArgCommSettings:
787 ArgDescriptor *q, *p = argDescriptors+5;
788 printf("\nXBoard accepts the following options:\n"
789 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
790 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
791 " SIZE = board-size spec(s)\n"
792 " Within parentheses are short forms, or options to set to true or false.\n"
793 " Persistent options (saved in the settings file) are marked with *)\n\n");
795 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
796 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
797 if(p->save) strcat(buf+len, "*");
798 for(q=p+1; q->argLoc == p->argLoc; q++) {
799 if(q->argName[0] == '-') continue;
800 strcat(buf+len, q == p+1 ? " (" : " ");
801 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
803 if(q != p+1) strcat(buf+len, ")");
805 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
808 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
812 SlaveResize (Option *opt)
814 static int slaveW, slaveH, w, h;
817 gtk_widget_get_allocation(shells[DummyDlg], &a);
818 w = a.width; h = a.height;
819 gtk_widget_get_allocation(opt->handle, &a);
820 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
821 slaveH = h - a.height + 13;
823 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
827 LoadIconFile (gchar *svgFilename)
831 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
832 return gdk_pixbuf_new_from_file(buf, NULL);
836 static char clickedFile[MSG_SIZ];
840 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
841 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
842 if(suppress) { // we just started XBoard without arguments
843 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
844 } else { // we are running something presumably useful
846 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
847 system(buf); // start new instance on this file
852 GtkosxApplication *theApp;
856 main (int argc, char **argv)
858 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
859 int boardWidth, w, h; //, boardHeight;
861 int forceMono = False;
863 srandom(time(0)); // [HGM] book: make random truly random
865 setbuf(stdout, NULL);
866 setbuf(stderr, NULL);
869 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
870 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
874 if(argc > 1 && !strcmp(argv[1], "--help" )) {
880 gtk_init (&argc, &argv);
882 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
883 char *path = gtkosx_application_get_bundle_path();
885 char *res_path = gtkosx_application_get_resource_path();
886 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
888 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
889 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
890 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
891 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
892 suppress = (argc == 1 || argc > 1 && argv[1][00] != '-'); // OSX sends signal even if name was already argv[1]!
893 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
894 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
895 // we must call application ready before we can get the signal,
896 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
897 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
898 gtkosx_application_ready(theApp);
899 if(argc == 1) { // called without args: OSX open-file signal might follow
900 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
901 usleep(10000); // wait 10 msec (and hope this is long enough).
902 while(gtk_events_pending())
903 gtk_main_iteration(); // process all events that came in upto now
904 suppress = 0; // future open-file signals should start new instance
905 if(clickedFile[0]) { // we were sent an open-file signal with filename!
906 fakeArgv[0] = argv[0];
907 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
913 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
914 typedef struct {char *name, *value; } Config;
915 static Config configList[] = {
916 { "Datadir", DATADIR },
917 { "Sysconfdir", SYSCONFDIR },
922 for(i=0; configList[i].name; i++) {
923 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
924 if(argc > 2) printf("%s", configList[i].value);
925 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
930 /* set up keyboard accelerators group */
931 GtkAccelerators = gtk_accel_group_new();
933 programName = strrchr(argv[0], '/');
934 if (programName == NULL)
935 programName = argv[0];
940 // if (appData.debugMode) {
941 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
944 bindtextdomain(PACKAGE, LOCALEDIR);
945 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
949 appData.boardSize = "";
950 InitAppData(ConvertToLine(argc, argv));
952 if (p == NULL) p = "/tmp";
953 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
954 gameCopyFilename = (char*) malloc(i);
955 gamePasteFilename = (char*) malloc(i);
956 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
957 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
959 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
960 static char buf[MSG_SIZ];
961 snprintf(buf, MSG_SIZ, appData.sysOpen, DATADIR);
962 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
963 EscapeExpand(buf, appData.firstInitString);
964 appData.firstInitString = strdup(buf);
965 EscapeExpand(buf, appData.secondInitString);
966 appData.secondInitString = strdup(buf);
967 EscapeExpand(buf, appData.firstComputerString);
968 appData.firstComputerString = strdup(buf);
969 EscapeExpand(buf, appData.secondComputerString);
970 appData.secondComputerString = strdup(buf);
973 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
976 if (chdir(chessDir) != 0) {
977 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
983 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
984 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
985 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
986 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
989 setbuf(debugFP, NULL);
993 if (appData.debugMode) {
994 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
998 /* [HGM,HR] make sure board size is acceptable */
999 if(appData.NrFiles > BOARD_FILES ||
1000 appData.NrRanks > BOARD_RANKS )
1001 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1004 /* This feature does not work; animation needs a rewrite */
1005 appData.highlightDragging = FALSE;
1009 gameInfo.variant = StringToVariant(appData.variant);
1010 InitPosition(FALSE);
1013 * determine size, based on supplied or remembered -size, or screen size
1015 if (isdigit(appData.boardSize[0])) {
1016 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1017 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1018 &fontPxlSize, &smallLayout, &tinyLayout);
1020 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1021 programName, appData.boardSize);
1025 /* Find some defaults; use the nearest known size */
1026 SizeDefaults *szd, *nearest;
1027 int distance = 99999;
1028 nearest = szd = sizeDefaults;
1029 while (szd->name != NULL) {
1030 if (abs(szd->squareSize - squareSize) < distance) {
1032 distance = abs(szd->squareSize - squareSize);
1033 if (distance == 0) break;
1037 if (i < 2) lineGap = nearest->lineGap;
1038 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1039 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1040 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1041 if (i < 6) smallLayout = nearest->smallLayout;
1042 if (i < 7) tinyLayout = nearest->tinyLayout;
1045 SizeDefaults *szd = sizeDefaults;
1046 if (*appData.boardSize == NULLCHAR) {
1047 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1048 GdkScreen *screen = gdk_screen_get_default();
1049 guint screenwidth = gdk_screen_get_width(screen);
1050 guint screenheight = gdk_screen_get_height(screen);
1051 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1052 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1055 if (szd->name == NULL) szd--;
1056 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1058 while (szd->name != NULL &&
1059 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1060 if (szd->name == NULL) {
1061 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1062 programName, appData.boardSize);
1066 squareSize = szd->squareSize;
1067 lineGap = szd->lineGap;
1068 clockFontPxlSize = szd->clockFontPxlSize;
1069 coordFontPxlSize = szd->coordFontPxlSize;
1070 fontPxlSize = szd->fontPxlSize;
1071 smallLayout = szd->smallLayout;
1072 tinyLayout = szd->tinyLayout;
1073 // [HGM] font: use defaults from settings file if available and not overruled
1075 if(BOARD_WIDTH != 8) {
1076 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
1077 lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
1080 defaultLineGap = lineGap;
1081 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1083 /* [HR] height treated separately (hacked) */
1084 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1085 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1088 * Determine what fonts to use.
1090 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1093 * Detect if there are not enough colors available and adapt.
1096 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1097 appData.monoMode = True;
1101 forceMono = MakeColors();
1104 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1106 appData.monoMode = True;
1109 ParseIcsTextColors();
1115 layoutName = "tinyLayout";
1116 } else if (smallLayout) {
1117 layoutName = "smallLayout";
1119 layoutName = "normalLayout";
1122 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1123 wpMain.width = -1; // prevent popup sizes window
1124 optList = BoardPopUp(squareSize, lineGap, (void*)
1134 InitDrawingHandle(optList + W_BOARD);
1135 shellWidget = shells[BoardWindow];
1136 currBoard = &optList[W_BOARD];
1137 boardWidget = optList[W_BOARD].handle;
1138 menuBarWidget = optList[W_MENU].handle;
1139 dropMenu = optList[W_DROP].handle;
1140 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1142 formWidget = XtParent(boardWidget);
1143 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1144 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1145 XtGetValues(optList[W_WHITE].handle, args, 2);
1146 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1147 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1148 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1149 XtGetValues(optList[W_PAUSE].handle, args, 2);
1153 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1154 // not need to go into InitDrawingSizes().
1158 // add accelerators to main shell
1159 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1162 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1164 WhiteIcon = LoadIconFile("icon_white");
1165 BlackIcon = LoadIconFile("icon_black");
1166 SetClockIcon(0); // sets white icon
1170 * Create a cursor for the board widget.
1173 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1174 XChangeWindowAttributes(xDisplay, xBoardWindow,
1175 CWCursor, &window_attributes);
1179 * Inhibit shell resizing.
1182 shellArgs[0].value = (XtArgVal) &w;
1183 shellArgs[1].value = (XtArgVal) &h;
1184 XtGetValues(shellWidget, shellArgs, 2);
1185 shellArgs[4].value = shellArgs[2].value = w;
1186 shellArgs[5].value = shellArgs[3].value = h;
1187 // XtSetValues(shellWidget, &shellArgs[2], 4);
1190 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1191 // It wil only become known asynchronously, when we first write a string into it.
1192 // This will then change the clock widget height, which triggers resizing the top-level window
1193 // and a configure event. Only then can we know the total height of the top-level window,
1194 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1195 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1198 gtk_widget_get_allocation(shells[BoardWindow], &a);
1199 w = a.width; h = a.height;
1200 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1201 clockKludge = hc = a.height;
1202 gtk_widget_get_allocation(boardWidget, &a);
1203 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1204 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1210 if(appData.logoSize)
1211 { // locate and read user logo
1212 char buf[MSG_SIZ], name[MSG_SIZ];
1213 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1214 if(!FindLogo(name, ".logo", buf))
1215 FindLogo(appData.logoDir, name + 6, buf);
1216 ASSIGN(userLogo, buf);
1219 if (appData.animate || appData.animateDragging)
1222 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1223 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1225 /* [AS] Restore layout */
1226 if( wpMoveHistory.visible ) {
1230 if( wpEvalGraph.visible )
1235 if( wpEngineOutput.visible ) {
1236 EngineOutputPopUp();
1239 if( wpConsole.visible && appData.icsActive ) {
1244 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1249 if (errorExitStatus == -1) {
1250 if (appData.icsActive) {
1251 /* We now wait until we see "login:" from the ICS before
1252 sending the logon script (problems with timestamp otherwise) */
1253 /*ICSInitScript();*/
1254 if (appData.icsInputBox) ICSInputBoxPopUp();
1258 signal(SIGWINCH, TermSizeSigHandler);
1260 signal(SIGINT, IntSigHandler);
1261 signal(SIGTERM, IntSigHandler);
1262 if (*appData.cmailGameName != NULLCHAR) {
1263 signal(SIGUSR1, CmailSigHandler);
1268 // XtSetKeyboardFocus(shellWidget, formWidget);
1270 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1273 /* check for GTK events and process them */
1276 gtk_main_iteration();
1279 if (appData.debugMode) fclose(debugFP); // [DM] debug
1286 while(gtk_events_pending()) gtk_main_iteration();
1290 TermSizeSigHandler (int sig)
1296 IntSigHandler (int sig)
1302 CmailSigHandler (int sig)
1307 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1309 /* Activate call-back function CmailSigHandlerCallBack() */
1310 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1312 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1316 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1319 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1321 /**** end signal code ****/
1324 #define Abs(n) ((n)<0 ? -(n) : (n))
1327 InsertPxlSize (char *pattern, int targetPxlSize)
1330 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1337 InsertPxlSize (char *pattern, int targetPxlSize)
1339 char *base_fnt_lst, strInt[12], *p, *q;
1340 int alternatives, i, len, strIntLen;
1343 * Replace the "*" (if present) in the pixel-size slot of each
1344 * alternative with the targetPxlSize.
1348 while ((p = strchr(p, ',')) != NULL) {
1352 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1353 strIntLen = strlen(strInt);
1354 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1358 while (alternatives--) {
1359 char *comma = strchr(p, ',');
1360 for (i=0; i<14; i++) {
1361 char *hyphen = strchr(p, '-');
1363 if (comma && hyphen > comma) break;
1364 len = hyphen + 1 - p;
1365 if (i == 7 && *p == '*' && len == 2) {
1367 memcpy(q, strInt, strIntLen);
1377 len = comma + 1 - p;
1384 return base_fnt_lst;
1390 CreateFontSet (char *base_fnt_lst)
1393 char **missing_list;
1397 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1398 &missing_list, &missing_count, &def_string);
1399 if (appData.debugMode) {
1401 XFontStruct **font_struct_list;
1402 char **font_name_list;
1403 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1405 fprintf(debugFP, " got list %s, locale %s\n",
1406 XBaseFontNameListOfFontSet(fntSet),
1407 XLocaleOfFontSet(fntSet));
1408 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1409 for (i = 0; i < count; i++) {
1410 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1413 for (i = 0; i < missing_count; i++) {
1414 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1417 if (fntSet == NULL) {
1418 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1424 #else // not ENABLE_NLS
1426 * Find a font that matches "pattern" that is as close as
1427 * possible to the targetPxlSize. Prefer fonts that are k
1428 * pixels smaller to fonts that are k pixels larger. The
1429 * pattern must be in the X Consortium standard format,
1430 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1431 * The return value should be freed with XtFree when no
1436 FindFont (char *pattern, int targetPxlSize)
1438 char **fonts, *p, *best, *scalable, *scalableTail;
1439 int i, j, nfonts, minerr, err, pxlSize;
1441 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1443 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1444 programName, pattern);
1451 for (i=0; i<nfonts; i++) {
1454 if (*p != '-') continue;
1456 if (*p == NULLCHAR) break;
1457 if (*p++ == '-') j++;
1459 if (j < 7) continue;
1462 scalable = fonts[i];
1465 err = pxlSize - targetPxlSize;
1466 if (Abs(err) < Abs(minerr) ||
1467 (minerr > 0 && err < 0 && -err == minerr)) {
1473 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1474 /* If the error is too big and there is a scalable font,
1475 use the scalable font. */
1476 int headlen = scalableTail - scalable;
1477 p = (char *) XtMalloc(strlen(scalable) + 10);
1478 while (isdigit(*scalableTail)) scalableTail++;
1479 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1481 p = (char *) XtMalloc(strlen(best) + 2);
1482 safeStrCpy(p, best, strlen(best)+1 );
1484 if (appData.debugMode) {
1485 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1486 pattern, targetPxlSize, p);
1488 XFreeFontNames(fonts);
1495 MarkMenuItem (char *menuRef, int state)
1497 MenuItem *item = MenuNameToItem(menuRef);
1499 if(item && item->handle) {
1500 ((GtkCheckMenuItem *) (item->handle))->active = state;
1506 EnableNamedMenuItem (char *menuRef, int state)
1508 MenuItem *item = MenuNameToItem(menuRef);
1510 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1515 EnableButtonBar (int state)
1518 XtSetSensitive(optList[W_BUTTON].handle, state);
1524 SetMenuEnables (Enables *enab)
1526 while (enab->name != NULL) {
1527 EnableNamedMenuItem(enab->name, enab->value);
1532 gboolean KeyPressProc(window, eventkey, data)
1534 GdkEventKey *eventkey;
1538 MoveTypeInProc(eventkey); // pop up for typed in moves
1541 /* check for other key values */
1542 switch(eventkey->keyval) {
1554 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1555 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1557 if(*nprms == 0) return;
1558 item = MenuNameToItem(prms[0]);
1559 if(item) ((MenuProc *) item->proc) ();
1573 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1574 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1575 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1576 dmEnables[i].piece);
1577 XtSetSensitive(entry, p != NULL || !appData.testLegality
1578 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1579 && !appData.icsActive));
1581 while (p && *p++ == dmEnables[i].piece) count++;
1582 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1584 XtSetArg(args[j], XtNlabel, label); j++;
1585 XtSetValues(entry, args, j);
1591 do_flash_delay (unsigned long msec)
1597 FlashDelay (int flash_delay)
1599 if(flash_delay) do_flash_delay(flash_delay);
1603 Fraction (int x, int start, int stop)
1605 double f = ((double) x - start)/(stop - start);
1606 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1610 static WindowPlacement wpNew;
1613 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1615 int touch=0, fudge = 4, f = 3;
1616 GetActualPlacement(sh, wp);
1617 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1618 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1619 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1620 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1621 //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);
1622 if(!touch ) return; // only windows that touch co-move
1623 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1624 int heightInc = wpNew.height - wpMain.height;
1625 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1626 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1627 wp->y += fracTop * heightInc;
1628 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1630 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1632 wp->height += heightInc;
1633 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1634 int widthInc = wpNew.width - wpMain.width;
1635 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1636 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1637 wp->y += fracLeft * widthInc;
1638 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1640 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1642 wp->width += widthInc;
1644 wp->x += wpNew.x - wpMain.x;
1645 wp->y += wpNew.y - wpMain.y;
1646 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1647 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1649 XtSetArg(args[j], XtNx, wp->x); j++;
1650 XtSetArg(args[j], XtNy, wp->y); j++;
1651 XtSetValues(sh, args, j);
1653 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1654 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1655 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1659 ReSize (WindowPlacement *wp)
1662 int sqx, sqy, w, h, lg = lineGap;
1663 static int first = 1;
1664 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
1665 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1666 w = a.width; h = a.height;
1667 gtk_widget_get_allocation(shellWidget, &a);
1668 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1669 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1670 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1671 w += a.width; h += a.height;
1673 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1674 w = a.width; h = a.height;
1676 sqx = (w - lg) / BOARD_WIDTH - lg;
1677 sqy = (h - lg) / BOARD_HEIGHT - lg;
1678 if(sqy < sqx) sqx = sqy;
1679 if(sqx < 20) return;
1680 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1682 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1683 sqx = (w - lg) / BOARD_WIDTH - lg;
1684 sqy = (h - lg) / BOARD_HEIGHT - lg;
1685 if(sqy < sqx) sqx = sqy;
1686 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1687 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1689 if(sqx != squareSize && !first) {
1690 squareSize = sqx; // adopt new square size
1691 CreatePNGPieces(); // make newly scaled pieces
1692 InitDrawingSizes(0, 0); // creates grid etc.
1693 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1694 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1695 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1696 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1697 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1698 first = appData.fixedSize;
1701 static guint delayedDragTag = 0;
1710 GetActualPlacement(shellWidget, &wpNew);
1711 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1712 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1713 busy = 0; return; // false alarm
1716 if(appData.useStickyWindows) {
1717 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1718 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1719 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1720 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1721 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1724 DrawPosition(True, NULL);
1725 if(delayedDragTag) g_source_remove(delayedDragTag);
1726 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1733 //printf("old timr = %d\n", delayedDragTag);
1734 if(delayedDragTag) g_source_remove(delayedDragTag);
1735 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1736 //printf("new timr = %d\n", delayedDragTag);
1740 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1742 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1744 wpNew.x = event->configure.x;
1745 wpNew.y = event->configure.y;
1746 wpNew.width = event->configure.width;
1747 wpNew.height = event->configure.height;
1748 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1754 /* Disable all user input other than deleting the window */
1755 static int frozen = 0;
1761 /* Grab by a widget that doesn't accept input */
1762 gtk_grab_add(optList[W_MESSG].handle);
1766 /* Undo a FreezeUI */
1770 if (!frozen) return;
1771 gtk_grab_remove(optList[W_MESSG].handle);
1778 static int oldPausing = FALSE;
1779 static GameMode oldMode = (GameMode) -1;
1781 if (!boardWidget) return;
1783 if (pausing != oldPausing) {
1784 oldPausing = pausing;
1785 MarkMenuItem("Mode.Pause", pausing);
1787 if (appData.showButtonBar) {
1788 /* Always toggle, don't set. Previous code messes up when
1789 invoked while the button is pressed, as releasing it
1790 toggles the state again. */
1792 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1793 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1797 wname = ModeToWidgetName(oldMode);
1798 if (wname != NULL) {
1799 MarkMenuItem(wname, False);
1801 wname = ModeToWidgetName(gameMode);
1802 if (wname != NULL) {
1803 MarkMenuItem(wname, True);
1805 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1806 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1809 /* Maybe all the enables should be handled here, not just this one */
1810 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1812 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1817 * Button/menu procedures
1820 void CopyFileToClipboard(gchar *filename)
1822 gchar *selection_tmp;
1826 FILE* f = fopen(filename, "r");
1829 if (f == NULL) return;
1833 selection_tmp = g_try_malloc(len + 1);
1834 if (selection_tmp == NULL) {
1835 printf("Malloc failed in CopyFileToClipboard\n");
1838 count = fread(selection_tmp, 1, len, f);
1841 g_free(selection_tmp);
1844 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1846 // copy selection_tmp to clipboard
1847 GdkDisplay *gdisp = gdk_display_get_default();
1849 g_free(selection_tmp);
1852 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1853 gtk_clipboard_set_text(cb, selection_tmp, -1);
1854 g_free(selection_tmp);
1858 CopySomething (char *src)
1860 GdkDisplay *gdisp = gdk_display_get_default();
1862 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1863 if (gdisp == NULL) return;
1864 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1865 gtk_clipboard_set_text(cb, src, -1);
1869 PastePositionProc ()
1871 GdkDisplay *gdisp = gdk_display_get_default();
1875 if (gdisp == NULL) return;
1876 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1877 fenstr = gtk_clipboard_wait_for_text(cb);
1878 if (fenstr==NULL) return; // nothing had been selected to copy
1879 EditPositionPasteFEN(fenstr);
1891 // get game from clipboard
1892 GdkDisplay *gdisp = gdk_display_get_default();
1893 if (gdisp == NULL) return;
1894 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1895 text = gtk_clipboard_wait_for_text(cb);
1896 if (text == NULL) return; // nothing to paste
1899 // write to temp file
1900 if (text == NULL || len == 0) {
1901 return; //nothing to paste
1903 f = fopen(gamePasteFilename, "w");
1905 DisplayError(_("Can't open temp file"), errno);
1908 fwrite(text, 1, len, f);
1912 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1919 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1925 void MoveTypeInProc(eventkey)
1926 GdkEventKey *eventkey;
1930 // ingnore if ctrl, alt, or meta is pressed
1931 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1935 buf[0]=eventkey->keyval;
1937 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1938 ConsoleAutoPopUp (buf);
1943 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1945 if (!TempBackwardActive) {
1946 TempBackwardActive = True;
1952 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1954 /* Check to see if triggered by a key release event for a repeating key.
1955 * If so the next queued event will be a key press of the same key at the same time */
1956 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1958 XPeekEvent(xDisplay, &next);
1959 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1960 next.xkey.keycode == event->xkey.keycode)
1964 TempBackwardActive = False;
1970 { // called from menu
1973 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
1976 system("xterm -e man xboard &");
1985 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);
1987 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
1995 SetWindowTitle (char *text, char *title, char *icon)
2000 if (appData.titleInWindow) {
2002 XtSetArg(args[i], XtNlabel, text); i++;
2003 XtSetValues(titleWidget, args, i);
2006 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2007 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2008 XtSetValues(shellWidget, args, i);
2009 XSync(xDisplay, False);
2011 if (appData.titleInWindow) {
2012 SetWidgetLabel(titleWidget, text);
2014 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2019 DisplayIcsInteractionTitle (String message)
2022 if (oldICSInteractionTitle == NULL) {
2023 /* Magic to find the old window title, adapted from vim */
2024 char *wina = getenv("WINDOWID");
2026 Window win = (Window) atoi(wina);
2027 Window root, parent, *children;
2028 unsigned int nchildren;
2029 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2031 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2032 if (!XQueryTree(xDisplay, win, &root, &parent,
2033 &children, &nchildren)) break;
2034 if (children) XFree((void *)children);
2035 if (parent == root || parent == 0) break;
2038 XSetErrorHandler(oldHandler);
2040 if (oldICSInteractionTitle == NULL) {
2041 oldICSInteractionTitle = "xterm";
2044 printf("\033]0;%s\007", message);
2051 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2053 GtkWidget *w = (GtkWidget *) opt->handle;
2060 strcpy(bgcolor, "black");
2061 strcpy(fgcolor, "white");
2063 strcpy(bgcolor, "white");
2064 strcpy(fgcolor, "black");
2067 appData.lowTimeWarning &&
2068 (timer / 1000) < appData.icsAlarmTime) {
2069 strcpy(fgcolor, appData.lowTimeWarningColor);
2072 gdk_color_parse( bgcolor, &col );
2073 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2075 if (appData.clockMode) {
2076 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2077 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2078 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2079 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2081 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2082 bgcolor, fgcolor, color);
2083 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2084 // bgcolor, fgcolor, color);
2086 gtk_label_set_markup(GTK_LABEL(w), markup);
2090 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2093 SetClockIcon (int color)
2095 GdkPixbuf *pm = *clockIcons[color];
2096 if (mainwindowIcon != pm) {
2097 mainwindowIcon = pm;
2099 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2101 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2106 #define INPUT_SOURCE_BUF_SIZE 8192
2115 char buf[INPUT_SOURCE_BUF_SIZE];
2120 DoInputCallback(io, cond, data)
2125 /* read input from one of the input source (for example a chess program, ICS, etc).
2126 * and call a function that will handle the input
2133 /* All information (callback function, file descriptor, etc) is
2134 * saved in an InputSource structure
2136 InputSource *is = (InputSource *) data;
2138 if (is->lineByLine) {
2139 count = read(is->fd, is->unused,
2140 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2142 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2143 RemoveInputSource(is); // cease reading stdin
2144 stdoutClosed = TRUE; // suppress future output
2147 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2150 is->unused += count;
2152 /* break input into lines and call the callback function on each
2155 while (p < is->unused) {
2156 q = memchr(p, '\n', is->unused - p);
2157 if (q == NULL) break;
2159 (is->func)(is, is->closure, p, q - p, 0);
2162 /* remember not yet used part of the buffer */
2164 while (p < is->unused) {
2169 /* read maximum length of input buffer and send the whole buffer
2170 * to the callback function
2172 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2177 (is->func)(is, is->closure, is->buf, count, error);
2179 return True; // Must return true or the watch will be removed
2182 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2189 GIOChannel *channel;
2190 ChildProc *cp = (ChildProc *) pr;
2192 is = (InputSource *) calloc(1, sizeof(InputSource));
2193 is->lineByLine = lineByLine;
2197 is->fd = fileno(stdin);
2199 is->kind = cp->kind;
2200 is->fd = cp->fdFrom;
2203 is->unused = is->buf;
2207 /* GTK-TODO: will this work on windows?*/
2209 channel = g_io_channel_unix_new(is->fd);
2210 g_io_channel_set_close_on_unref (channel, TRUE);
2211 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2213 is->closure = closure;
2214 return (InputSourceRef) is;
2219 RemoveInputSource(isr)
2222 InputSource *is = (InputSource *) isr;
2224 if (is->sid == 0) return;
2225 g_source_remove(is->sid);
2232 static Boolean frameWaiting;
2235 FrameAlarm (int sig)
2237 frameWaiting = False;
2238 /* In case System-V style signals. Needed?? */
2239 signal(SIGALRM, FrameAlarm);
2243 FrameDelay (int time)
2245 struct itimerval delay;
2248 frameWaiting = True;
2249 signal(SIGALRM, FrameAlarm);
2250 delay.it_interval.tv_sec =
2251 delay.it_value.tv_sec = time / 1000;
2252 delay.it_interval.tv_usec =
2253 delay.it_value.tv_usec = (time % 1000) * 1000;
2254 setitimer(ITIMER_REAL, &delay, NULL);
2255 while (frameWaiting) pause();
2256 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2257 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2258 setitimer(ITIMER_REAL, &delay, NULL);
2265 FrameDelay (int time)
2268 XSync(xDisplay, False);
2270 // gtk_main_iteration_do(False);
2273 usleep(time * 1000);
2279 FindLogo (char *place, char *name, char *buf)
2280 { // check if file exists in given place
2282 if(!place) return 0;
2283 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2284 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2292 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2294 char buf[MSG_SIZ], *logoName = buf;
2295 if(appData.logo[n][0]) {
2296 logoName = appData.logo[n];
2297 } else if(appData.autoLogo) {
2298 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2299 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2300 } else { // engine; cascade
2301 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2302 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2303 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2304 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2308 { ASSIGN(cps->programLogo, logoName); }
2312 UpdateLogos (int displ)
2314 if(optList[W_WHITE-1].handle == NULL) return;
2315 LoadLogo(&first, 0, 0);
2316 LoadLogo(&second, 1, appData.icsActive);
2317 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2321 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2332 GtkFileFilter *gtkfilter;
2333 GtkFileFilter *gtkfilter_all;
2335 char fileext[10] = "";
2336 char *result = NULL;
2338 char curDir[MSG_SIZ];
2340 StartDir(filter, NULL); // change to start directory for this file type
2342 if(def && *def && def[strlen(def)-1] == '/') {
2343 getcwd(curDir, MSG_SIZ);
2348 /* make a copy of the filter string, so that strtok can work with it*/
2349 cp = strdup(filter);
2351 /* add filters for file extensions */
2352 gtkfilter = gtk_file_filter_new();
2353 gtkfilter_all = gtk_file_filter_new();
2355 /* one filter to show everything */
2356 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2357 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2359 /* add filter if present */
2360 result = strtok(cp, space);
2361 while( result != NULL ) {
2362 snprintf(fileext,10,"*%s",result);
2363 result = strtok( NULL, space );
2364 gtk_file_filter_add_pattern(gtkfilter, fileext);
2367 /* second filter to only show what's useful */
2368 gtk_file_filter_set_name (gtkfilter,filter);
2370 if (openMode[0] == 'r')
2372 dialog = gtk_file_chooser_dialog_new (label,
2374 GTK_FILE_CHOOSER_ACTION_OPEN,
2375 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2376 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2381 dialog = gtk_file_chooser_dialog_new (label,
2383 GTK_FILE_CHOOSER_ACTION_SAVE,
2384 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2385 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2387 /* add filename suggestions */
2388 if (strlen(def) > 0 )
2389 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2391 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2395 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2396 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2397 /* activate filter */
2398 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2400 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2405 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2408 f = fopen(filename, openMode);
2411 DisplayError(_("Failed to open file"), errno);
2415 /* TODO add indec */
2417 ASSIGN(*name, filename);
2418 ScheduleDelayedEvent(DelayedLoad, 50);
2420 StartDir(filter, filename);
2423 else StartDir(filter, "");
2425 gtk_widget_destroy (dialog);
2428 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);