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/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
843 if(1000*now.sec + now.ms - 1000*started.sec - started.ms < 1000) { // received during first second
844 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
845 } else { // we are running something presumably useful
847 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
848 system(buf); // start new instance on this file
853 GtkosxApplication *theApp;
857 main (int argc, char **argv)
859 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
860 int boardWidth, w, h; //, boardHeight;
862 int forceMono = False;
864 srandom(time(0)); // [HGM] book: make random truly random
866 setbuf(stdout, NULL);
867 setbuf(stderr, NULL);
870 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
871 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
875 if(argc > 1 && !strcmp(argv[1], "--help" )) {
881 gtk_init (&argc, &argv);
883 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
884 char *path = gtkosx_application_get_bundle_path();
886 char *res_path = gtkosx_application_get_resource_path();
887 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
889 GetTimeMark(&started); // remember start time
890 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
891 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
892 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
893 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
894 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
895 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
896 // we must call application ready before we can get the signal,
897 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
898 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
899 gtkosx_application_ready(theApp);
900 if(argc == 1) { // called without args: OSX open-file signal might follow
901 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
902 usleep(10000); // wait 10 msec (and hope this is long enough).
903 while(gtk_events_pending())
904 gtk_main_iteration(); // process all events that came in upto now
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 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // scale height
1027 /* Find some defaults; use the nearest known size */
1028 SizeDefaults *szd, *nearest;
1029 int distance = 99999;
1030 nearest = szd = sizeDefaults;
1031 while (szd->name != NULL) {
1032 if (abs(szd->squareSize - squareSize) < distance) {
1034 distance = abs(szd->squareSize - squareSize);
1035 if (distance == 0) break;
1039 if (i < 2) lineGap = nearest->lineGap;
1040 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1041 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1042 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1043 if (i < 6) smallLayout = nearest->smallLayout;
1044 if (i < 7) tinyLayout = nearest->tinyLayout;
1047 SizeDefaults *szd = sizeDefaults;
1048 if (*appData.boardSize == NULLCHAR) {
1049 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1050 GdkScreen *screen = gdk_screen_get_default();
1051 guint screenwidth = gdk_screen_get_width(screen);
1052 guint screenheight = gdk_screen_get_height(screen);
1053 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1054 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1057 if (szd->name == NULL) szd--;
1058 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1060 while (szd->name != NULL &&
1061 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1062 if (szd->name == NULL) {
1063 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1064 programName, appData.boardSize);
1068 squareSize = szd->squareSize;
1069 lineGap = szd->lineGap;
1070 clockFontPxlSize = szd->clockFontPxlSize;
1071 coordFontPxlSize = szd->coordFontPxlSize;
1072 fontPxlSize = szd->fontPxlSize;
1073 smallLayout = szd->smallLayout;
1074 tinyLayout = szd->tinyLayout;
1075 // [HGM] font: use defaults from settings file if available and not overruled
1078 defaultLineGap = lineGap;
1079 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1081 /* [HR] height treated separately (hacked) */
1082 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1083 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1086 * Determine what fonts to use.
1088 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1091 * Detect if there are not enough colors available and adapt.
1094 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1095 appData.monoMode = True;
1099 forceMono = MakeColors();
1102 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1104 appData.monoMode = True;
1107 ParseIcsTextColors();
1113 layoutName = "tinyLayout";
1114 } else if (smallLayout) {
1115 layoutName = "smallLayout";
1117 layoutName = "normalLayout";
1120 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1121 wpMain.width = -1; // prevent popup sizes window
1122 optList = BoardPopUp(squareSize, lineGap, (void*)
1132 InitDrawingHandle(optList + W_BOARD);
1133 shellWidget = shells[BoardWindow];
1134 currBoard = &optList[W_BOARD];
1135 boardWidget = optList[W_BOARD].handle;
1136 menuBarWidget = optList[W_MENU].handle;
1137 dropMenu = optList[W_DROP].handle;
1138 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1140 formWidget = XtParent(boardWidget);
1141 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1142 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1143 XtGetValues(optList[W_WHITE].handle, args, 2);
1144 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1145 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1146 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1147 XtGetValues(optList[W_PAUSE].handle, args, 2);
1151 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1152 // not need to go into InitDrawingSizes().
1156 // add accelerators to main shell
1157 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1160 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1162 WhiteIcon = LoadIconFile("icon_white");
1163 BlackIcon = LoadIconFile("icon_black");
1164 SetClockIcon(0); // sets white icon
1168 * Create a cursor for the board widget.
1171 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1172 XChangeWindowAttributes(xDisplay, xBoardWindow,
1173 CWCursor, &window_attributes);
1177 * Inhibit shell resizing.
1180 shellArgs[0].value = (XtArgVal) &w;
1181 shellArgs[1].value = (XtArgVal) &h;
1182 XtGetValues(shellWidget, shellArgs, 2);
1183 shellArgs[4].value = shellArgs[2].value = w;
1184 shellArgs[5].value = shellArgs[3].value = h;
1185 // XtSetValues(shellWidget, &shellArgs[2], 4);
1188 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1189 // It wil only become known asynchronously, when we first write a string into it.
1190 // This will then change the clock widget height, which triggers resizing the top-level window
1191 // and a configure event. Only then can we know the total height of the top-level window,
1192 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1193 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1196 gtk_widget_get_allocation(shells[BoardWindow], &a);
1197 w = a.width; h = a.height;
1198 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1199 clockKludge = hc = a.height;
1200 gtk_widget_get_allocation(boardWidget, &a);
1201 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1202 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1208 if(appData.logoSize)
1209 { // locate and read user logo
1210 char buf[MSG_SIZ], name[MSG_SIZ];
1211 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1212 if(!FindLogo(name, ".logo", buf))
1213 FindLogo(appData.logoDir, name + 6, buf);
1214 ASSIGN(userLogo, buf);
1217 if (appData.animate || appData.animateDragging)
1220 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1221 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1223 /* [AS] Restore layout */
1224 if( wpMoveHistory.visible ) {
1228 if( wpEvalGraph.visible )
1233 if( wpEngineOutput.visible ) {
1234 EngineOutputPopUp();
1237 if( wpConsole.visible && appData.icsActive ) {
1242 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1247 if (errorExitStatus == -1) {
1248 if (appData.icsActive) {
1249 /* We now wait until we see "login:" from the ICS before
1250 sending the logon script (problems with timestamp otherwise) */
1251 /*ICSInitScript();*/
1252 if (appData.icsInputBox) ICSInputBoxPopUp();
1256 signal(SIGWINCH, TermSizeSigHandler);
1258 signal(SIGINT, IntSigHandler);
1259 signal(SIGTERM, IntSigHandler);
1260 if (*appData.cmailGameName != NULLCHAR) {
1261 signal(SIGUSR1, CmailSigHandler);
1266 // XtSetKeyboardFocus(shellWidget, formWidget);
1268 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1271 /* check for GTK events and process them */
1274 gtk_main_iteration();
1277 if (appData.debugMode) fclose(debugFP); // [DM] debug
1284 while(gtk_events_pending()) gtk_main_iteration();
1288 TermSizeSigHandler (int sig)
1294 IntSigHandler (int sig)
1300 CmailSigHandler (int sig)
1305 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1307 /* Activate call-back function CmailSigHandlerCallBack() */
1308 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1310 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1314 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1317 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1319 /**** end signal code ****/
1322 #define Abs(n) ((n)<0 ? -(n) : (n))
1325 InsertPxlSize (char *pattern, int targetPxlSize)
1328 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1335 InsertPxlSize (char *pattern, int targetPxlSize)
1337 char *base_fnt_lst, strInt[12], *p, *q;
1338 int alternatives, i, len, strIntLen;
1341 * Replace the "*" (if present) in the pixel-size slot of each
1342 * alternative with the targetPxlSize.
1346 while ((p = strchr(p, ',')) != NULL) {
1350 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1351 strIntLen = strlen(strInt);
1352 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1356 while (alternatives--) {
1357 char *comma = strchr(p, ',');
1358 for (i=0; i<14; i++) {
1359 char *hyphen = strchr(p, '-');
1361 if (comma && hyphen > comma) break;
1362 len = hyphen + 1 - p;
1363 if (i == 7 && *p == '*' && len == 2) {
1365 memcpy(q, strInt, strIntLen);
1375 len = comma + 1 - p;
1382 return base_fnt_lst;
1388 CreateFontSet (char *base_fnt_lst)
1391 char **missing_list;
1395 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1396 &missing_list, &missing_count, &def_string);
1397 if (appData.debugMode) {
1399 XFontStruct **font_struct_list;
1400 char **font_name_list;
1401 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1403 fprintf(debugFP, " got list %s, locale %s\n",
1404 XBaseFontNameListOfFontSet(fntSet),
1405 XLocaleOfFontSet(fntSet));
1406 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1407 for (i = 0; i < count; i++) {
1408 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1411 for (i = 0; i < missing_count; i++) {
1412 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1415 if (fntSet == NULL) {
1416 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1422 #else // not ENABLE_NLS
1424 * Find a font that matches "pattern" that is as close as
1425 * possible to the targetPxlSize. Prefer fonts that are k
1426 * pixels smaller to fonts that are k pixels larger. The
1427 * pattern must be in the X Consortium standard format,
1428 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1429 * The return value should be freed with XtFree when no
1434 FindFont (char *pattern, int targetPxlSize)
1436 char **fonts, *p, *best, *scalable, *scalableTail;
1437 int i, j, nfonts, minerr, err, pxlSize;
1439 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1441 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1442 programName, pattern);
1449 for (i=0; i<nfonts; i++) {
1452 if (*p != '-') continue;
1454 if (*p == NULLCHAR) break;
1455 if (*p++ == '-') j++;
1457 if (j < 7) continue;
1460 scalable = fonts[i];
1463 err = pxlSize - targetPxlSize;
1464 if (Abs(err) < Abs(minerr) ||
1465 (minerr > 0 && err < 0 && -err == minerr)) {
1471 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1472 /* If the error is too big and there is a scalable font,
1473 use the scalable font. */
1474 int headlen = scalableTail - scalable;
1475 p = (char *) XtMalloc(strlen(scalable) + 10);
1476 while (isdigit(*scalableTail)) scalableTail++;
1477 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1479 p = (char *) XtMalloc(strlen(best) + 2);
1480 safeStrCpy(p, best, strlen(best)+1 );
1482 if (appData.debugMode) {
1483 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1484 pattern, targetPxlSize, p);
1486 XFreeFontNames(fonts);
1493 MarkMenuItem (char *menuRef, int state)
1495 MenuItem *item = MenuNameToItem(menuRef);
1497 if(item && item->handle) {
1498 ((GtkCheckMenuItem *) (item->handle))->active = state;
1504 EnableNamedMenuItem (char *menuRef, int state)
1506 MenuItem *item = MenuNameToItem(menuRef);
1508 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1513 EnableButtonBar (int state)
1516 XtSetSensitive(optList[W_BUTTON].handle, state);
1522 SetMenuEnables (Enables *enab)
1524 while (enab->name != NULL) {
1525 EnableNamedMenuItem(enab->name, enab->value);
1530 gboolean KeyPressProc(window, eventkey, data)
1532 GdkEventKey *eventkey;
1536 MoveTypeInProc(eventkey); // pop up for typed in moves
1539 /* check for other key values */
1540 switch(eventkey->keyval) {
1552 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1553 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1555 if(*nprms == 0) return;
1556 item = MenuNameToItem(prms[0]);
1557 if(item) ((MenuProc *) item->proc) ();
1571 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1572 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1573 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1574 dmEnables[i].piece);
1575 XtSetSensitive(entry, p != NULL || !appData.testLegality
1576 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1577 && !appData.icsActive));
1579 while (p && *p++ == dmEnables[i].piece) count++;
1580 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1582 XtSetArg(args[j], XtNlabel, label); j++;
1583 XtSetValues(entry, args, j);
1589 do_flash_delay (unsigned long msec)
1595 FlashDelay (int flash_delay)
1597 if(flash_delay) do_flash_delay(flash_delay);
1601 Fraction (int x, int start, int stop)
1603 double f = ((double) x - start)/(stop - start);
1604 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1608 static WindowPlacement wpNew;
1611 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1613 int touch=0, fudge = 4, f = 3;
1614 GetActualPlacement(sh, wp);
1615 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1616 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1617 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1618 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1619 //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);
1620 if(!touch ) return; // only windows that touch co-move
1621 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1622 int heightInc = wpNew.height - wpMain.height;
1623 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1624 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1625 wp->y += fracTop * heightInc;
1626 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1628 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1630 wp->height += heightInc;
1631 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1632 int widthInc = wpNew.width - wpMain.width;
1633 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1634 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1635 wp->y += fracLeft * widthInc;
1636 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1638 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1640 wp->width += widthInc;
1642 wp->x += wpNew.x - wpMain.x;
1643 wp->y += wpNew.y - wpMain.y;
1644 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1645 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1647 XtSetArg(args[j], XtNx, wp->x); j++;
1648 XtSetArg(args[j], XtNy, wp->y); j++;
1649 XtSetValues(sh, args, j);
1651 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1652 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1653 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1657 ReSize (WindowPlacement *wp)
1660 int sqx, sqy, w, h, lg = lineGap;
1661 static int first = 1;
1662 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
1663 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1664 w = a.width; h = a.height;
1665 gtk_widget_get_allocation(shellWidget, &a);
1666 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1667 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1668 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1669 w += a.width; h += a.height;
1671 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1672 w = a.width; h = a.height;
1674 sqx = (w - lg) / BOARD_WIDTH - lg;
1675 sqy = (h - lg) / BOARD_HEIGHT - lg;
1676 if(sqy < sqx) sqx = sqy;
1677 if(sqx < 20) return;
1678 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1680 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1681 sqx = (w - lg) / BOARD_WIDTH - lg;
1682 sqy = (h - lg) / BOARD_HEIGHT - lg;
1683 if(sqy < sqx) sqx = sqy;
1684 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1685 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1687 if(sqx != squareSize && !first) {
1688 squareSize = sqx; // adopt new square size
1689 CreatePNGPieces(); // make newly scaled pieces
1690 InitDrawingSizes(0, 0); // creates grid etc.
1691 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1692 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1693 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1694 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1695 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1696 first = appData.fixedSize;
1699 static guint delayedDragTag = 0;
1708 GetActualPlacement(shellWidget, &wpNew);
1709 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1710 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1711 busy = 0; return; // false alarm
1714 if(appData.useStickyWindows) {
1715 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1716 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1717 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1718 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1719 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1722 DrawPosition(True, NULL);
1723 if(delayedDragTag) g_source_remove(delayedDragTag);
1724 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1731 //printf("old timr = %d\n", delayedDragTag);
1732 if(delayedDragTag) g_source_remove(delayedDragTag);
1733 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1734 //printf("new timr = %d\n", delayedDragTag);
1738 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1740 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1742 wpNew.x = event->configure.x;
1743 wpNew.y = event->configure.y;
1744 wpNew.width = event->configure.width;
1745 wpNew.height = event->configure.height;
1746 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1752 /* Disable all user input other than deleting the window */
1753 static int frozen = 0;
1759 /* Grab by a widget that doesn't accept input */
1760 gtk_grab_add(optList[W_MESSG].handle);
1764 /* Undo a FreezeUI */
1768 if (!frozen) return;
1769 gtk_grab_remove(optList[W_MESSG].handle);
1776 static int oldPausing = FALSE;
1777 static GameMode oldmode = (GameMode) -1;
1779 if (!boardWidget) return;
1781 if (pausing != oldPausing) {
1782 oldPausing = pausing;
1783 MarkMenuItem("Mode.Pause", pausing);
1785 if (appData.showButtonBar) {
1786 /* Always toggle, don't set. Previous code messes up when
1787 invoked while the button is pressed, as releasing it
1788 toggles the state again. */
1790 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1791 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1795 wname = ModeToWidgetName(oldmode);
1796 if (wname != NULL) {
1797 MarkMenuItem(wname, False);
1799 wname = ModeToWidgetName(gameMode);
1800 if (wname != NULL) {
1801 MarkMenuItem(wname, True);
1804 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1806 /* Maybe all the enables should be handled here, not just this one */
1807 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1809 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1814 * Button/menu procedures
1817 void CopyFileToClipboard(gchar *filename)
1819 gchar *selection_tmp;
1823 FILE* f = fopen(filename, "r");
1826 if (f == NULL) return;
1830 selection_tmp = g_try_malloc(len + 1);
1831 if (selection_tmp == NULL) {
1832 printf("Malloc failed in CopyFileToClipboard\n");
1835 count = fread(selection_tmp, 1, len, f);
1838 g_free(selection_tmp);
1841 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1843 // copy selection_tmp to clipboard
1844 GdkDisplay *gdisp = gdk_display_get_default();
1846 g_free(selection_tmp);
1849 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1850 gtk_clipboard_set_text(cb, selection_tmp, -1);
1851 g_free(selection_tmp);
1855 CopySomething (char *src)
1857 GdkDisplay *gdisp = gdk_display_get_default();
1859 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1860 if (gdisp == NULL) return;
1861 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1862 gtk_clipboard_set_text(cb, src, -1);
1866 PastePositionProc ()
1868 GdkDisplay *gdisp = gdk_display_get_default();
1872 if (gdisp == NULL) return;
1873 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1874 fenstr = gtk_clipboard_wait_for_text(cb);
1875 if (fenstr==NULL) return; // nothing had been selected to copy
1876 EditPositionPasteFEN(fenstr);
1888 // get game from clipboard
1889 GdkDisplay *gdisp = gdk_display_get_default();
1890 if (gdisp == NULL) return;
1891 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1892 text = gtk_clipboard_wait_for_text(cb);
1893 if (text == NULL) return; // nothing to paste
1896 // write to temp file
1897 if (text == NULL || len == 0) {
1898 return; //nothing to paste
1900 f = fopen(gamePasteFilename, "w");
1902 DisplayError(_("Can't open temp file"), errno);
1905 fwrite(text, 1, len, f);
1909 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1916 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1922 void MoveTypeInProc(eventkey)
1923 GdkEventKey *eventkey;
1927 // ingnore if ctrl, alt, or meta is pressed
1928 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1932 buf[0]=eventkey->keyval;
1934 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1935 ConsoleAutoPopUp (buf);
1940 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1942 if (!TempBackwardActive) {
1943 TempBackwardActive = True;
1949 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1951 /* Check to see if triggered by a key release event for a repeating key.
1952 * If so the next queued event will be a key press of the same key at the same time */
1953 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1955 XPeekEvent(xDisplay, &next);
1956 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1957 next.xkey.keycode == event->xkey.keycode)
1961 TempBackwardActive = False;
1967 { // called from menu
1970 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
1973 system("xterm -e man xboard &");
1982 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);
1984 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
1992 SetWindowTitle (char *text, char *title, char *icon)
1997 if (appData.titleInWindow) {
1999 XtSetArg(args[i], XtNlabel, text); i++;
2000 XtSetValues(titleWidget, args, i);
2003 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2004 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2005 XtSetValues(shellWidget, args, i);
2006 XSync(xDisplay, False);
2008 if (appData.titleInWindow) {
2009 SetWidgetLabel(titleWidget, text);
2011 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2016 DisplayIcsInteractionTitle (String message)
2019 if (oldICSInteractionTitle == NULL) {
2020 /* Magic to find the old window title, adapted from vim */
2021 char *wina = getenv("WINDOWID");
2023 Window win = (Window) atoi(wina);
2024 Window root, parent, *children;
2025 unsigned int nchildren;
2026 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2028 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2029 if (!XQueryTree(xDisplay, win, &root, &parent,
2030 &children, &nchildren)) break;
2031 if (children) XFree((void *)children);
2032 if (parent == root || parent == 0) break;
2035 XSetErrorHandler(oldHandler);
2037 if (oldICSInteractionTitle == NULL) {
2038 oldICSInteractionTitle = "xterm";
2041 printf("\033]0;%s\007", message);
2048 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2050 GtkWidget *w = (GtkWidget *) opt->handle;
2057 strcpy(bgcolor, "black");
2058 strcpy(fgcolor, "white");
2060 strcpy(bgcolor, "white");
2061 strcpy(fgcolor, "black");
2064 appData.lowTimeWarning &&
2065 (timer / 1000) < appData.icsAlarmTime) {
2066 strcpy(fgcolor, appData.lowTimeWarningColor);
2069 gdk_color_parse( bgcolor, &col );
2070 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2072 if (appData.clockMode) {
2073 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2074 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2075 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2076 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2078 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2079 bgcolor, fgcolor, color);
2080 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2081 // bgcolor, fgcolor, color);
2083 gtk_label_set_markup(GTK_LABEL(w), markup);
2087 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2090 SetClockIcon (int color)
2092 GdkPixbuf *pm = *clockIcons[color];
2093 if (mainwindowIcon != pm) {
2094 mainwindowIcon = pm;
2096 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2098 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2103 #define INPUT_SOURCE_BUF_SIZE 8192
2112 char buf[INPUT_SOURCE_BUF_SIZE];
2117 DoInputCallback(io, cond, data)
2122 /* read input from one of the input source (for example a chess program, ICS, etc).
2123 * and call a function that will handle the input
2130 /* All information (callback function, file descriptor, etc) is
2131 * saved in an InputSource structure
2133 InputSource *is = (InputSource *) data;
2135 if (is->lineByLine) {
2136 count = read(is->fd, is->unused,
2137 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2139 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2140 RemoveInputSource(is); // cease reading stdin
2141 stdoutClosed = TRUE; // suppress future output
2144 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2147 is->unused += count;
2149 /* break input into lines and call the callback function on each
2152 while (p < is->unused) {
2153 q = memchr(p, '\n', is->unused - p);
2154 if (q == NULL) break;
2156 (is->func)(is, is->closure, p, q - p, 0);
2159 /* remember not yet used part of the buffer */
2161 while (p < is->unused) {
2166 /* read maximum length of input buffer and send the whole buffer
2167 * to the callback function
2169 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2174 (is->func)(is, is->closure, is->buf, count, error);
2176 return True; // Must return true or the watch will be removed
2179 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2186 GIOChannel *channel;
2187 ChildProc *cp = (ChildProc *) pr;
2189 is = (InputSource *) calloc(1, sizeof(InputSource));
2190 is->lineByLine = lineByLine;
2194 is->fd = fileno(stdin);
2196 is->kind = cp->kind;
2197 is->fd = cp->fdFrom;
2200 is->unused = is->buf;
2204 /* GTK-TODO: will this work on windows?*/
2206 channel = g_io_channel_unix_new(is->fd);
2207 g_io_channel_set_close_on_unref (channel, TRUE);
2208 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2210 is->closure = closure;
2211 return (InputSourceRef) is;
2216 RemoveInputSource(isr)
2219 InputSource *is = (InputSource *) isr;
2221 if (is->sid == 0) return;
2222 g_source_remove(is->sid);
2229 static Boolean frameWaiting;
2232 FrameAlarm (int sig)
2234 frameWaiting = False;
2235 /* In case System-V style signals. Needed?? */
2236 signal(SIGALRM, FrameAlarm);
2240 FrameDelay (int time)
2242 struct itimerval delay;
2245 frameWaiting = True;
2246 signal(SIGALRM, FrameAlarm);
2247 delay.it_interval.tv_sec =
2248 delay.it_value.tv_sec = time / 1000;
2249 delay.it_interval.tv_usec =
2250 delay.it_value.tv_usec = (time % 1000) * 1000;
2251 setitimer(ITIMER_REAL, &delay, NULL);
2252 while (frameWaiting) pause();
2253 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2254 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2255 setitimer(ITIMER_REAL, &delay, NULL);
2262 FrameDelay (int time)
2265 XSync(xDisplay, False);
2267 // gtk_main_iteration_do(False);
2270 usleep(time * 1000);
2276 FindLogo (char *place, char *name, char *buf)
2277 { // check if file exists in given place
2279 if(!place) return 0;
2280 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2281 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2289 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2291 char buf[MSG_SIZ], *logoName = buf;
2292 if(appData.logo[n][0]) {
2293 logoName = appData.logo[n];
2294 } else if(appData.autoLogo) {
2295 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2296 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2297 } else { // engine; cascade
2298 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2299 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2300 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2301 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2305 { ASSIGN(cps->programLogo, logoName); }
2309 UpdateLogos (int displ)
2311 if(optList[W_WHITE-1].handle == NULL) return;
2312 LoadLogo(&first, 0, 0);
2313 LoadLogo(&second, 1, appData.icsActive);
2314 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2318 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2329 GtkFileFilter *gtkfilter;
2330 GtkFileFilter *gtkfilter_all;
2332 char fileext[10] = "";
2333 char *result = NULL;
2335 char curDir[MSG_SIZ];
2337 StartDir(filter, NULL); // change to start directory for this file type
2339 if(def && *def && def[strlen(def)-1] == '/') {
2340 getcwd(curDir, MSG_SIZ);
2345 /* make a copy of the filter string, so that strtok can work with it*/
2346 cp = strdup(filter);
2348 /* add filters for file extensions */
2349 gtkfilter = gtk_file_filter_new();
2350 gtkfilter_all = gtk_file_filter_new();
2352 /* one filter to show everything */
2353 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2354 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2356 /* add filter if present */
2357 result = strtok(cp, space);
2358 while( result != NULL ) {
2359 snprintf(fileext,10,"*%s",result);
2360 result = strtok( NULL, space );
2361 gtk_file_filter_add_pattern(gtkfilter, fileext);
2364 /* second filter to only show what's useful */
2365 gtk_file_filter_set_name (gtkfilter,filter);
2367 if (openMode[0] == 'r')
2369 dialog = gtk_file_chooser_dialog_new (label,
2371 GTK_FILE_CHOOSER_ACTION_OPEN,
2372 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2373 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2378 dialog = gtk_file_chooser_dialog_new (label,
2380 GTK_FILE_CHOOSER_ACTION_SAVE,
2381 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2382 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2384 /* add filename suggestions */
2385 if (strlen(def) > 0 )
2386 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2388 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2392 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2393 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2394 /* activate filter */
2395 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2397 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2402 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2405 f = fopen(filename, openMode);
2408 DisplayError(_("Failed to open file"), errno);
2412 /* TODO add indec */
2414 ASSIGN(*name, filename);
2415 ScheduleDelayedEvent(DelayedLoad, 50);
2417 StartDir(filter, filename);
2420 else StartDir(filter, "");
2422 gtk_widget_destroy (dialog);
2425 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);