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>
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
181 # undef SETTINGS_FILE
182 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
183 # define DATADIR dataDir
184 # define MANDIR manDir
185 # define LOCALEDIR localeDir
186 # define SETTINGS_FILE masterSettings
187 # define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
188 char dataDir[MSG_SIZ]; // for expanding ~~
189 char manDir[MSG_SIZ];
190 char localeDir[MSG_SIZ];
191 char masterSettings[MSG_SIZ];
195 # define SYNC_MENUBAR
202 #define usleep(t) _sleep2(((t)+500)/1000)
206 # define _(s) gettext (s)
207 # define N_(s) gettext_noop (s)
213 int main P((int argc, char **argv));
214 RETSIGTYPE CmailSigHandler P((int sig));
215 RETSIGTYPE IntSigHandler P((int sig));
216 RETSIGTYPE TermSizeSigHandler P((int sig));
217 char *InsertPxlSize P((char *pattern, int targetPxlSize));
220 XFontSet CreateFontSet P((char *base_fnt_lst));
222 char *FindFont P((char *pattern, int targetPxlSize));
225 void DelayedDrag P((void));
226 void ICSInputBoxPopUp P((void));
227 void MoveTypeInProc P((GdkEventKey *eventkey));
228 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
229 Boolean TempBackwardActive = False;
230 void DisplayMove P((int moveNumber));
231 void update_ics_width P(());
232 int CopyMemoProc P(());
233 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
234 static int FindLogo P((char *place, char *name, char *buf));
238 XFontSet fontSet, clockFontSet;
241 XFontStruct *clockFontStruct;
243 Font coordFontID, countFontID;
244 XFontStruct *coordFontStruct, *countFontStruct;
246 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
247 GtkWidget *mainwindow;
249 Option *optList; // contains all widgets of main window
252 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
255 static GdkPixbuf *mainwindowIcon=NULL;
256 static GdkPixbuf *WhiteIcon=NULL;
257 static GdkPixbuf *BlackIcon=NULL;
259 /* key board accelerators */
260 GtkAccelGroup *GtkAccelerators;
262 typedef unsigned int BoardSize;
264 Boolean chessProgram;
266 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
267 int smallLayout = 0, tinyLayout = 0,
268 marginW, marginH, // [HGM] for run-time resizing
269 fromX = -1, fromY = -1, toX, toY, commentUp = False,
270 errorExitStatus = -1, defaultLineGap;
272 Dimension textHeight;
274 char *chessDir, *programName, *programVersion;
275 Boolean alwaysOnTop = False;
276 char *icsTextMenuString;
278 char *firstChessProgramNames;
279 char *secondChessProgramNames;
281 WindowPlacement wpMain;
282 WindowPlacement wpConsole;
283 WindowPlacement wpComment;
284 WindowPlacement wpMoveHistory;
285 WindowPlacement wpEvalGraph;
286 WindowPlacement wpEngineOutput;
287 WindowPlacement wpGameList;
288 WindowPlacement wpTags;
289 WindowPlacement wpDualBoard;
291 /* This magic number is the number of intermediate frames used
292 in each half of the animation. For short moves it's reduced
293 by 1. The total number of frames will be factor * 2 + 1. */
296 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
303 DropMenuEnables dmEnables[] = {
312 XtResource clientResources[] = {
313 { "flashCount", "flashCount", XtRInt, sizeof(int),
314 XtOffset(AppDataPtr, flashCount), XtRImmediate,
315 (XtPointer) FLASH_COUNT },
319 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
320 char globalTranslations[] =
321 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
322 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
323 :<KeyDown>Return: TempBackwardProc() \n \
324 :<KeyUp>Return: TempForwardProc() \n";
326 char ICSInputTranslations[] =
327 "<Key>Up: UpKeyProc() \n "
328 "<Key>Down: DownKeyProc() \n "
329 "<Key>Return: EnterKeyProc() \n";
331 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
332 // as the widget is destroyed before the up-click can call extend-end
333 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
336 String xboardResources[] = {
337 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
345 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
348 //---------------------------------------------------------------------------------------------------------
349 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
352 #define CW_USEDEFAULT (1<<31)
353 #define ICS_TEXT_MENU_SIZE 90
354 #define DEBUG_FILE "xboard.debug"
355 #define SetCurrentDirectory chdir
356 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
360 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
363 // front-end part of option handling
365 // [HGM] This platform-dependent table provides the location for storing the color info
366 extern char *crWhite, * crBlack;
370 &appData.whitePieceColor,
371 &appData.blackPieceColor,
372 &appData.lightSquareColor,
373 &appData.darkSquareColor,
374 &appData.highlightSquareColor,
375 &appData.premoveHighlightColor,
376 &appData.lowTimeWarningColor,
387 // [HGM] font: keep a font for each square size, even non-stndard ones
390 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
391 char *fontTable[NUM_FONTS][MAX_SIZE];
394 ParseFont (char *name, int number)
395 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
397 if(sscanf(name, "size%d:", &size)) {
398 // [HGM] font: font is meant for specific boardSize (likely from settings file);
399 // defer processing it until we know if it matches our board size
400 if(!strstr(name, "-*-") && // ignore X-fonts
401 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
402 fontTable[number][size] = strdup(strchr(name, ':')+1);
403 fontValid[number][size] = True;
408 case 0: // CLOCK_FONT
409 appData.clockFont = strdup(name);
411 case 1: // MESSAGE_FONT
412 appData.font = strdup(name);
414 case 2: // COORD_FONT
415 appData.coordFont = strdup(name);
418 appData.icsFont = strdup(name);
421 appData.tagsFont = strdup(name);
424 appData.commentFont = strdup(name);
426 case MOVEHISTORY_FONT:
427 appData.historyFont = strdup(name);
430 appData.gameListFont = strdup(name);
435 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
440 { // only 2 fonts currently
441 appData.clockFont = strdup(CLOCK_FONT_NAME);
442 appData.coordFont = strdup(COORD_FONT_NAME);
443 appData.font = strdup(DEFAULT_FONT_NAME);
444 appData.icsFont = strdup(CONSOLE_FONT_NAME);
445 appData.tagsFont = strdup(TAGS_FONT_NAME);
446 appData.commentFont = strdup(COMMENT_FONT_NAME);
447 appData.historyFont = strdup(HISTORY_FONT_NAME);
448 appData.gameListFont = strdup(GAMELIST_FONT_NAME);
453 { // no-op, until we identify the code for this already in XBoard and move it here
457 ParseColor (int n, char *name)
458 { // in XBoard, just copy the color-name string
459 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
465 return *(char**)colorVariable[n];
469 ParseTextAttribs (ColorClass cc, char *s)
471 (&appData.colorShout)[cc] = strdup(s);
475 ParseBoardSize (void *addr, char *name)
477 appData.boardSize = strdup(name);
482 { // In XBoard the sound-playing program takes care of obtaining the actual sound
486 SetCommPortDefaults ()
487 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
490 // [HGM] args: these three cases taken out to stay in front-end
492 SaveFontArg (FILE *f, ArgDescriptor *ad)
495 int i, n = (int)(intptr_t)ad->argLoc;
497 case 0: // CLOCK_FONT
498 name = appData.clockFont;
500 case 1: // MESSAGE_FONT
503 case 2: // COORD_FONT
504 name = appData.coordFont;
507 name = appData.icsFont;
510 name = appData.tagsFont;
513 name = appData.commentFont;
515 case MOVEHISTORY_FONT:
516 name = appData.historyFont;
519 name = appData.gameListFont;
524 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
525 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
526 fontTable[n][squareSize] = strdup(name);
527 fontValid[n][squareSize] = True;
530 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
531 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
536 { // nothing to do, as the sounds are at all times represented by their text-string names already
540 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
541 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
542 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
546 SaveColor (FILE *f, ArgDescriptor *ad)
547 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
548 if(colorVariable[(int)(intptr_t)ad->argLoc])
549 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
553 SaveBoardSize (FILE *f, char *name, void *addr)
554 { // wrapper to shield back-end from BoardSize & sizeInfo
555 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
559 ParseCommPortSettings (char *s)
560 { // no such option in XBoard (yet)
566 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
570 gtk_widget_get_allocation(shell, &a);
571 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
575 wp->height = a.height;
576 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
577 frameX = 3; frameY = 3; // remember to decide if windows touch
581 GetPlacement (DialogClass dlg, WindowPlacement *wp)
582 { // wrapper to shield back-end from widget type
583 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
588 { // wrapper to shield use of window handles from back-end (make addressible by number?)
589 // In XBoard this will have to wait until awareness of window parameters is implemented
590 GetActualPlacement(shellWidget, &wpMain);
591 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
592 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
593 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
594 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
595 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
596 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
597 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
601 PrintCommPortSettings (FILE *f, char *name)
602 { // This option does not exist in XBoard
606 EnsureOnScreen (int *x, int *y, int minX, int minY)
613 { // [HGM] args: allows testing if main window is realized from back-end
614 return DialogExists(BoardWindow);
618 PopUpStartupDialog ()
619 { // start menu not implemented in XBoard
623 ConvertToLine (int argc, char **argv)
625 static char line[128*1024], buf[1024];
629 for(i=1; i<argc; i++)
631 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
632 && argv[i][0] != '{' )
633 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
635 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
636 strncat(line, buf, 128*1024 - strlen(line) - 1 );
639 line[strlen(line)-1] = NULLCHAR;
643 //--------------------------------------------------------------------------------------------
648 ResizeBoardWindow (int w, int h, int inhibit)
652 // if(clockKludge) return; // ignore as long as clock does not have final height
653 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
655 gtk_widget_get_allocation(shellWidget, &a);
656 marginW = a.width - bw;
657 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
658 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
659 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
660 // h += marginH + a.height + 1;
661 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
663 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board again
668 { // dummy, as the GTK code does not make colors in advance
673 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
674 { // determine what fonts to use, and create them
676 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
677 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
678 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
679 appData.font = fontTable[MESSAGE_FONT][squareSize];
680 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
681 appData.coordFont = fontTable[COORD_FONT][squareSize];
682 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
683 appData.icsFont = fontTable[CONSOLE_FONT][squareSize];
684 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
685 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize];
686 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
687 appData.commentFont = fontTable[COMMENT_FONT][squareSize];
688 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
689 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize];
690 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
691 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize];
693 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
694 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
695 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
696 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
697 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
698 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
699 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
700 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
706 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
707 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
708 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
709 appData.font = fontTable[MESSAGE_FONT][squareSize];
710 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
711 appData.coordFont = fontTable[COORD_FONT][squareSize];
714 appData.font = InsertPxlSize(appData.font, fontPxlSize);
715 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
716 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
717 fontSet = CreateFontSet(appData.font);
718 clockFontSet = CreateFontSet(appData.clockFont);
720 /* For the coordFont, use the 0th font of the fontset. */
721 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
722 XFontStruct **font_struct_list;
723 XFontSetExtents *fontSize;
724 char **font_name_list;
725 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
726 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
727 coordFontStruct = XQueryFont(xDisplay, coordFontID);
728 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
729 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
732 appData.font = FindFont(appData.font, fontPxlSize);
733 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
734 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
735 clockFontID = XLoadFont(xDisplay, appData.clockFont);
736 clockFontStruct = XQueryFont(xDisplay, clockFontID);
737 coordFontID = XLoadFont(xDisplay, appData.coordFont);
738 coordFontStruct = XQueryFont(xDisplay, coordFontID);
739 // textHeight in !NLS mode!
741 countFontID = coordFontID; // [HGM] holdings
742 countFontStruct = coordFontStruct;
744 xdb = XtDatabase(xDisplay);
746 XrmPutLineResource(&xdb, "*international: True");
747 vTo.size = sizeof(XFontSet);
748 vTo.addr = (XtPointer) &fontSet;
749 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
751 XrmPutStringResource(&xdb, "*font", appData.font);
762 case ArgInt: p = " N"; break;
763 case ArgString: p = " STR"; break;
764 case ArgBoolean: p = " TF"; break;
765 case ArgSettingsFilename:
766 case ArgBackupSettingsFile:
767 case ArgFilename: p = " FILE"; break;
768 case ArgX: p = " Nx"; break;
769 case ArgY: p = " Ny"; break;
770 case ArgAttribs: p = " TEXTCOL"; break;
771 case ArgColor: p = " COL"; break;
772 case ArgFont: p = " FONT"; break;
773 case ArgBoardSize: p = " SIZE"; break;
774 case ArgFloat: p = " FLOAT"; break;
779 case ArgCommSettings:
791 ArgDescriptor *q, *p = argDescriptors+5;
792 printf("\nXBoard accepts the following options:\n"
793 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
794 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
795 " SIZE = board-size spec(s)\n"
796 " Within parentheses are short forms, or options to set to true or false.\n"
797 " Persistent options (saved in the settings file) are marked with *)\n\n");
799 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
800 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
801 if(p->save) strcat(buf+len, "*");
802 for(q=p+1; q->argLoc == p->argLoc; q++) {
803 if(q->argName[0] == '-') continue;
804 strcat(buf+len, q == p+1 ? " (" : " ");
805 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
807 if(q != p+1) strcat(buf+len, ")");
809 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
812 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
816 SlaveResize (Option *opt)
818 static int slaveW, slaveH, w, h;
821 gtk_widget_get_allocation(shells[DummyDlg], &a);
822 w = a.width; h = a.height;
823 gtk_widget_get_allocation(opt->handle, &a);
824 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
825 slaveH = h - a.height + 13;
827 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
831 LoadIconFile (gchar *svgFilename)
835 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
836 return gdk_pixbuf_new_from_file(buf, NULL);
840 static char clickedFile[MSG_SIZ];
844 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
845 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
848 if(1000*now.sec + now.ms - 1000*started.sec - started.ms < 1000) { // received during first second
849 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
850 } else { // we are running something presumably useful
852 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
853 system(buf); // start new instance on this file
858 GtkosxApplication *theApp;
862 main (int argc, char **argv)
864 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
865 int boardWidth, w, h; //, boardHeight;
867 int forceMono = False;
869 srandom(time(0)); // [HGM] book: make random truly random
871 setbuf(stdout, NULL);
872 setbuf(stderr, NULL);
875 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
876 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
880 if(argc > 1 && !strcmp(argv[1], "--help" )) {
886 gtk_init (&argc, &argv);
888 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
889 char *path = gtkosx_application_get_bundle_path();
891 char *res_path = gtkosx_application_get_resource_path();
892 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
894 GetTimeMark(&started); // remember start time
895 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
896 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
897 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
898 snprintf(manDir, MSG_SIZ, "%s/Contents/Resources/share/man", path);
899 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
900 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
901 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
902 // we must call application ready before we can get the signal,
903 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
904 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
905 gtkosx_application_ready(theApp);
906 if(argc == 1) { // called without args: OSX open-file signal might follow
907 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
908 usleep(10000); // wait 10 msec (and hope this is long enough).
909 while(gtk_events_pending())
910 gtk_main_iteration(); // process all events that came in upto now
911 if(clickedFile[0]) { // we were sent an open-file signal with filename!
912 fakeArgv[0] = argv[0];
913 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
919 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
920 typedef struct {char *name, *value; } Config;
921 static Config configList[] = {
922 { "Datadir", DATADIR },
923 { "Sysconfdir", SYSCONFDIR },
928 for(i=0; configList[i].name; i++) {
929 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
930 if(argc > 2) printf("%s", configList[i].value);
931 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
936 /* set up keyboard accelerators group */
937 GtkAccelerators = gtk_accel_group_new();
939 programName = strrchr(argv[0], '/');
940 if (programName == NULL)
941 programName = argv[0];
946 // if (appData.debugMode) {
947 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
950 bindtextdomain(PACKAGE, LOCALEDIR);
951 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
955 appData.boardSize = "";
956 InitAppData(ConvertToLine(argc, argv));
958 if (p == NULL) p = "/tmp";
959 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
960 gameCopyFilename = (char*) malloc(i);
961 gamePasteFilename = (char*) malloc(i);
962 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
963 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
965 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
966 static char buf[MSG_SIZ];
967 snprintf(buf, MSG_SIZ, appData.sysOpen, DATADIR);
968 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
969 EscapeExpand(buf, appData.firstInitString);
970 appData.firstInitString = strdup(buf);
971 EscapeExpand(buf, appData.secondInitString);
972 appData.secondInitString = strdup(buf);
973 EscapeExpand(buf, appData.firstComputerString);
974 appData.firstComputerString = strdup(buf);
975 EscapeExpand(buf, appData.secondComputerString);
976 appData.secondComputerString = strdup(buf);
979 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
982 if (chdir(chessDir) != 0) {
983 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
989 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
990 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
991 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
992 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
995 setbuf(debugFP, NULL);
999 if (appData.debugMode) {
1000 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1004 /* [HGM,HR] make sure board size is acceptable */
1005 if(appData.NrFiles > BOARD_FILES ||
1006 appData.NrRanks > BOARD_RANKS )
1007 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1010 /* This feature does not work; animation needs a rewrite */
1011 appData.highlightDragging = FALSE;
1015 gameInfo.variant = StringToVariant(appData.variant);
1016 InitPosition(FALSE);
1019 * determine size, based on supplied or remembered -size, or screen size
1021 if (isdigit(appData.boardSize[0])) {
1022 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1023 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1024 &fontPxlSize, &smallLayout, &tinyLayout);
1026 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1027 programName, appData.boardSize);
1031 /* Find some defaults; use the nearest known size */
1032 SizeDefaults *szd, *nearest;
1033 int distance = 99999;
1034 nearest = szd = sizeDefaults;
1035 while (szd->name != NULL) {
1036 if (abs(szd->squareSize - squareSize) < distance) {
1038 distance = abs(szd->squareSize - squareSize);
1039 if (distance == 0) break;
1043 if (i < 2) lineGap = nearest->lineGap;
1044 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1045 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1046 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1047 if (i < 6) smallLayout = nearest->smallLayout;
1048 if (i < 7) tinyLayout = nearest->tinyLayout;
1051 SizeDefaults *szd = sizeDefaults;
1052 if (*appData.boardSize == NULLCHAR) {
1053 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1054 GdkScreen *screen = gdk_screen_get_default();
1055 guint screenwidth = gdk_screen_get_width(screen);
1056 guint screenheight = gdk_screen_get_height(screen);
1057 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1058 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1061 if (szd->name == NULL) szd--;
1062 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1064 while (szd->name != NULL &&
1065 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1066 if (szd->name == NULL) {
1067 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1068 programName, appData.boardSize);
1072 squareSize = szd->squareSize;
1073 lineGap = szd->lineGap;
1074 clockFontPxlSize = szd->clockFontPxlSize;
1075 coordFontPxlSize = szd->coordFontPxlSize;
1076 fontPxlSize = szd->fontPxlSize;
1077 smallLayout = szd->smallLayout;
1078 tinyLayout = szd->tinyLayout;
1079 // [HGM] font: use defaults from settings file if available and not overruled
1081 if(BOARD_WIDTH != 8) {
1082 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
1083 lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
1086 defaultLineGap = lineGap;
1087 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1089 /* [HR] height treated separately (hacked) */
1090 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1091 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1094 * Determine what fonts to use.
1096 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1099 * Detect if there are not enough colors available and adapt.
1102 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1103 appData.monoMode = True;
1107 forceMono = MakeColors();
1110 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1112 appData.monoMode = True;
1115 ParseIcsTextColors();
1121 layoutName = "tinyLayout";
1122 } else if (smallLayout) {
1123 layoutName = "smallLayout";
1125 layoutName = "normalLayout";
1128 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1129 wpMain.width = -1; // prevent popup sizes window
1130 optList = BoardPopUp(squareSize, lineGap, (void*)
1140 InitDrawingHandle(optList + W_BOARD);
1141 shellWidget = shells[BoardWindow];
1142 currBoard = &optList[W_BOARD];
1143 boardWidget = optList[W_BOARD].handle;
1144 menuBarWidget = optList[W_MENU].handle;
1145 dropMenu = optList[W_DROP].handle;
1146 titleWidget = optList[optList[W_TITLE].type != Skip ? W_TITLE : W_SMALL].handle;
1148 formWidget = XtParent(boardWidget);
1149 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1150 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1151 XtGetValues(optList[W_WHITE].handle, args, 2);
1152 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1153 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1154 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1155 XtGetValues(optList[W_PAUSE].handle, args, 2);
1159 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1160 // not need to go into InitDrawingSizes().
1164 // add accelerators to main shell
1165 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1168 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1170 WhiteIcon = LoadIconFile("icon_white");
1171 BlackIcon = LoadIconFile("icon_black");
1172 SetClockIcon(0); // sets white icon
1176 * Create a cursor for the board widget.
1179 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1180 XChangeWindowAttributes(xDisplay, xBoardWindow,
1181 CWCursor, &window_attributes);
1185 * Inhibit shell resizing.
1188 shellArgs[0].value = (XtArgVal) &w;
1189 shellArgs[1].value = (XtArgVal) &h;
1190 XtGetValues(shellWidget, shellArgs, 2);
1191 shellArgs[4].value = shellArgs[2].value = w;
1192 shellArgs[5].value = shellArgs[3].value = h;
1193 // XtSetValues(shellWidget, &shellArgs[2], 4);
1196 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1197 // It wil only become known asynchronously, when we first write a string into it.
1198 // This will then change the clock widget height, which triggers resizing the top-level window
1199 // and a configure event. Only then can we know the total height of the top-level window,
1200 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1201 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1204 gtk_widget_get_allocation(shells[BoardWindow], &a);
1205 w = a.width; h = a.height;
1206 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1207 clockKludge = hc = a.height;
1208 gtk_widget_get_allocation(boardWidget, &a);
1209 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1210 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1216 if(appData.logoSize)
1217 { // locate and read user logo
1218 char buf[MSG_SIZ], name[MSG_SIZ];
1219 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1220 if(!FindLogo(name, ".logo", buf))
1221 FindLogo(appData.logoDir, name + 6, buf);
1222 ASSIGN(userLogo, buf);
1225 if (appData.animate || appData.animateDragging)
1228 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1229 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1231 /* [AS] Restore layout */
1232 if( wpMoveHistory.visible ) {
1236 if( wpEvalGraph.visible )
1241 if( wpEngineOutput.visible ) {
1242 EngineOutputPopUp();
1245 if( wpConsole.visible && appData.icsActive ) {
1250 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1255 if (errorExitStatus == -1) {
1256 if (appData.icsActive) {
1257 /* We now wait until we see "login:" from the ICS before
1258 sending the logon script (problems with timestamp otherwise) */
1259 /*ICSInitScript();*/
1260 if (appData.icsInputBox) ICSInputBoxPopUp();
1264 signal(SIGWINCH, TermSizeSigHandler);
1266 signal(SIGINT, IntSigHandler);
1267 signal(SIGTERM, IntSigHandler);
1268 if (*appData.cmailGameName != NULLCHAR) {
1269 signal(SIGUSR1, CmailSigHandler);
1274 // XtSetKeyboardFocus(shellWidget, formWidget);
1276 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1279 /* check for GTK events and process them */
1282 gtk_main_iteration();
1285 if (appData.debugMode) fclose(debugFP); // [DM] debug
1292 while(gtk_events_pending()) gtk_main_iteration();
1296 TermSizeSigHandler (int sig)
1302 IntSigHandler (int sig)
1308 CmailSigHandler (int sig)
1313 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1315 /* Activate call-back function CmailSigHandlerCallBack() */
1316 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1318 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1322 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1325 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1327 /**** end signal code ****/
1330 #define Abs(n) ((n)<0 ? -(n) : (n))
1333 InsertPxlSize (char *pattern, int targetPxlSize)
1336 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1343 InsertPxlSize (char *pattern, int targetPxlSize)
1345 char *base_fnt_lst, strInt[12], *p, *q;
1346 int alternatives, i, len, strIntLen;
1349 * Replace the "*" (if present) in the pixel-size slot of each
1350 * alternative with the targetPxlSize.
1354 while ((p = strchr(p, ',')) != NULL) {
1358 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1359 strIntLen = strlen(strInt);
1360 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1364 while (alternatives--) {
1365 char *comma = strchr(p, ',');
1366 for (i=0; i<14; i++) {
1367 char *hyphen = strchr(p, '-');
1369 if (comma && hyphen > comma) break;
1370 len = hyphen + 1 - p;
1371 if (i == 7 && *p == '*' && len == 2) {
1373 memcpy(q, strInt, strIntLen);
1383 len = comma + 1 - p;
1390 return base_fnt_lst;
1396 CreateFontSet (char *base_fnt_lst)
1399 char **missing_list;
1403 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1404 &missing_list, &missing_count, &def_string);
1405 if (appData.debugMode) {
1407 XFontStruct **font_struct_list;
1408 char **font_name_list;
1409 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1411 fprintf(debugFP, " got list %s, locale %s\n",
1412 XBaseFontNameListOfFontSet(fntSet),
1413 XLocaleOfFontSet(fntSet));
1414 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1415 for (i = 0; i < count; i++) {
1416 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1419 for (i = 0; i < missing_count; i++) {
1420 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1423 if (fntSet == NULL) {
1424 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1430 #else // not ENABLE_NLS
1432 * Find a font that matches "pattern" that is as close as
1433 * possible to the targetPxlSize. Prefer fonts that are k
1434 * pixels smaller to fonts that are k pixels larger. The
1435 * pattern must be in the X Consortium standard format,
1436 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1437 * The return value should be freed with XtFree when no
1442 FindFont (char *pattern, int targetPxlSize)
1444 char **fonts, *p, *best, *scalable, *scalableTail;
1445 int i, j, nfonts, minerr, err, pxlSize;
1447 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1449 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1450 programName, pattern);
1457 for (i=0; i<nfonts; i++) {
1460 if (*p != '-') continue;
1462 if (*p == NULLCHAR) break;
1463 if (*p++ == '-') j++;
1465 if (j < 7) continue;
1468 scalable = fonts[i];
1471 err = pxlSize - targetPxlSize;
1472 if (Abs(err) < Abs(minerr) ||
1473 (minerr > 0 && err < 0 && -err == minerr)) {
1479 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1480 /* If the error is too big and there is a scalable font,
1481 use the scalable font. */
1482 int headlen = scalableTail - scalable;
1483 p = (char *) XtMalloc(strlen(scalable) + 10);
1484 while (isdigit(*scalableTail)) scalableTail++;
1485 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1487 p = (char *) XtMalloc(strlen(best) + 2);
1488 safeStrCpy(p, best, strlen(best)+1 );
1490 if (appData.debugMode) {
1491 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1492 pattern, targetPxlSize, p);
1494 XFreeFontNames(fonts);
1501 MarkMenuItem (char *menuRef, int state)
1503 MenuItem *item = MenuNameToItem(menuRef);
1505 if(item && item->handle) {
1506 ((GtkCheckMenuItem *) (item->handle))->active = state;
1512 EnableNamedMenuItem (char *menuRef, int state)
1514 MenuItem *item = MenuNameToItem(menuRef);
1516 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1521 EnableButtonBar (int state)
1524 XtSetSensitive(optList[W_BUTTON].handle, state);
1530 SetMenuEnables (Enables *enab)
1532 while (enab->name != NULL) {
1533 EnableNamedMenuItem(enab->name, enab->value);
1538 gboolean KeyPressProc(window, eventkey, data)
1540 GdkEventKey *eventkey;
1544 MoveTypeInProc(eventkey); // pop up for typed in moves
1547 /* check for other key values */
1548 switch(eventkey->keyval) {
1560 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1561 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1563 if(*nprms == 0) return;
1564 item = MenuNameToItem(prms[0]);
1565 if(item) ((MenuProc *) item->proc) ();
1579 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1580 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1581 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1582 dmEnables[i].piece);
1583 XtSetSensitive(entry, p != NULL || !appData.testLegality
1584 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1585 && !appData.icsActive));
1587 while (p && *p++ == dmEnables[i].piece) count++;
1588 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1590 XtSetArg(args[j], XtNlabel, label); j++;
1591 XtSetValues(entry, args, j);
1597 do_flash_delay (unsigned long msec)
1603 FlashDelay (int flash_delay)
1605 if(flash_delay) do_flash_delay(flash_delay);
1609 Fraction (int x, int start, int stop)
1611 double f = ((double) x - start)/(stop - start);
1612 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1616 static WindowPlacement wpNew;
1619 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1621 int touch=0, fudge = 4, f = 3;
1622 GetActualPlacement(sh, wp);
1623 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1624 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1625 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1626 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1627 //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);
1628 if(!touch ) return; // only windows that touch co-move
1629 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1630 int heightInc = wpNew.height - wpMain.height;
1631 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1632 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1633 wp->y += fracTop * heightInc;
1634 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1636 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1638 wp->height += heightInc;
1639 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1640 int widthInc = wpNew.width - wpMain.width;
1641 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1642 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1643 wp->y += fracLeft * widthInc;
1644 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1646 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1648 wp->width += widthInc;
1650 wp->x += wpNew.x - wpMain.x;
1651 wp->y += wpNew.y - wpMain.y;
1652 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1653 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1655 XtSetArg(args[j], XtNx, wp->x); j++;
1656 XtSetArg(args[j], XtNy, wp->y); j++;
1657 XtSetValues(sh, args, j);
1659 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1660 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1661 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1665 ReSize (WindowPlacement *wp)
1668 int sqx, sqy, w, h, lg = lineGap;
1669 static int first = 1;
1670 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
1671 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1672 w = a.width; h = a.height;
1673 gtk_widget_get_allocation(shellWidget, &a);
1674 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1675 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1676 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1677 w += a.width; h += a.height;
1679 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1680 w = a.width; h = a.height;
1682 sqx = (w - lg) / BOARD_WIDTH - lg;
1683 sqy = (h - lg) / BOARD_HEIGHT - lg;
1684 if(sqy < sqx) sqx = sqy;
1685 if(sqx < 20) return;
1686 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1688 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1689 sqx = (w - lg) / BOARD_WIDTH - lg;
1690 sqy = (h - lg) / BOARD_HEIGHT - lg;
1691 if(sqy < sqx) sqx = sqy;
1692 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1693 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1695 if(sqx != squareSize && !first) {
1696 squareSize = sqx; // adopt new square size
1697 CreatePNGPieces(); // make newly scaled pieces
1698 InitDrawingSizes(0, 0); // creates grid etc.
1699 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1700 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1701 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1702 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1703 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1704 first = appData.fixedSize;
1707 static guint delayedDragTag = 0;
1716 GetActualPlacement(shellWidget, &wpNew);
1717 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1718 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1719 busy = 0; return; // false alarm
1722 if(appData.useStickyWindows) {
1723 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1724 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1725 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1726 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1727 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1730 DrawPosition(True, NULL);
1731 if(delayedDragTag) g_source_remove(delayedDragTag);
1732 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1739 //printf("old timr = %d\n", delayedDragTag);
1740 if(delayedDragTag) g_source_remove(delayedDragTag);
1741 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1742 //printf("new timr = %d\n", delayedDragTag);
1746 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1748 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1750 wpNew.x = event->configure.x;
1751 wpNew.y = event->configure.y;
1752 wpNew.width = event->configure.width;
1753 wpNew.height = event->configure.height;
1754 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1760 /* Disable all user input other than deleting the window */
1761 static int frozen = 0;
1767 /* Grab by a widget that doesn't accept input */
1768 gtk_grab_add(optList[W_MESSG].handle);
1772 /* Undo a FreezeUI */
1776 if (!frozen) return;
1777 gtk_grab_remove(optList[W_MESSG].handle);
1784 static int oldPausing = FALSE;
1785 static GameMode oldMode = (GameMode) -1;
1787 if (!boardWidget) return;
1789 if (pausing != oldPausing) {
1790 oldPausing = pausing;
1791 MarkMenuItem("Mode.Pause", pausing);
1793 if (appData.showButtonBar) {
1794 /* Always toggle, don't set. Previous code messes up when
1795 invoked while the button is pressed, as releasing it
1796 toggles the state again. */
1798 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1799 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1803 wname = ModeToWidgetName(oldMode);
1804 if (wname != NULL) {
1805 MarkMenuItem(wname, False);
1807 wname = ModeToWidgetName(gameMode);
1808 if (wname != NULL) {
1809 MarkMenuItem(wname, True);
1811 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1812 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1815 /* Maybe all the enables should be handled here, not just this one */
1816 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1818 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1823 * Button/menu procedures
1826 void CopyFileToClipboard(gchar *filename)
1828 gchar *selection_tmp;
1832 FILE* f = fopen(filename, "r");
1835 if (f == NULL) return;
1839 selection_tmp = g_try_malloc(len + 1);
1840 if (selection_tmp == NULL) {
1841 printf("Malloc failed in CopyFileToClipboard\n");
1844 count = fread(selection_tmp, 1, len, f);
1847 g_free(selection_tmp);
1850 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1852 // copy selection_tmp to clipboard
1853 GdkDisplay *gdisp = gdk_display_get_default();
1855 g_free(selection_tmp);
1858 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1859 gtk_clipboard_set_text(cb, selection_tmp, -1);
1860 g_free(selection_tmp);
1864 CopySomething (char *src)
1866 GdkDisplay *gdisp = gdk_display_get_default();
1868 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1869 if (gdisp == NULL) return;
1870 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1871 gtk_clipboard_set_text(cb, src, -1);
1875 PastePositionProc ()
1877 GdkDisplay *gdisp = gdk_display_get_default();
1881 if (gdisp == NULL) return;
1882 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1883 fenstr = gtk_clipboard_wait_for_text(cb);
1884 if (fenstr==NULL) return; // nothing had been selected to copy
1885 EditPositionPasteFEN(fenstr);
1894 guint len=0; int flip = appData.flipView;
1897 // get game from clipboard
1898 GdkDisplay *gdisp = gdk_display_get_default();
1899 if (gdisp == NULL) return;
1900 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1901 text = gtk_clipboard_wait_for_text(cb);
1902 if (text == NULL) return; // nothing to paste
1905 // write to temp file
1906 if (text == NULL || len == 0) {
1907 return; //nothing to paste
1909 f = fopen(gamePasteFilename, "w");
1911 DisplayError(_("Can't open temp file"), errno);
1914 fwrite(text, 1, len, f);
1918 if(!appData.autoFlipView) appData.flipView = flipView;
1919 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1920 appData.flipView = flip;
1927 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1933 void MoveTypeInProc(eventkey)
1934 GdkEventKey *eventkey;
1938 // ingnore if ctrl, alt, or meta is pressed
1939 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1943 buf[0]=eventkey->keyval;
1945 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1946 ConsoleAutoPopUp (buf);
1951 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1953 if (!TempBackwardActive) {
1954 TempBackwardActive = True;
1960 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1962 /* Check to see if triggered by a key release event for a repeating key.
1963 * If so the next queued event will be a key press of the same key at the same time */
1964 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1966 XPeekEvent(xDisplay, &next);
1967 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1968 next.xkey.keycode == event->xkey.keycode)
1972 TempBackwardActive = False;
1978 { // called from menu
1981 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
1984 system("xterm -e man xboard &");
1993 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);
1995 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2003 SetWindowTitle (char *text, char *title, char *icon)
2008 if (appData.titleInWindow) {
2010 XtSetArg(args[i], XtNlabel, text); i++;
2011 XtSetValues(titleWidget, args, i);
2014 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2015 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2016 XtSetValues(shellWidget, args, i);
2017 XSync(xDisplay, False);
2019 if (appData.titleInWindow) {
2020 SetWidgetLabel(titleWidget, text);
2022 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2027 DisplayIcsInteractionTitle (String message)
2030 if (oldICSInteractionTitle == NULL) {
2031 /* Magic to find the old window title, adapted from vim */
2032 char *wina = getenv("WINDOWID");
2034 Window win = (Window) atoi(wina);
2035 Window root, parent, *children;
2036 unsigned int nchildren;
2037 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2039 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2040 if (!XQueryTree(xDisplay, win, &root, &parent,
2041 &children, &nchildren)) break;
2042 if (children) XFree((void *)children);
2043 if (parent == root || parent == 0) break;
2046 XSetErrorHandler(oldHandler);
2048 if (oldICSInteractionTitle == NULL) {
2049 oldICSInteractionTitle = "xterm";
2052 printf("\033]0;%s\007", message);
2059 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2061 GtkWidget *w = (GtkWidget *) opt->handle;
2068 strcpy(bgcolor, "black");
2069 strcpy(fgcolor, "white");
2071 strcpy(bgcolor, "white");
2072 strcpy(fgcolor, "black");
2075 appData.lowTimeWarning &&
2076 (timer / 1000) < appData.icsAlarmTime) {
2077 strcpy(fgcolor, appData.lowTimeWarningColor);
2080 gdk_color_parse( bgcolor, &col );
2081 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2083 if (appData.clockMode) {
2084 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2085 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2086 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2087 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2089 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2090 bgcolor, fgcolor, color);
2091 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2092 // bgcolor, fgcolor, color);
2094 gtk_label_set_markup(GTK_LABEL(w), markup);
2098 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2101 SetClockIcon (int color)
2103 GdkPixbuf *pm = *clockIcons[color];
2104 if (mainwindowIcon != pm) {
2105 mainwindowIcon = pm;
2107 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2109 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2114 #define INPUT_SOURCE_BUF_SIZE 8192
2123 char buf[INPUT_SOURCE_BUF_SIZE];
2128 DoInputCallback(io, cond, data)
2133 /* read input from one of the input source (for example a chess program, ICS, etc).
2134 * and call a function that will handle the input
2141 /* All information (callback function, file descriptor, etc) is
2142 * saved in an InputSource structure
2144 InputSource *is = (InputSource *) data;
2146 if (is->lineByLine) {
2147 count = read(is->fd, is->unused,
2148 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2150 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2151 RemoveInputSource(is); // cease reading stdin
2152 stdoutClosed = TRUE; // suppress future output
2155 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2158 is->unused += count;
2160 /* break input into lines and call the callback function on each
2163 while (p < is->unused) {
2164 q = memchr(p, '\n', is->unused - p);
2165 if (q == NULL) break;
2167 (is->func)(is, is->closure, p, q - p, 0);
2170 /* remember not yet used part of the buffer */
2172 while (p < is->unused) {
2177 /* read maximum length of input buffer and send the whole buffer
2178 * to the callback function
2180 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2185 (is->func)(is, is->closure, is->buf, count, error);
2187 return True; // Must return true or the watch will be removed
2190 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2197 GIOChannel *channel;
2198 ChildProc *cp = (ChildProc *) pr;
2200 is = (InputSource *) calloc(1, sizeof(InputSource));
2201 is->lineByLine = lineByLine;
2205 is->fd = fileno(stdin);
2207 is->kind = cp->kind;
2208 is->fd = cp->fdFrom;
2211 is->unused = is->buf;
2215 /* GTK-TODO: will this work on windows?*/
2217 channel = g_io_channel_unix_new(is->fd);
2218 g_io_channel_set_close_on_unref (channel, TRUE);
2219 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2221 is->closure = closure;
2222 return (InputSourceRef) is;
2227 RemoveInputSource(isr)
2230 InputSource *is = (InputSource *) isr;
2232 if (is->sid == 0) return;
2233 g_source_remove(is->sid);
2240 static Boolean frameWaiting;
2243 FrameAlarm (int sig)
2245 frameWaiting = False;
2246 /* In case System-V style signals. Needed?? */
2247 signal(SIGALRM, FrameAlarm);
2251 FrameDelay (int time)
2253 struct itimerval delay;
2256 frameWaiting = True;
2257 signal(SIGALRM, FrameAlarm);
2258 delay.it_interval.tv_sec =
2259 delay.it_value.tv_sec = time / 1000;
2260 delay.it_interval.tv_usec =
2261 delay.it_value.tv_usec = (time % 1000) * 1000;
2262 setitimer(ITIMER_REAL, &delay, NULL);
2263 while (frameWaiting) pause();
2264 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2265 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2266 setitimer(ITIMER_REAL, &delay, NULL);
2273 FrameDelay (int time)
2276 XSync(xDisplay, False);
2278 // gtk_main_iteration_do(False);
2281 usleep(time * 1000);
2287 FindLogo (char *place, char *name, char *buf)
2288 { // check if file exists in given place
2290 if(!place) return 0;
2291 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2292 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2300 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2302 char buf[MSG_SIZ], *logoName = buf;
2303 if(appData.logo[n][0]) {
2304 logoName = appData.logo[n];
2305 } else if(appData.autoLogo) {
2306 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2307 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2308 } else { // engine; cascade
2309 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2310 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2311 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2312 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2316 { ASSIGN(cps->programLogo, logoName); }
2320 UpdateLogos (int displ)
2322 if(optList[W_WHITE-1].handle == NULL) return;
2323 LoadLogo(&first, 0, 0);
2324 LoadLogo(&second, 1, appData.icsActive);
2325 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2329 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2340 GtkFileFilter *gtkfilter;
2341 GtkFileFilter *gtkfilter_all;
2343 char fileext[10] = "";
2344 char *result = NULL;
2346 char curDir[MSG_SIZ];
2348 StartDir(filter, NULL); // change to start directory for this file type
2350 if(def && *def && def[strlen(def)-1] == '/') {
2351 getcwd(curDir, MSG_SIZ);
2356 /* make a copy of the filter string, so that strtok can work with it*/
2357 cp = strdup(filter);
2359 /* add filters for file extensions */
2360 gtkfilter = gtk_file_filter_new();
2361 gtkfilter_all = gtk_file_filter_new();
2363 /* one filter to show everything */
2364 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2365 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2367 /* add filter if present */
2368 result = strtok(cp, space);
2369 while( result != NULL ) {
2370 snprintf(fileext,10,"*%s",result);
2371 result = strtok( NULL, space );
2372 gtk_file_filter_add_pattern(gtkfilter, fileext);
2375 /* second filter to only show what's useful */
2376 gtk_file_filter_set_name (gtkfilter,filter);
2378 if (openMode[0] == 'r')
2380 dialog = gtk_file_chooser_dialog_new (label,
2382 GTK_FILE_CHOOSER_ACTION_OPEN,
2383 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2384 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2389 dialog = gtk_file_chooser_dialog_new (label,
2391 GTK_FILE_CHOOSER_ACTION_SAVE,
2392 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2393 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2395 /* add filename suggestions */
2396 if (strlen(def) > 0 )
2397 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2399 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2403 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2404 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2405 /* activate filter */
2406 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2408 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2413 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2416 f = fopen(filename, openMode);
2419 DisplayError(_("Failed to open file"), errno);
2423 /* TODO add indec */
2425 ASSIGN(*name, filename);
2426 ScheduleDelayedEvent(DelayedLoad, 50);
2428 StartDir(filter, filename);
2431 else StartDir(filter, "");
2433 gtk_widget_destroy (dialog);
2436 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);