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, 2016 Free
9 * Software Foundation, Inc.
11 * The following terms apply to Digital Equipment Corporation's copyright
13 * ------------------------------------------------------------------------
16 * Permission to use, copy, modify, and distribute this software and its
17 * documentation for any purpose and without fee is hereby granted,
18 * provided that the above copyright notice appear in all copies and that
19 * both that copyright notice and this permission notice appear in
20 * supporting documentation, and that the name of Digital not be
21 * used in advertising or publicity pertaining to distribution of the
22 * software without specific, written prior permission.
24 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31 * ------------------------------------------------------------------------
33 * The following terms apply to the enhanced version of XBoard
34 * distributed by the Free Software Foundation:
35 * ------------------------------------------------------------------------
37 * GNU XBoard is free software: you can redistribute it and/or modify
38 * it under the terms of the GNU General Public License as published by
39 * the Free Software Foundation, either version 3 of the License, or (at
40 * your option) any later version.
42 * GNU XBoard is distributed in the hope that it will be useful, but
43 * WITHOUT ANY WARRANTY; without even the implied warranty of
44 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
45 * General Public License for more details.
47 * You should have received a copy of the GNU General Public License
48 * along with this program. If not, see http://www.gnu.org/licenses/. *
50 *------------------------------------------------------------------------
51 ** See the file ChangeLog for a revision history. */
61 #include <sys/types.h>
65 #include <cairo/cairo.h>
66 #include <cairo/cairo-xlib.h>
70 # if HAVE_SYS_SOCKET_H
71 # include <sys/socket.h>
72 # include <netinet/in.h>
74 # else /* not HAVE_SYS_SOCKET_H */
75 # if HAVE_LAN_SOCKET_H
76 # include <lan/socket.h>
78 # include <lan/netdb.h>
79 # else /* not HAVE_LAN_SOCKET_H */
80 # define OMIT_SOCKETS 1
81 # endif /* not HAVE_LAN_SOCKET_H */
82 # endif /* not HAVE_SYS_SOCKET_H */
83 #endif /* !OMIT_SOCKETS */
88 #else /* not STDC_HEADERS */
89 extern char *getenv();
92 # else /* not HAVE_STRING_H */
94 # endif /* not HAVE_STRING_H */
95 #endif /* not STDC_HEADERS */
98 # include <sys/fcntl.h>
99 #else /* not HAVE_SYS_FCNTL_H */
102 # endif /* HAVE_FCNTL_H */
103 #endif /* not HAVE_SYS_FCNTL_H */
105 #if HAVE_SYS_SYSTEMINFO_H
106 # include <sys/systeminfo.h>
107 #endif /* HAVE_SYS_SYSTEMINFO_H */
109 #if TIME_WITH_SYS_TIME
110 # include <sys/time.h>
114 # include <sys/time.h>
125 # include <sys/wait.h>
130 # define NAMLEN(dirent) strlen((dirent)->d_name)
131 # define HAVE_DIR_STRUCT
133 # define dirent direct
134 # define NAMLEN(dirent) (dirent)->d_namlen
136 # include <sys/ndir.h>
137 # define HAVE_DIR_STRUCT
140 # include <sys/dir.h>
141 # define HAVE_DIR_STRUCT
145 # define HAVE_DIR_STRUCT
153 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
156 #include "frontend.h"
158 #include "backendz.h"
166 #include "engineoutput.h"
172 # include <gtkmacintegration/gtkosxapplication.h>
173 // prevent pathname of positional file argument provided by OS X being be mistaken for option name
174 // (price is that we won't recognize Windows option format anymore).
177 // redefine some defaults
181 # undef SETTINGS_FILE
182 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
183 # define DATADIR dataDir
184 # define LOCALEDIR localeDir
185 # define SETTINGS_FILE masterSettings
186 # define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
187 char dataDir[MSG_SIZ]; // for expanding ~~
188 char localeDir[MSG_SIZ];
189 char masterSettings[MSG_SIZ];
193 # define SYNC_MENUBAR
200 #define usleep(t) _sleep2(((t)+500)/1000)
204 # define _(s) gettext (s)
205 # define N_(s) gettext_noop (s)
211 int main P((int argc, char **argv));
212 RETSIGTYPE CmailSigHandler P((int sig));
213 RETSIGTYPE IntSigHandler P((int sig));
214 RETSIGTYPE TermSizeSigHandler P((int sig));
215 char *InsertPxlSize P((char *pattern, int targetPxlSize));
217 XFontSet CreateFontSet P((char *base_fnt_lst));
219 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
844 if(1000*now.sec + now.ms - 1000*started.sec - started.ms < 1000) { // received during first second
845 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
846 } else { // we are running something presumably useful
848 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
849 system(buf); // start new instance on this file
854 GtkosxApplication *theApp;
858 main (int argc, char **argv)
860 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
861 int boardWidth, w, h; //, boardHeight;
863 int forceMono = False;
865 srandom(time(0)); // [HGM] book: make random truly random
867 setbuf(stdout, NULL);
868 setbuf(stderr, NULL);
871 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
872 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
876 if(argc > 1 && !strcmp(argv[1], "--help" )) {
882 gtk_init (&argc, &argv);
884 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
885 char *path = gtkosx_application_get_bundle_path();
887 char *res_path = gtkosx_application_get_resource_path();
888 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
890 GetTimeMark(&started); // remember start time
891 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
892 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
893 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
894 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
895 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
896 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
897 // we must call application ready before we can get the signal,
898 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
899 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
900 gtkosx_application_ready(theApp);
901 if(argc == 1) { // called without args: OSX open-file signal might follow
902 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
903 usleep(10000); // wait 10 msec (and hope this is long enough).
904 while(gtk_events_pending())
905 gtk_main_iteration(); // process all events that came in upto now
906 if(clickedFile[0]) { // we were sent an open-file signal with filename!
907 fakeArgv[0] = argv[0];
908 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
914 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
915 typedef struct {char *name, *value; } Config;
916 static Config configList[] = {
917 { "Datadir", DATADIR },
918 { "Sysconfdir", SYSCONFDIR },
923 for(i=0; configList[i].name; i++) {
924 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
925 if(argc > 2) printf("%s", configList[i].value);
926 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
931 /* set up keyboard accelerators group */
932 GtkAccelerators = gtk_accel_group_new();
934 programName = strrchr(argv[0], '/');
935 if (programName == NULL)
936 programName = argv[0];
941 // if (appData.debugMode) {
942 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
945 bindtextdomain(PACKAGE, LOCALEDIR);
946 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
950 appData.boardSize = "";
951 InitAppData(ConvertToLine(argc, argv));
953 if (p == NULL) p = "/tmp";
954 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
955 gameCopyFilename = (char*) malloc(i);
956 gamePasteFilename = (char*) malloc(i);
957 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
958 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
960 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
961 static char buf[MSG_SIZ];
962 snprintf(buf, MSG_SIZ, appData.sysOpen, DATADIR);
963 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
964 EscapeExpand(buf, appData.firstInitString);
965 appData.firstInitString = strdup(buf);
966 EscapeExpand(buf, appData.secondInitString);
967 appData.secondInitString = strdup(buf);
968 EscapeExpand(buf, appData.firstComputerString);
969 appData.firstComputerString = strdup(buf);
970 EscapeExpand(buf, appData.secondComputerString);
971 appData.secondComputerString = strdup(buf);
974 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
977 if (chdir(chessDir) != 0) {
978 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
984 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
985 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
986 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
987 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
990 setbuf(debugFP, NULL);
994 if (appData.debugMode) {
995 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
999 /* [HGM,HR] make sure board size is acceptable */
1000 if(appData.NrFiles > BOARD_FILES ||
1001 appData.NrRanks > BOARD_RANKS )
1002 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1005 /* This feature does not work; animation needs a rewrite */
1006 appData.highlightDragging = FALSE;
1010 gameInfo.variant = StringToVariant(appData.variant);
1011 InitPosition(FALSE);
1014 * determine size, based on supplied or remembered -size, or screen size
1016 if (isdigit(appData.boardSize[0])) {
1017 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1018 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1019 &fontPxlSize, &smallLayout, &tinyLayout);
1021 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1022 programName, appData.boardSize);
1026 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // scale height
1028 /* Find some defaults; use the nearest known size */
1029 SizeDefaults *szd, *nearest;
1030 int distance = 99999;
1031 nearest = szd = sizeDefaults;
1032 while (szd->name != NULL) {
1033 if (abs(szd->squareSize - squareSize) < distance) {
1035 distance = abs(szd->squareSize - squareSize);
1036 if (distance == 0) break;
1040 if (i < 2) lineGap = nearest->lineGap;
1041 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1042 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1043 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1044 if (i < 6) smallLayout = nearest->smallLayout;
1045 if (i < 7) tinyLayout = nearest->tinyLayout;
1048 SizeDefaults *szd = sizeDefaults;
1049 if (*appData.boardSize == NULLCHAR) {
1050 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1051 GdkScreen *screen = gdk_screen_get_default();
1052 guint screenwidth = gdk_screen_get_width(screen);
1053 guint screenheight = gdk_screen_get_height(screen);
1054 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1055 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1058 if (szd->name == NULL) szd--;
1059 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1061 while (szd->name != NULL &&
1062 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1063 if (szd->name == NULL) {
1064 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1065 programName, appData.boardSize);
1069 squareSize = szd->squareSize;
1070 lineGap = szd->lineGap;
1071 clockFontPxlSize = szd->clockFontPxlSize;
1072 coordFontPxlSize = szd->coordFontPxlSize;
1073 fontPxlSize = szd->fontPxlSize;
1074 smallLayout = szd->smallLayout;
1075 tinyLayout = szd->tinyLayout;
1076 // [HGM] font: use defaults from settings file if available and not overruled
1079 defaultLineGap = lineGap;
1080 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1082 /* [HR] height treated separately (hacked) */
1083 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1084 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1087 * Determine what fonts to use.
1089 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1092 * Detect if there are not enough colors available and adapt.
1095 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1096 appData.monoMode = True;
1100 forceMono = MakeColors();
1103 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1105 appData.monoMode = True;
1108 ParseIcsTextColors();
1114 layoutName = "tinyLayout";
1115 } else if (smallLayout) {
1116 layoutName = "smallLayout";
1118 layoutName = "normalLayout";
1121 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1122 wpMain.width = -1; // prevent popup sizes window
1123 optList = BoardPopUp(squareSize, lineGap, (void*)
1133 InitDrawingHandle(optList + W_BOARD);
1134 shellWidget = shells[BoardWindow];
1135 currBoard = &optList[W_BOARD];
1136 boardWidget = optList[W_BOARD].handle;
1137 menuBarWidget = optList[W_MENU].handle;
1138 dropMenu = optList[W_DROP].handle;
1139 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1141 formWidget = XtParent(boardWidget);
1142 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1143 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1144 XtGetValues(optList[W_WHITE].handle, args, 2);
1145 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1146 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1147 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1148 XtGetValues(optList[W_PAUSE].handle, args, 2);
1152 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1153 // not need to go into InitDrawingSizes().
1157 // add accelerators to main shell
1158 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1161 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1163 WhiteIcon = LoadIconFile("icon_white");
1164 BlackIcon = LoadIconFile("icon_black");
1165 SetClockIcon(0); // sets white icon
1169 * Create a cursor for the board widget.
1172 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1173 XChangeWindowAttributes(xDisplay, xBoardWindow,
1174 CWCursor, &window_attributes);
1178 * Inhibit shell resizing.
1181 shellArgs[0].value = (XtArgVal) &w;
1182 shellArgs[1].value = (XtArgVal) &h;
1183 XtGetValues(shellWidget, shellArgs, 2);
1184 shellArgs[4].value = shellArgs[2].value = w;
1185 shellArgs[5].value = shellArgs[3].value = h;
1186 // XtSetValues(shellWidget, &shellArgs[2], 4);
1189 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1190 // It wil only become known asynchronously, when we first write a string into it.
1191 // This will then change the clock widget height, which triggers resizing the top-level window
1192 // and a configure event. Only then can we know the total height of the top-level window,
1193 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1194 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1197 gtk_widget_get_allocation(shells[BoardWindow], &a);
1198 w = a.width; h = a.height;
1199 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1200 clockKludge = hc = a.height;
1201 gtk_widget_get_allocation(boardWidget, &a);
1202 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1203 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1209 if(appData.logoSize)
1210 { // locate and read user logo
1211 char buf[MSG_SIZ], name[MSG_SIZ];
1212 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1213 if(!FindLogo(name, ".logo", buf))
1214 FindLogo(appData.logoDir, name + 6, buf);
1215 ASSIGN(userLogo, buf);
1218 if (appData.animate || appData.animateDragging)
1221 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1222 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1224 /* [AS] Restore layout */
1225 if( wpMoveHistory.visible ) {
1229 if( wpEvalGraph.visible )
1234 if( wpEngineOutput.visible ) {
1235 EngineOutputPopUp();
1238 if( wpConsole.visible && appData.icsActive ) {
1243 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1248 if (errorExitStatus == -1) {
1249 if (appData.icsActive) {
1250 /* We now wait until we see "login:" from the ICS before
1251 sending the logon script (problems with timestamp otherwise) */
1252 /*ICSInitScript();*/
1253 if (appData.icsInputBox) ICSInputBoxPopUp();
1257 signal(SIGWINCH, TermSizeSigHandler);
1259 signal(SIGINT, IntSigHandler);
1260 signal(SIGTERM, IntSigHandler);
1261 if (*appData.cmailGameName != NULLCHAR) {
1262 signal(SIGUSR1, CmailSigHandler);
1267 // XtSetKeyboardFocus(shellWidget, formWidget);
1269 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1272 /* check for GTK events and process them */
1275 gtk_main_iteration();
1278 if (appData.debugMode) fclose(debugFP); // [DM] debug
1285 while(gtk_events_pending()) gtk_main_iteration();
1289 TermSizeSigHandler (int sig)
1295 IntSigHandler (int sig)
1301 CmailSigHandler (int sig)
1306 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1308 /* Activate call-back function CmailSigHandlerCallBack() */
1309 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1311 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1315 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1318 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1320 /**** end signal code ****/
1323 #define Abs(n) ((n)<0 ? -(n) : (n))
1326 InsertPxlSize (char *pattern, int targetPxlSize)
1329 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1336 InsertPxlSize (char *pattern, int targetPxlSize)
1338 char *base_fnt_lst, strInt[12], *p, *q;
1339 int alternatives, i, len, strIntLen;
1342 * Replace the "*" (if present) in the pixel-size slot of each
1343 * alternative with the targetPxlSize.
1347 while ((p = strchr(p, ',')) != NULL) {
1351 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1352 strIntLen = strlen(strInt);
1353 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1357 while (alternatives--) {
1358 char *comma = strchr(p, ',');
1359 for (i=0; i<14; i++) {
1360 char *hyphen = strchr(p, '-');
1362 if (comma && hyphen > comma) break;
1363 len = hyphen + 1 - p;
1364 if (i == 7 && *p == '*' && len == 2) {
1366 memcpy(q, strInt, strIntLen);
1376 len = comma + 1 - p;
1383 return base_fnt_lst;
1389 CreateFontSet (char *base_fnt_lst)
1392 char **missing_list;
1396 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1397 &missing_list, &missing_count, &def_string);
1398 if (appData.debugMode) {
1400 XFontStruct **font_struct_list;
1401 char **font_name_list;
1402 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1404 fprintf(debugFP, " got list %s, locale %s\n",
1405 XBaseFontNameListOfFontSet(fntSet),
1406 XLocaleOfFontSet(fntSet));
1407 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1408 for (i = 0; i < count; i++) {
1409 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1412 for (i = 0; i < missing_count; i++) {
1413 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1416 if (fntSet == NULL) {
1417 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1423 #else // not ENABLE_NLS
1425 * Find a font that matches "pattern" that is as close as
1426 * possible to the targetPxlSize. Prefer fonts that are k
1427 * pixels smaller to fonts that are k pixels larger. The
1428 * pattern must be in the X Consortium standard format,
1429 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1430 * The return value should be freed with XtFree when no
1435 FindFont (char *pattern, int targetPxlSize)
1437 char **fonts, *p, *best, *scalable, *scalableTail;
1438 int i, j, nfonts, minerr, err, pxlSize;
1440 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1442 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1443 programName, pattern);
1450 for (i=0; i<nfonts; i++) {
1453 if (*p != '-') continue;
1455 if (*p == NULLCHAR) break;
1456 if (*p++ == '-') j++;
1458 if (j < 7) continue;
1461 scalable = fonts[i];
1464 err = pxlSize - targetPxlSize;
1465 if (Abs(err) < Abs(minerr) ||
1466 (minerr > 0 && err < 0 && -err == minerr)) {
1472 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1473 /* If the error is too big and there is a scalable font,
1474 use the scalable font. */
1475 int headlen = scalableTail - scalable;
1476 p = (char *) XtMalloc(strlen(scalable) + 10);
1477 while (isdigit(*scalableTail)) scalableTail++;
1478 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1480 p = (char *) XtMalloc(strlen(best) + 2);
1481 safeStrCpy(p, best, strlen(best)+1 );
1483 if (appData.debugMode) {
1484 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1485 pattern, targetPxlSize, p);
1487 XFreeFontNames(fonts);
1494 MarkMenuItem (char *menuRef, int state)
1496 MenuItem *item = MenuNameToItem(menuRef);
1498 if(item && item->handle) {
1499 ((GtkCheckMenuItem *) (item->handle))->active = state;
1505 EnableNamedMenuItem (char *menuRef, int state)
1507 MenuItem *item = MenuNameToItem(menuRef);
1509 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1514 EnableButtonBar (int state)
1517 XtSetSensitive(optList[W_BUTTON].handle, state);
1523 SetMenuEnables (Enables *enab)
1525 while (enab->name != NULL) {
1526 EnableNamedMenuItem(enab->name, enab->value);
1531 gboolean KeyPressProc(window, eventkey, data)
1533 GdkEventKey *eventkey;
1537 MoveTypeInProc(eventkey); // pop up for typed in moves
1540 /* check for other key values */
1541 switch(eventkey->keyval) {
1553 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1554 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1556 if(*nprms == 0) return;
1557 item = MenuNameToItem(prms[0]);
1558 if(item) ((MenuProc *) item->proc) ();
1572 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1573 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1574 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1575 dmEnables[i].piece);
1576 XtSetSensitive(entry, p != NULL || !appData.testLegality
1577 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1578 && !appData.icsActive));
1580 while (p && *p++ == dmEnables[i].piece) count++;
1581 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1583 XtSetArg(args[j], XtNlabel, label); j++;
1584 XtSetValues(entry, args, j);
1590 do_flash_delay (unsigned long msec)
1596 FlashDelay (int flash_delay)
1598 if(flash_delay) do_flash_delay(flash_delay);
1602 Fraction (int x, int start, int stop)
1604 double f = ((double) x - start)/(stop - start);
1605 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1609 static WindowPlacement wpNew;
1612 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1614 int touch=0, fudge = 4, f = 3;
1615 GetActualPlacement(sh, wp);
1616 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1617 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1618 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1619 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1620 //printf("CoDrag: touch = %d x=%d w=%d x2=%d w2=%d fx=%d\n", touch, wpMain.x, wpMain.width, wp->x, wp->width, frameX);
1621 if(!touch ) return; // only windows that touch co-move
1622 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1623 int heightInc = wpNew.height - wpMain.height;
1624 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1625 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1626 wp->y += fracTop * heightInc;
1627 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1629 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1631 wp->height += heightInc;
1632 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1633 int widthInc = wpNew.width - wpMain.width;
1634 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1635 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1636 wp->y += fracLeft * widthInc;
1637 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1639 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1641 wp->width += widthInc;
1643 wp->x += wpNew.x - wpMain.x;
1644 wp->y += wpNew.y - wpMain.y;
1645 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1646 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1648 XtSetArg(args[j], XtNx, wp->x); j++;
1649 XtSetArg(args[j], XtNy, wp->y); j++;
1650 XtSetValues(sh, args, j);
1652 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1653 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1654 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1658 ReSize (WindowPlacement *wp)
1661 int sqx, sqy, w, h, lg = lineGap;
1662 static int first = 1;
1663 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
1664 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1665 w = a.width; h = a.height;
1666 gtk_widget_get_allocation(shellWidget, &a);
1667 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1668 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1669 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1670 w += a.width; h += a.height;
1672 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1673 w = a.width; h = a.height;
1675 sqx = (w - lg) / BOARD_WIDTH - lg;
1676 sqy = (h - lg) / BOARD_HEIGHT - lg;
1677 if(sqy < sqx) sqx = sqy;
1678 if(sqx < 20) return;
1679 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1681 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1682 sqx = (w - lg) / BOARD_WIDTH - lg;
1683 sqy = (h - lg) / BOARD_HEIGHT - lg;
1684 if(sqy < sqx) sqx = sqy;
1685 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1686 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1688 if(sqx != squareSize && !first) {
1689 squareSize = sqx; // adopt new square size
1690 CreatePNGPieces(); // make newly scaled pieces
1691 InitDrawingSizes(0, 0); // creates grid etc.
1692 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1693 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1694 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1695 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1696 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1697 first = appData.fixedSize;
1700 static guint delayedDragTag = 0;
1709 GetActualPlacement(shellWidget, &wpNew);
1710 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1711 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1712 busy = 0; return; // false alarm
1715 if(appData.useStickyWindows) {
1716 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1717 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1718 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1719 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1720 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1723 DrawPosition(True, NULL);
1724 if(delayedDragTag) g_source_remove(delayedDragTag);
1725 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1732 //printf("old timr = %d\n", delayedDragTag);
1733 if(delayedDragTag) g_source_remove(delayedDragTag);
1734 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1735 //printf("new timr = %d\n", delayedDragTag);
1739 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1741 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1743 wpNew.x = event->configure.x;
1744 wpNew.y = event->configure.y;
1745 wpNew.width = event->configure.width;
1746 wpNew.height = event->configure.height;
1747 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1753 /* Disable all user input other than deleting the window */
1754 static int frozen = 0;
1760 /* Grab by a widget that doesn't accept input */
1761 gtk_grab_add(optList[W_MESSG].handle);
1765 /* Undo a FreezeUI */
1769 if (!frozen) return;
1770 gtk_grab_remove(optList[W_MESSG].handle);
1777 static int oldPausing = FALSE;
1778 static GameMode oldmode = (GameMode) -1;
1780 if (!boardWidget) return;
1782 if (pausing != oldPausing) {
1783 oldPausing = pausing;
1784 MarkMenuItem("Mode.Pause", pausing);
1786 if (appData.showButtonBar) {
1787 /* Always toggle, don't set. Previous code messes up when
1788 invoked while the button is pressed, as releasing it
1789 toggles the state again. */
1791 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1792 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1796 wname = ModeToWidgetName(oldmode);
1797 if (wname != NULL) {
1798 MarkMenuItem(wname, False);
1800 wname = ModeToWidgetName(gameMode);
1801 if (wname != NULL) {
1802 MarkMenuItem(wname, True);
1805 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1807 /* Maybe all the enables should be handled here, not just this one */
1808 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1810 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1815 * Button/menu procedures
1818 void CopyFileToClipboard(gchar *filename)
1820 gchar *selection_tmp;
1824 FILE* f = fopen(filename, "r");
1827 if (f == NULL) return;
1831 selection_tmp = g_try_malloc(len + 1);
1832 if (selection_tmp == NULL) {
1833 printf("Malloc failed in CopyFileToClipboard\n");
1836 count = fread(selection_tmp, 1, len, f);
1839 g_free(selection_tmp);
1842 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1844 // copy selection_tmp to clipboard
1845 GdkDisplay *gdisp = gdk_display_get_default();
1847 g_free(selection_tmp);
1850 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1851 gtk_clipboard_set_text(cb, selection_tmp, -1);
1852 g_free(selection_tmp);
1856 CopySomething (char *src)
1858 GdkDisplay *gdisp = gdk_display_get_default();
1860 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1861 if (gdisp == NULL) return;
1862 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1863 gtk_clipboard_set_text(cb, src, -1);
1867 PastePositionProc ()
1869 GdkDisplay *gdisp = gdk_display_get_default();
1873 if (gdisp == NULL) return;
1874 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1875 fenstr = gtk_clipboard_wait_for_text(cb);
1876 if (fenstr==NULL) return; // nothing had been selected to copy
1877 EditPositionPasteFEN(fenstr);
1889 // get game from clipboard
1890 GdkDisplay *gdisp = gdk_display_get_default();
1891 if (gdisp == NULL) return;
1892 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1893 text = gtk_clipboard_wait_for_text(cb);
1894 if (text == NULL) return; // nothing to paste
1897 // write to temp file
1898 if (text == NULL || len == 0) {
1899 return; //nothing to paste
1901 f = fopen(gamePasteFilename, "w");
1903 DisplayError(_("Can't open temp file"), errno);
1906 fwrite(text, 1, len, f);
1910 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1917 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1923 void MoveTypeInProc(eventkey)
1924 GdkEventKey *eventkey;
1928 // ingnore if ctrl, alt, or meta is pressed
1929 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1933 buf[0]=eventkey->keyval;
1935 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1936 ConsoleAutoPopUp (buf);
1941 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1943 if (!TempBackwardActive) {
1944 TempBackwardActive = True;
1950 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1952 /* Check to see if triggered by a key release event for a repeating key.
1953 * If so the next queued event will be a key press of the same key at the same time */
1954 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1956 XPeekEvent(xDisplay, &next);
1957 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1958 next.xkey.keycode == event->xkey.keycode)
1962 TempBackwardActive = False;
1968 { // called from menu
1971 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
1974 system("xterm -e man xboard &");
1983 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);
1985 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
1993 SetWindowTitle (char *text, char *title, char *icon)
1998 if (appData.titleInWindow) {
2000 XtSetArg(args[i], XtNlabel, text); i++;
2001 XtSetValues(titleWidget, args, i);
2004 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2005 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2006 XtSetValues(shellWidget, args, i);
2007 XSync(xDisplay, False);
2009 if (appData.titleInWindow) {
2010 SetWidgetLabel(titleWidget, text);
2012 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2017 DisplayIcsInteractionTitle (String message)
2020 if (oldICSInteractionTitle == NULL) {
2021 /* Magic to find the old window title, adapted from vim */
2022 char *wina = getenv("WINDOWID");
2024 Window win = (Window) atoi(wina);
2025 Window root, parent, *children;
2026 unsigned int nchildren;
2027 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2029 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2030 if (!XQueryTree(xDisplay, win, &root, &parent,
2031 &children, &nchildren)) break;
2032 if (children) XFree((void *)children);
2033 if (parent == root || parent == 0) break;
2036 XSetErrorHandler(oldHandler);
2038 if (oldICSInteractionTitle == NULL) {
2039 oldICSInteractionTitle = "xterm";
2042 printf("\033]0;%s\007", message);
2049 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2051 GtkWidget *w = (GtkWidget *) opt->handle;
2058 strcpy(bgcolor, "black");
2059 strcpy(fgcolor, "white");
2061 strcpy(bgcolor, "white");
2062 strcpy(fgcolor, "black");
2065 appData.lowTimeWarning &&
2066 (timer / 1000) < appData.icsAlarmTime) {
2067 strcpy(fgcolor, appData.lowTimeWarningColor);
2070 gdk_color_parse( bgcolor, &col );
2071 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2073 if (appData.clockMode) {
2074 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2075 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2076 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2077 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2079 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2080 bgcolor, fgcolor, color);
2081 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2082 // bgcolor, fgcolor, color);
2084 gtk_label_set_markup(GTK_LABEL(w), markup);
2088 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2091 SetClockIcon (int color)
2093 GdkPixbuf *pm = *clockIcons[color];
2094 if (mainwindowIcon != pm) {
2095 mainwindowIcon = pm;
2097 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2099 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2104 #define INPUT_SOURCE_BUF_SIZE 8192
2113 char buf[INPUT_SOURCE_BUF_SIZE];
2118 DoInputCallback(io, cond, data)
2123 /* read input from one of the input source (for example a chess program, ICS, etc).
2124 * and call a function that will handle the input
2131 /* All information (callback function, file descriptor, etc) is
2132 * saved in an InputSource structure
2134 InputSource *is = (InputSource *) data;
2136 if (is->lineByLine) {
2137 count = read(is->fd, is->unused,
2138 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2140 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2141 RemoveInputSource(is); // cease reading stdin
2142 stdoutClosed = TRUE; // suppress future output
2145 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2148 is->unused += count;
2150 /* break input into lines and call the callback function on each
2153 while (p < is->unused) {
2154 q = memchr(p, '\n', is->unused - p);
2155 if (q == NULL) break;
2157 (is->func)(is, is->closure, p, q - p, 0);
2160 /* remember not yet used part of the buffer */
2162 while (p < is->unused) {
2167 /* read maximum length of input buffer and send the whole buffer
2168 * to the callback function
2170 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2175 (is->func)(is, is->closure, is->buf, count, error);
2177 return True; // Must return true or the watch will be removed
2180 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2187 GIOChannel *channel;
2188 ChildProc *cp = (ChildProc *) pr;
2190 is = (InputSource *) calloc(1, sizeof(InputSource));
2191 is->lineByLine = lineByLine;
2195 is->fd = fileno(stdin);
2197 is->kind = cp->kind;
2198 is->fd = cp->fdFrom;
2201 is->unused = is->buf;
2205 /* GTK-TODO: will this work on windows?*/
2207 channel = g_io_channel_unix_new(is->fd);
2208 g_io_channel_set_close_on_unref (channel, TRUE);
2209 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2211 is->closure = closure;
2212 return (InputSourceRef) is;
2217 RemoveInputSource(isr)
2220 InputSource *is = (InputSource *) isr;
2222 if (is->sid == 0) return;
2223 g_source_remove(is->sid);
2230 static Boolean frameWaiting;
2233 FrameAlarm (int sig)
2235 frameWaiting = False;
2236 /* In case System-V style signals. Needed?? */
2237 signal(SIGALRM, FrameAlarm);
2241 FrameDelay (int time)
2243 struct itimerval delay;
2246 frameWaiting = True;
2247 signal(SIGALRM, FrameAlarm);
2248 delay.it_interval.tv_sec =
2249 delay.it_value.tv_sec = time / 1000;
2250 delay.it_interval.tv_usec =
2251 delay.it_value.tv_usec = (time % 1000) * 1000;
2252 setitimer(ITIMER_REAL, &delay, NULL);
2253 while (frameWaiting) pause();
2254 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2255 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2256 setitimer(ITIMER_REAL, &delay, NULL);
2263 FrameDelay (int time)
2266 XSync(xDisplay, False);
2268 // gtk_main_iteration_do(False);
2271 usleep(time * 1000);
2277 FindLogo (char *place, char *name, char *buf)
2278 { // check if file exists in given place
2280 if(!place) return 0;
2281 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2282 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2290 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2292 char buf[MSG_SIZ], *logoName = buf;
2293 if(appData.logo[n][0]) {
2294 logoName = appData.logo[n];
2295 } else if(appData.autoLogo) {
2296 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2297 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2298 } else { // engine; cascade
2299 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2300 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2301 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2302 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2306 { ASSIGN(cps->programLogo, logoName); }
2310 UpdateLogos (int displ)
2312 if(optList[W_WHITE-1].handle == NULL) return;
2313 LoadLogo(&first, 0, 0);
2314 LoadLogo(&second, 1, appData.icsActive);
2315 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2319 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2330 GtkFileFilter *gtkfilter;
2331 GtkFileFilter *gtkfilter_all;
2333 char fileext[10] = "";
2334 char *result = NULL;
2336 char curDir[MSG_SIZ];
2338 StartDir(filter, NULL); // change to start directory for this file type
2340 if(def && *def && def[strlen(def)-1] == '/') {
2341 getcwd(curDir, MSG_SIZ);
2346 /* make a copy of the filter string, so that strtok can work with it*/
2347 cp = strdup(filter);
2349 /* add filters for file extensions */
2350 gtkfilter = gtk_file_filter_new();
2351 gtkfilter_all = gtk_file_filter_new();
2353 /* one filter to show everything */
2354 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2355 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2357 /* add filter if present */
2358 result = strtok(cp, space);
2359 while( result != NULL ) {
2360 snprintf(fileext,10,"*%s",result);
2361 result = strtok( NULL, space );
2362 gtk_file_filter_add_pattern(gtkfilter, fileext);
2365 /* second filter to only show what's useful */
2366 gtk_file_filter_set_name (gtkfilter,filter);
2368 if (openMode[0] == 'r')
2370 dialog = gtk_file_chooser_dialog_new (label,
2372 GTK_FILE_CHOOSER_ACTION_OPEN,
2373 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2374 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2379 dialog = gtk_file_chooser_dialog_new (label,
2381 GTK_FILE_CHOOSER_ACTION_SAVE,
2382 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2383 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2385 /* add filename suggestions */
2386 if (strlen(def) > 0 )
2387 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2389 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2393 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2394 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2395 /* activate filter */
2396 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2398 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2403 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2406 f = fopen(filename, openMode);
2409 DisplayError(_("Failed to open file"), errno);
2413 /* TODO add indec */
2415 ASSIGN(*name, filename);
2416 ScheduleDelayedEvent(DelayedLoad, 50);
2418 StartDir(filter, filename);
2421 else StartDir(filter, "");
2423 gtk_widget_destroy (dialog);
2426 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);