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
180 # undef SETTINGS_FILE
181 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
182 # define DATADIR dataDir
183 # define LOCALEDIR localeDir
184 # define SETTINGS_FILE masterSettings
185 # define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
186 char dataDir[MSG_SIZ]; // for expanding ~~
187 char localeDir[MSG_SIZ];
188 char masterSettings[MSG_SIZ];
192 # define SYNC_MENUBAR
199 #define usleep(t) _sleep2(((t)+500)/1000)
203 # define _(s) gettext (s)
204 # define N_(s) gettext_noop (s)
210 int main P((int argc, char **argv));
211 RETSIGTYPE CmailSigHandler P((int sig));
212 RETSIGTYPE IntSigHandler P((int sig));
213 RETSIGTYPE TermSizeSigHandler P((int sig));
214 char *InsertPxlSize P((char *pattern, int targetPxlSize));
217 XFontSet CreateFontSet P((char *base_fnt_lst));
219 char *FindFont P((char *pattern, int targetPxlSize));
222 void DelayedDrag P((void));
223 void ICSInputBoxPopUp P((void));
224 void MoveTypeInProc P((GdkEventKey *eventkey));
225 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
226 Boolean TempBackwardActive = False;
227 void DisplayMove P((int moveNumber));
228 void update_ics_width P(());
229 int CopyMemoProc P(());
230 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
231 static int FindLogo P((char *place, char *name, char *buf));
235 XFontSet fontSet, clockFontSet;
238 XFontStruct *clockFontStruct;
240 Font coordFontID, countFontID;
241 XFontStruct *coordFontStruct, *countFontStruct;
243 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
244 GtkWidget *mainwindow;
246 Option *optList; // contains all widgets of main window
249 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
252 static GdkPixbuf *mainwindowIcon=NULL;
253 static GdkPixbuf *WhiteIcon=NULL;
254 static GdkPixbuf *BlackIcon=NULL;
256 /* key board accelerators */
257 GtkAccelGroup *GtkAccelerators;
259 typedef unsigned int BoardSize;
261 Boolean chessProgram;
263 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
264 int smallLayout = 0, tinyLayout = 0,
265 marginW, marginH, // [HGM] for run-time resizing
266 fromX = -1, fromY = -1, toX, toY, commentUp = False,
267 errorExitStatus = -1, defaultLineGap;
269 Dimension textHeight;
271 char *chessDir, *programName, *programVersion;
272 Boolean alwaysOnTop = False;
273 char *icsTextMenuString;
275 char *firstChessProgramNames;
276 char *secondChessProgramNames;
278 WindowPlacement wpMain;
279 WindowPlacement wpConsole;
280 WindowPlacement wpComment;
281 WindowPlacement wpMoveHistory;
282 WindowPlacement wpEvalGraph;
283 WindowPlacement wpEngineOutput;
284 WindowPlacement wpGameList;
285 WindowPlacement wpTags;
286 WindowPlacement wpDualBoard;
288 /* This magic number is the number of intermediate frames used
289 in each half of the animation. For short moves it's reduced
290 by 1. The total number of frames will be factor * 2 + 1. */
293 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
300 DropMenuEnables dmEnables[] = {
309 XtResource clientResources[] = {
310 { "flashCount", "flashCount", XtRInt, sizeof(int),
311 XtOffset(AppDataPtr, flashCount), XtRImmediate,
312 (XtPointer) FLASH_COUNT },
316 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
317 char globalTranslations[] =
318 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
319 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
320 :<KeyDown>Return: TempBackwardProc() \n \
321 :<KeyUp>Return: TempForwardProc() \n";
323 char ICSInputTranslations[] =
324 "<Key>Up: UpKeyProc() \n "
325 "<Key>Down: DownKeyProc() \n "
326 "<Key>Return: EnterKeyProc() \n";
328 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
329 // as the widget is destroyed before the up-click can call extend-end
330 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
333 String xboardResources[] = {
334 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
342 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
345 //---------------------------------------------------------------------------------------------------------
346 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
349 #define CW_USEDEFAULT (1<<31)
350 #define ICS_TEXT_MENU_SIZE 90
351 #define DEBUG_FILE "xboard.debug"
352 #define SetCurrentDirectory chdir
353 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
357 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
360 // front-end part of option handling
362 // [HGM] This platform-dependent table provides the location for storing the color info
363 extern char *crWhite, * crBlack;
367 &appData.whitePieceColor,
368 &appData.blackPieceColor,
369 &appData.lightSquareColor,
370 &appData.darkSquareColor,
371 &appData.highlightSquareColor,
372 &appData.premoveHighlightColor,
373 &appData.lowTimeWarningColor,
384 // [HGM] font: keep a font for each square size, even non-stndard ones
387 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
388 char *fontTable[NUM_FONTS][MAX_SIZE];
391 ParseFont (char *name, int number)
392 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
394 if(sscanf(name, "size%d:", &size)) {
395 // [HGM] font: font is meant for specific boardSize (likely from settings file);
396 // defer processing it until we know if it matches our board size
397 if(!strstr(name, "-*-") && // ignore X-fonts
398 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
399 fontTable[number][size] = strdup(strchr(name, ':')+1);
400 fontValid[number][size] = True;
405 case 0: // CLOCK_FONT
406 appData.clockFont = strdup(name);
408 case 1: // MESSAGE_FONT
409 appData.font = strdup(name);
411 case 2: // COORD_FONT
412 appData.coordFont = strdup(name);
415 appData.icsFont = strdup(name);
418 appData.tagsFont = strdup(name);
421 appData.commentFont = strdup(name);
423 case MOVEHISTORY_FONT:
424 appData.historyFont = strdup(name);
427 appData.gameListFont = strdup(name);
432 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
437 { // only 2 fonts currently
438 appData.clockFont = strdup(CLOCK_FONT_NAME);
439 appData.coordFont = strdup(COORD_FONT_NAME);
440 appData.font = strdup(DEFAULT_FONT_NAME);
441 appData.icsFont = strdup(CONSOLE_FONT_NAME);
442 appData.tagsFont = strdup(TAGS_FONT_NAME);
443 appData.commentFont = strdup(COMMENT_FONT_NAME);
444 appData.historyFont = strdup(HISTORY_FONT_NAME);
445 appData.gameListFont = strdup(GAMELIST_FONT_NAME);
450 { // no-op, until we identify the code for this already in XBoard and move it here
454 ParseColor (int n, char *name)
455 { // in XBoard, just copy the color-name string
456 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
462 return *(char**)colorVariable[n];
466 ParseTextAttribs (ColorClass cc, char *s)
468 (&appData.colorShout)[cc] = strdup(s);
472 ParseBoardSize (void *addr, char *name)
474 appData.boardSize = strdup(name);
479 { // In XBoard the sound-playing program takes care of obtaining the actual sound
483 SetCommPortDefaults ()
484 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
487 // [HGM] args: these three cases taken out to stay in front-end
489 SaveFontArg (FILE *f, ArgDescriptor *ad)
492 int i, n = (int)(intptr_t)ad->argLoc;
494 case 0: // CLOCK_FONT
495 name = appData.clockFont;
497 case 1: // MESSAGE_FONT
500 case 2: // COORD_FONT
501 name = appData.coordFont;
504 name = appData.icsFont;
507 name = appData.tagsFont;
510 name = appData.commentFont;
512 case MOVEHISTORY_FONT:
513 name = appData.historyFont;
516 name = appData.gameListFont;
521 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
522 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
523 fontTable[n][squareSize] = strdup(name);
524 fontValid[n][squareSize] = True;
527 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
528 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
533 { // nothing to do, as the sounds are at all times represented by their text-string names already
537 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
538 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
539 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
543 SaveColor (FILE *f, ArgDescriptor *ad)
544 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
545 if(colorVariable[(int)(intptr_t)ad->argLoc])
546 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
550 SaveBoardSize (FILE *f, char *name, void *addr)
551 { // wrapper to shield back-end from BoardSize & sizeInfo
552 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
556 ParseCommPortSettings (char *s)
557 { // no such option in XBoard (yet)
563 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
567 gtk_widget_get_allocation(shell, &a);
568 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
572 wp->height = a.height;
573 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
574 frameX = 3; frameY = 3; // remember to decide if windows touch
578 GetPlacement (DialogClass dlg, WindowPlacement *wp)
579 { // wrapper to shield back-end from widget type
580 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
585 { // wrapper to shield use of window handles from back-end (make addressible by number?)
586 // In XBoard this will have to wait until awareness of window parameters is implemented
587 GetActualPlacement(shellWidget, &wpMain);
588 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
589 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
590 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
591 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
592 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
593 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
594 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
598 PrintCommPortSettings (FILE *f, char *name)
599 { // This option does not exist in XBoard
603 EnsureOnScreen (int *x, int *y, int minX, int minY)
610 { // [HGM] args: allows testing if main window is realized from back-end
611 return DialogExists(BoardWindow);
615 PopUpStartupDialog ()
616 { // start menu not implemented in XBoard
620 ConvertToLine (int argc, char **argv)
622 static char line[128*1024], buf[1024];
626 for(i=1; i<argc; i++)
628 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
629 && argv[i][0] != '{' )
630 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
632 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
633 strncat(line, buf, 128*1024 - strlen(line) - 1 );
636 line[strlen(line)-1] = NULLCHAR;
640 //--------------------------------------------------------------------------------------------
645 ResizeBoardWindow (int w, int h, int inhibit)
649 // if(clockKludge) return; // ignore as long as clock does not have final height
650 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
652 gtk_widget_get_allocation(shellWidget, &a);
653 marginW = a.width - bw;
654 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
655 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
656 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
657 // h += marginH + a.height + 1;
658 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
660 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board again
665 { // dummy, as the GTK code does not make colors in advance
670 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
671 { // determine what fonts to use, and create them
673 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
674 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
675 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
676 appData.font = fontTable[MESSAGE_FONT][squareSize];
677 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
678 appData.coordFont = fontTable[COORD_FONT][squareSize];
679 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
680 appData.icsFont = fontTable[CONSOLE_FONT][squareSize];
681 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
682 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize];
683 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
684 appData.commentFont = fontTable[COMMENT_FONT][squareSize];
685 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
686 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize];
687 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
688 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize];
690 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
691 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
692 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
693 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
694 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
695 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
696 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
697 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
703 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
704 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
705 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
706 appData.font = fontTable[MESSAGE_FONT][squareSize];
707 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
708 appData.coordFont = fontTable[COORD_FONT][squareSize];
711 appData.font = InsertPxlSize(appData.font, fontPxlSize);
712 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
713 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
714 fontSet = CreateFontSet(appData.font);
715 clockFontSet = CreateFontSet(appData.clockFont);
717 /* For the coordFont, use the 0th font of the fontset. */
718 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
719 XFontStruct **font_struct_list;
720 XFontSetExtents *fontSize;
721 char **font_name_list;
722 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
723 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
724 coordFontStruct = XQueryFont(xDisplay, coordFontID);
725 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
726 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
729 appData.font = FindFont(appData.font, fontPxlSize);
730 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
731 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
732 clockFontID = XLoadFont(xDisplay, appData.clockFont);
733 clockFontStruct = XQueryFont(xDisplay, clockFontID);
734 coordFontID = XLoadFont(xDisplay, appData.coordFont);
735 coordFontStruct = XQueryFont(xDisplay, coordFontID);
736 // textHeight in !NLS mode!
738 countFontID = coordFontID; // [HGM] holdings
739 countFontStruct = coordFontStruct;
741 xdb = XtDatabase(xDisplay);
743 XrmPutLineResource(&xdb, "*international: True");
744 vTo.size = sizeof(XFontSet);
745 vTo.addr = (XtPointer) &fontSet;
746 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
748 XrmPutStringResource(&xdb, "*font", appData.font);
759 case ArgInt: p = " N"; break;
760 case ArgString: p = " STR"; break;
761 case ArgBoolean: p = " TF"; break;
762 case ArgSettingsFilename:
763 case ArgBackupSettingsFile:
764 case ArgFilename: p = " FILE"; break;
765 case ArgX: p = " Nx"; break;
766 case ArgY: p = " Ny"; break;
767 case ArgAttribs: p = " TEXTCOL"; break;
768 case ArgColor: p = " COL"; break;
769 case ArgFont: p = " FONT"; break;
770 case ArgBoardSize: p = " SIZE"; break;
771 case ArgFloat: p = " FLOAT"; break;
776 case ArgCommSettings:
788 ArgDescriptor *q, *p = argDescriptors+5;
789 printf("\nXBoard accepts the following options:\n"
790 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
791 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
792 " SIZE = board-size spec(s)\n"
793 " Within parentheses are short forms, or options to set to true or false.\n"
794 " Persistent options (saved in the settings file) are marked with *)\n\n");
796 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
797 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
798 if(p->save) strcat(buf+len, "*");
799 for(q=p+1; q->argLoc == p->argLoc; q++) {
800 if(q->argName[0] == '-') continue;
801 strcat(buf+len, q == p+1 ? " (" : " ");
802 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
804 if(q != p+1) strcat(buf+len, ")");
806 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
809 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
813 SlaveResize (Option *opt)
815 static int slaveW, slaveH, w, h;
818 gtk_widget_get_allocation(shells[DummyDlg], &a);
819 w = a.width; h = a.height;
820 gtk_widget_get_allocation(opt->handle, &a);
821 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
822 slaveH = h - a.height + 13;
824 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
828 LoadIconFile (gchar *svgFilename)
832 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
833 return gdk_pixbuf_new_from_file(buf, NULL);
837 static char clickedFile[MSG_SIZ];
841 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
842 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
845 if(1000*now.sec + now.ms - 1000*started.sec - started.ms < 1000) { // received during first second
846 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
847 } else { // we are running something presumably useful
849 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
850 system(buf); // start new instance on this file
855 GtkosxApplication *theApp;
859 main (int argc, char **argv)
861 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
862 int boardWidth, w, h; //, boardHeight;
864 int forceMono = False;
866 srandom(time(0)); // [HGM] book: make random truly random
868 setbuf(stdout, NULL);
869 setbuf(stderr, NULL);
872 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
873 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
877 if(argc > 1 && !strcmp(argv[1], "--help" )) {
883 gtk_init (&argc, &argv);
885 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
886 char *path = gtkosx_application_get_bundle_path();
888 char *res_path = gtkosx_application_get_resource_path();
889 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
891 GetTimeMark(&started); // remember start time
892 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
893 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
894 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
895 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
896 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
897 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
898 // we must call application ready before we can get the signal,
899 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
900 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
901 gtkosx_application_ready(theApp);
902 if(argc == 1) { // called without args: OSX open-file signal might follow
903 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
904 usleep(10000); // wait 10 msec (and hope this is long enough).
905 while(gtk_events_pending())
906 gtk_main_iteration(); // process all events that came in upto now
907 if(clickedFile[0]) { // we were sent an open-file signal with filename!
908 fakeArgv[0] = argv[0];
909 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
915 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
916 typedef struct {char *name, *value; } Config;
917 static Config configList[] = {
918 { "Datadir", DATADIR },
919 { "Sysconfdir", SYSCONFDIR },
924 for(i=0; configList[i].name; i++) {
925 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
926 if(argc > 2) printf("%s", configList[i].value);
927 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
932 /* set up keyboard accelerators group */
933 GtkAccelerators = gtk_accel_group_new();
935 programName = strrchr(argv[0], '/');
936 if (programName == NULL)
937 programName = argv[0];
942 // if (appData.debugMode) {
943 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
946 bindtextdomain(PACKAGE, LOCALEDIR);
947 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
951 appData.boardSize = "";
952 InitAppData(ConvertToLine(argc, argv));
954 if (p == NULL) p = "/tmp";
955 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
956 gameCopyFilename = (char*) malloc(i);
957 gamePasteFilename = (char*) malloc(i);
958 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
959 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
961 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
962 static char buf[MSG_SIZ];
963 snprintf(buf, MSG_SIZ, appData.sysOpen, DATADIR);
964 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
965 EscapeExpand(buf, appData.firstInitString);
966 appData.firstInitString = strdup(buf);
967 EscapeExpand(buf, appData.secondInitString);
968 appData.secondInitString = strdup(buf);
969 EscapeExpand(buf, appData.firstComputerString);
970 appData.firstComputerString = strdup(buf);
971 EscapeExpand(buf, appData.secondComputerString);
972 appData.secondComputerString = strdup(buf);
975 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
978 if (chdir(chessDir) != 0) {
979 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
985 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
986 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
987 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
988 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
991 setbuf(debugFP, NULL);
995 if (appData.debugMode) {
996 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1000 /* [HGM,HR] make sure board size is acceptable */
1001 if(appData.NrFiles > BOARD_FILES ||
1002 appData.NrRanks > BOARD_RANKS )
1003 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1006 /* This feature does not work; animation needs a rewrite */
1007 appData.highlightDragging = FALSE;
1011 gameInfo.variant = StringToVariant(appData.variant);
1012 InitPosition(FALSE);
1015 * determine size, based on supplied or remembered -size, or screen size
1017 if (isdigit(appData.boardSize[0])) {
1018 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1019 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1020 &fontPxlSize, &smallLayout, &tinyLayout);
1022 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1023 programName, appData.boardSize);
1027 /* Find some defaults; use the nearest known size */
1028 SizeDefaults *szd, *nearest;
1029 int distance = 99999;
1030 nearest = szd = sizeDefaults;
1031 while (szd->name != NULL) {
1032 if (abs(szd->squareSize - squareSize) < distance) {
1034 distance = abs(szd->squareSize - squareSize);
1035 if (distance == 0) break;
1039 if (i < 2) lineGap = nearest->lineGap;
1040 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1041 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1042 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1043 if (i < 6) smallLayout = nearest->smallLayout;
1044 if (i < 7) tinyLayout = nearest->tinyLayout;
1047 SizeDefaults *szd = sizeDefaults;
1048 if (*appData.boardSize == NULLCHAR) {
1049 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1050 GdkScreen *screen = gdk_screen_get_default();
1051 guint screenwidth = gdk_screen_get_width(screen);
1052 guint screenheight = gdk_screen_get_height(screen);
1053 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1054 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1057 if (szd->name == NULL) szd--;
1058 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1060 while (szd->name != NULL &&
1061 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1062 if (szd->name == NULL) {
1063 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1064 programName, appData.boardSize);
1068 squareSize = szd->squareSize;
1069 lineGap = szd->lineGap;
1070 clockFontPxlSize = szd->clockFontPxlSize;
1071 coordFontPxlSize = szd->coordFontPxlSize;
1072 fontPxlSize = szd->fontPxlSize;
1073 smallLayout = szd->smallLayout;
1074 tinyLayout = szd->tinyLayout;
1075 // [HGM] font: use defaults from settings file if available and not overruled
1077 if(BOARD_WIDTH != 8) {
1078 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
1079 lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
1082 defaultLineGap = lineGap;
1083 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1085 /* [HR] height treated separately (hacked) */
1086 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1087 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1090 * Determine what fonts to use.
1092 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1095 * Detect if there are not enough colors available and adapt.
1098 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1099 appData.monoMode = True;
1103 forceMono = MakeColors();
1106 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1108 appData.monoMode = True;
1111 ParseIcsTextColors();
1117 layoutName = "tinyLayout";
1118 } else if (smallLayout) {
1119 layoutName = "smallLayout";
1121 layoutName = "normalLayout";
1124 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1125 wpMain.width = -1; // prevent popup sizes window
1126 optList = BoardPopUp(squareSize, lineGap, (void*)
1136 InitDrawingHandle(optList + W_BOARD);
1137 shellWidget = shells[BoardWindow];
1138 currBoard = &optList[W_BOARD];
1139 boardWidget = optList[W_BOARD].handle;
1140 menuBarWidget = optList[W_MENU].handle;
1141 dropMenu = optList[W_DROP].handle;
1142 titleWidget = optList[optList[W_TITLE].type != Skip ? W_TITLE : W_SMALL].handle;
1144 formWidget = XtParent(boardWidget);
1145 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1146 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1147 XtGetValues(optList[W_WHITE].handle, args, 2);
1148 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1149 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1150 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1151 XtGetValues(optList[W_PAUSE].handle, args, 2);
1155 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1156 // not need to go into InitDrawingSizes().
1160 // add accelerators to main shell
1161 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1164 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1166 WhiteIcon = LoadIconFile("icon_white");
1167 BlackIcon = LoadIconFile("icon_black");
1168 SetClockIcon(0); // sets white icon
1172 * Create a cursor for the board widget.
1175 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1176 XChangeWindowAttributes(xDisplay, xBoardWindow,
1177 CWCursor, &window_attributes);
1181 * Inhibit shell resizing.
1184 shellArgs[0].value = (XtArgVal) &w;
1185 shellArgs[1].value = (XtArgVal) &h;
1186 XtGetValues(shellWidget, shellArgs, 2);
1187 shellArgs[4].value = shellArgs[2].value = w;
1188 shellArgs[5].value = shellArgs[3].value = h;
1189 // XtSetValues(shellWidget, &shellArgs[2], 4);
1192 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1193 // It wil only become known asynchronously, when we first write a string into it.
1194 // This will then change the clock widget height, which triggers resizing the top-level window
1195 // and a configure event. Only then can we know the total height of the top-level window,
1196 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1197 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1200 gtk_widget_get_allocation(shells[BoardWindow], &a);
1201 w = a.width; h = a.height;
1202 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1203 clockKludge = hc = a.height;
1204 gtk_widget_get_allocation(boardWidget, &a);
1205 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1206 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1212 if(appData.logoSize)
1213 { // locate and read user logo
1214 char buf[MSG_SIZ], name[MSG_SIZ];
1215 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1216 if(!FindLogo(name, ".logo", buf))
1217 FindLogo(appData.logoDir, name + 6, buf);
1218 ASSIGN(userLogo, buf);
1221 if (appData.animate || appData.animateDragging)
1224 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1225 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1227 /* [AS] Restore layout */
1228 if( wpMoveHistory.visible ) {
1232 if( wpEvalGraph.visible )
1237 if( wpEngineOutput.visible ) {
1238 EngineOutputPopUp();
1241 if( wpConsole.visible && appData.icsActive ) {
1246 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1251 if (errorExitStatus == -1) {
1252 if (appData.icsActive) {
1253 /* We now wait until we see "login:" from the ICS before
1254 sending the logon script (problems with timestamp otherwise) */
1255 /*ICSInitScript();*/
1256 if (appData.icsInputBox) ICSInputBoxPopUp();
1260 signal(SIGWINCH, TermSizeSigHandler);
1262 signal(SIGINT, IntSigHandler);
1263 signal(SIGTERM, IntSigHandler);
1264 if (*appData.cmailGameName != NULLCHAR) {
1265 signal(SIGUSR1, CmailSigHandler);
1270 // XtSetKeyboardFocus(shellWidget, formWidget);
1272 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1275 /* check for GTK events and process them */
1278 gtk_main_iteration();
1281 if (appData.debugMode) fclose(debugFP); // [DM] debug
1288 while(gtk_events_pending()) gtk_main_iteration();
1292 TermSizeSigHandler (int sig)
1298 IntSigHandler (int sig)
1304 CmailSigHandler (int sig)
1309 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1311 /* Activate call-back function CmailSigHandlerCallBack() */
1312 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1314 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1318 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1321 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1323 /**** end signal code ****/
1326 #define Abs(n) ((n)<0 ? -(n) : (n))
1329 InsertPxlSize (char *pattern, int targetPxlSize)
1332 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1339 InsertPxlSize (char *pattern, int targetPxlSize)
1341 char *base_fnt_lst, strInt[12], *p, *q;
1342 int alternatives, i, len, strIntLen;
1345 * Replace the "*" (if present) in the pixel-size slot of each
1346 * alternative with the targetPxlSize.
1350 while ((p = strchr(p, ',')) != NULL) {
1354 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1355 strIntLen = strlen(strInt);
1356 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1360 while (alternatives--) {
1361 char *comma = strchr(p, ',');
1362 for (i=0; i<14; i++) {
1363 char *hyphen = strchr(p, '-');
1365 if (comma && hyphen > comma) break;
1366 len = hyphen + 1 - p;
1367 if (i == 7 && *p == '*' && len == 2) {
1369 memcpy(q, strInt, strIntLen);
1379 len = comma + 1 - p;
1386 return base_fnt_lst;
1392 CreateFontSet (char *base_fnt_lst)
1395 char **missing_list;
1399 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1400 &missing_list, &missing_count, &def_string);
1401 if (appData.debugMode) {
1403 XFontStruct **font_struct_list;
1404 char **font_name_list;
1405 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1407 fprintf(debugFP, " got list %s, locale %s\n",
1408 XBaseFontNameListOfFontSet(fntSet),
1409 XLocaleOfFontSet(fntSet));
1410 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1411 for (i = 0; i < count; i++) {
1412 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1415 for (i = 0; i < missing_count; i++) {
1416 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1419 if (fntSet == NULL) {
1420 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1426 #else // not ENABLE_NLS
1428 * Find a font that matches "pattern" that is as close as
1429 * possible to the targetPxlSize. Prefer fonts that are k
1430 * pixels smaller to fonts that are k pixels larger. The
1431 * pattern must be in the X Consortium standard format,
1432 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1433 * The return value should be freed with XtFree when no
1438 FindFont (char *pattern, int targetPxlSize)
1440 char **fonts, *p, *best, *scalable, *scalableTail;
1441 int i, j, nfonts, minerr, err, pxlSize;
1443 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1445 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1446 programName, pattern);
1453 for (i=0; i<nfonts; i++) {
1456 if (*p != '-') continue;
1458 if (*p == NULLCHAR) break;
1459 if (*p++ == '-') j++;
1461 if (j < 7) continue;
1464 scalable = fonts[i];
1467 err = pxlSize - targetPxlSize;
1468 if (Abs(err) < Abs(minerr) ||
1469 (minerr > 0 && err < 0 && -err == minerr)) {
1475 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1476 /* If the error is too big and there is a scalable font,
1477 use the scalable font. */
1478 int headlen = scalableTail - scalable;
1479 p = (char *) XtMalloc(strlen(scalable) + 10);
1480 while (isdigit(*scalableTail)) scalableTail++;
1481 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1483 p = (char *) XtMalloc(strlen(best) + 2);
1484 safeStrCpy(p, best, strlen(best)+1 );
1486 if (appData.debugMode) {
1487 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1488 pattern, targetPxlSize, p);
1490 XFreeFontNames(fonts);
1497 MarkMenuItem (char *menuRef, int state)
1499 MenuItem *item = MenuNameToItem(menuRef);
1501 if(item && item->handle) {
1502 ((GtkCheckMenuItem *) (item->handle))->active = state;
1508 EnableNamedMenuItem (char *menuRef, int state)
1510 MenuItem *item = MenuNameToItem(menuRef);
1512 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1517 EnableButtonBar (int state)
1520 XtSetSensitive(optList[W_BUTTON].handle, state);
1526 SetMenuEnables (Enables *enab)
1528 while (enab->name != NULL) {
1529 EnableNamedMenuItem(enab->name, enab->value);
1534 gboolean KeyPressProc(window, eventkey, data)
1536 GdkEventKey *eventkey;
1540 MoveTypeInProc(eventkey); // pop up for typed in moves
1543 /* check for other key values */
1544 switch(eventkey->keyval) {
1556 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1557 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1559 if(*nprms == 0) return;
1560 item = MenuNameToItem(prms[0]);
1561 if(item) ((MenuProc *) item->proc) ();
1575 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1576 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1577 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1578 dmEnables[i].piece);
1579 XtSetSensitive(entry, p != NULL || !appData.testLegality
1580 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1581 && !appData.icsActive));
1583 while (p && *p++ == dmEnables[i].piece) count++;
1584 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1586 XtSetArg(args[j], XtNlabel, label); j++;
1587 XtSetValues(entry, args, j);
1593 do_flash_delay (unsigned long msec)
1599 FlashDelay (int flash_delay)
1601 if(flash_delay) do_flash_delay(flash_delay);
1605 Fraction (int x, int start, int stop)
1607 double f = ((double) x - start)/(stop - start);
1608 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1612 static WindowPlacement wpNew;
1615 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1617 int touch=0, fudge = 4, f = 3;
1618 GetActualPlacement(sh, wp);
1619 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1620 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1621 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1622 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1623 //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);
1624 if(!touch ) return; // only windows that touch co-move
1625 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1626 int heightInc = wpNew.height - wpMain.height;
1627 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1628 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1629 wp->y += fracTop * heightInc;
1630 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1632 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1634 wp->height += heightInc;
1635 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1636 int widthInc = wpNew.width - wpMain.width;
1637 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1638 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1639 wp->y += fracLeft * widthInc;
1640 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1642 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1644 wp->width += widthInc;
1646 wp->x += wpNew.x - wpMain.x;
1647 wp->y += wpNew.y - wpMain.y;
1648 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1649 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1651 XtSetArg(args[j], XtNx, wp->x); j++;
1652 XtSetArg(args[j], XtNy, wp->y); j++;
1653 XtSetValues(sh, args, j);
1655 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1656 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1657 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1661 ReSize (WindowPlacement *wp)
1664 int sqx, sqy, w, h, lg = lineGap;
1665 static int first = 1;
1666 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
1667 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1668 w = a.width; h = a.height;
1669 gtk_widget_get_allocation(shellWidget, &a);
1670 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1671 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1672 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1673 w += a.width; h += a.height;
1675 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1676 w = a.width; h = a.height;
1678 sqx = (w - lg) / BOARD_WIDTH - lg;
1679 sqy = (h - lg) / BOARD_HEIGHT - lg;
1680 if(sqy < sqx) sqx = sqy;
1681 if(sqx < 20) return;
1682 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1684 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1685 sqx = (w - lg) / BOARD_WIDTH - lg;
1686 sqy = (h - lg) / BOARD_HEIGHT - lg;
1687 if(sqy < sqx) sqx = sqy;
1688 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1689 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1691 if(sqx != squareSize && !first) {
1692 squareSize = sqx; // adopt new square size
1693 CreatePNGPieces(); // make newly scaled pieces
1694 InitDrawingSizes(0, 0); // creates grid etc.
1695 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1696 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1697 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1698 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1699 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1700 first = appData.fixedSize;
1703 static guint delayedDragTag = 0;
1712 GetActualPlacement(shellWidget, &wpNew);
1713 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1714 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1715 busy = 0; return; // false alarm
1718 if(appData.useStickyWindows) {
1719 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1720 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1721 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1722 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1723 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1726 DrawPosition(True, NULL);
1727 if(delayedDragTag) g_source_remove(delayedDragTag);
1728 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1735 //printf("old timr = %d\n", delayedDragTag);
1736 if(delayedDragTag) g_source_remove(delayedDragTag);
1737 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1738 //printf("new timr = %d\n", delayedDragTag);
1742 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1744 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1746 wpNew.x = event->configure.x;
1747 wpNew.y = event->configure.y;
1748 wpNew.width = event->configure.width;
1749 wpNew.height = event->configure.height;
1750 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1756 /* Disable all user input other than deleting the window */
1757 static int frozen = 0;
1763 /* Grab by a widget that doesn't accept input */
1764 gtk_grab_add(optList[W_MESSG].handle);
1768 /* Undo a FreezeUI */
1772 if (!frozen) return;
1773 gtk_grab_remove(optList[W_MESSG].handle);
1780 static int oldPausing = FALSE;
1781 static GameMode oldMode = (GameMode) -1;
1783 if (!boardWidget) return;
1785 if (pausing != oldPausing) {
1786 oldPausing = pausing;
1787 MarkMenuItem("Mode.Pause", pausing);
1789 if (appData.showButtonBar) {
1790 /* Always toggle, don't set. Previous code messes up when
1791 invoked while the button is pressed, as releasing it
1792 toggles the state again. */
1794 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1795 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1799 wname = ModeToWidgetName(oldMode);
1800 if (wname != NULL) {
1801 MarkMenuItem(wname, False);
1803 wname = ModeToWidgetName(gameMode);
1804 if (wname != NULL) {
1805 MarkMenuItem(wname, True);
1807 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1808 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1811 /* Maybe all the enables should be handled here, not just this one */
1812 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1814 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1819 * Button/menu procedures
1822 void CopyFileToClipboard(gchar *filename)
1824 gchar *selection_tmp;
1828 FILE* f = fopen(filename, "r");
1831 if (f == NULL) return;
1835 selection_tmp = g_try_malloc(len + 1);
1836 if (selection_tmp == NULL) {
1837 printf("Malloc failed in CopyFileToClipboard\n");
1840 count = fread(selection_tmp, 1, len, f);
1843 g_free(selection_tmp);
1846 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1848 // copy selection_tmp to clipboard
1849 GdkDisplay *gdisp = gdk_display_get_default();
1851 g_free(selection_tmp);
1854 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1855 gtk_clipboard_set_text(cb, selection_tmp, -1);
1856 g_free(selection_tmp);
1860 CopySomething (char *src)
1862 GdkDisplay *gdisp = gdk_display_get_default();
1864 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1865 if (gdisp == NULL) return;
1866 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1867 gtk_clipboard_set_text(cb, src, -1);
1871 PastePositionProc ()
1873 GdkDisplay *gdisp = gdk_display_get_default();
1877 if (gdisp == NULL) return;
1878 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1879 fenstr = gtk_clipboard_wait_for_text(cb);
1880 if (fenstr==NULL) return; // nothing had been selected to copy
1881 EditPositionPasteFEN(fenstr);
1890 guint len=0; int flip = appData.flipView;
1893 // get game from clipboard
1894 GdkDisplay *gdisp = gdk_display_get_default();
1895 if (gdisp == NULL) return;
1896 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1897 text = gtk_clipboard_wait_for_text(cb);
1898 if (text == NULL) return; // nothing to paste
1901 // write to temp file
1902 if (text == NULL || len == 0) {
1903 return; //nothing to paste
1905 f = fopen(gamePasteFilename, "w");
1907 DisplayError(_("Can't open temp file"), errno);
1910 fwrite(text, 1, len, f);
1914 if(!appData.autoFlipView) appData.flipView = flipView;
1915 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1916 appData.flipView = flip;
1923 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1929 void MoveTypeInProc(eventkey)
1930 GdkEventKey *eventkey;
1934 // ingnore if ctrl, alt, or meta is pressed
1935 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1939 buf[0]=eventkey->keyval;
1941 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1942 ConsoleAutoPopUp (buf);
1947 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1949 if (!TempBackwardActive) {
1950 TempBackwardActive = True;
1956 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1958 /* Check to see if triggered by a key release event for a repeating key.
1959 * If so the next queued event will be a key press of the same key at the same time */
1960 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1962 XPeekEvent(xDisplay, &next);
1963 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1964 next.xkey.keycode == event->xkey.keycode)
1968 TempBackwardActive = False;
1974 { // called from menu
1977 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
1980 system("xterm -e man xboard &");
1989 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);
1991 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
1999 SetWindowTitle (char *text, char *title, char *icon)
2004 if (appData.titleInWindow) {
2006 XtSetArg(args[i], XtNlabel, text); i++;
2007 XtSetValues(titleWidget, args, i);
2010 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2011 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2012 XtSetValues(shellWidget, args, i);
2013 XSync(xDisplay, False);
2015 if (appData.titleInWindow) {
2016 SetWidgetLabel(titleWidget, text);
2018 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2023 DisplayIcsInteractionTitle (String message)
2026 if (oldICSInteractionTitle == NULL) {
2027 /* Magic to find the old window title, adapted from vim */
2028 char *wina = getenv("WINDOWID");
2030 Window win = (Window) atoi(wina);
2031 Window root, parent, *children;
2032 unsigned int nchildren;
2033 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2035 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2036 if (!XQueryTree(xDisplay, win, &root, &parent,
2037 &children, &nchildren)) break;
2038 if (children) XFree((void *)children);
2039 if (parent == root || parent == 0) break;
2042 XSetErrorHandler(oldHandler);
2044 if (oldICSInteractionTitle == NULL) {
2045 oldICSInteractionTitle = "xterm";
2048 printf("\033]0;%s\007", message);
2055 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2057 GtkWidget *w = (GtkWidget *) opt->handle;
2064 strcpy(bgcolor, "black");
2065 strcpy(fgcolor, "white");
2067 strcpy(bgcolor, "white");
2068 strcpy(fgcolor, "black");
2071 appData.lowTimeWarning &&
2072 (timer / 1000) < appData.icsAlarmTime) {
2073 strcpy(fgcolor, appData.lowTimeWarningColor);
2076 gdk_color_parse( bgcolor, &col );
2077 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2079 if (appData.clockMode) {
2080 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2081 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2082 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2083 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2085 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2086 bgcolor, fgcolor, color);
2087 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2088 // bgcolor, fgcolor, color);
2090 gtk_label_set_markup(GTK_LABEL(w), markup);
2094 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2097 SetClockIcon (int color)
2099 GdkPixbuf *pm = *clockIcons[color];
2100 if (mainwindowIcon != pm) {
2101 mainwindowIcon = pm;
2103 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2105 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2110 #define INPUT_SOURCE_BUF_SIZE 8192
2119 char buf[INPUT_SOURCE_BUF_SIZE];
2124 DoInputCallback(io, cond, data)
2129 /* read input from one of the input source (for example a chess program, ICS, etc).
2130 * and call a function that will handle the input
2137 /* All information (callback function, file descriptor, etc) is
2138 * saved in an InputSource structure
2140 InputSource *is = (InputSource *) data;
2142 if (is->lineByLine) {
2143 count = read(is->fd, is->unused,
2144 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2146 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2147 RemoveInputSource(is); // cease reading stdin
2148 stdoutClosed = TRUE; // suppress future output
2151 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2154 is->unused += count;
2156 /* break input into lines and call the callback function on each
2159 while (p < is->unused) {
2160 q = memchr(p, '\n', is->unused - p);
2161 if (q == NULL) break;
2163 (is->func)(is, is->closure, p, q - p, 0);
2166 /* remember not yet used part of the buffer */
2168 while (p < is->unused) {
2173 /* read maximum length of input buffer and send the whole buffer
2174 * to the callback function
2176 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2181 (is->func)(is, is->closure, is->buf, count, error);
2183 return True; // Must return true or the watch will be removed
2186 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2193 GIOChannel *channel;
2194 ChildProc *cp = (ChildProc *) pr;
2196 is = (InputSource *) calloc(1, sizeof(InputSource));
2197 is->lineByLine = lineByLine;
2201 is->fd = fileno(stdin);
2203 is->kind = cp->kind;
2204 is->fd = cp->fdFrom;
2207 is->unused = is->buf;
2211 /* GTK-TODO: will this work on windows?*/
2213 channel = g_io_channel_unix_new(is->fd);
2214 g_io_channel_set_close_on_unref (channel, TRUE);
2215 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2217 is->closure = closure;
2218 return (InputSourceRef) is;
2223 RemoveInputSource(isr)
2226 InputSource *is = (InputSource *) isr;
2228 if (is->sid == 0) return;
2229 g_source_remove(is->sid);
2236 static Boolean frameWaiting;
2239 FrameAlarm (int sig)
2241 frameWaiting = False;
2242 /* In case System-V style signals. Needed?? */
2243 signal(SIGALRM, FrameAlarm);
2247 FrameDelay (int time)
2249 struct itimerval delay;
2252 frameWaiting = True;
2253 signal(SIGALRM, FrameAlarm);
2254 delay.it_interval.tv_sec =
2255 delay.it_value.tv_sec = time / 1000;
2256 delay.it_interval.tv_usec =
2257 delay.it_value.tv_usec = (time % 1000) * 1000;
2258 setitimer(ITIMER_REAL, &delay, NULL);
2259 while (frameWaiting) pause();
2260 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2261 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2262 setitimer(ITIMER_REAL, &delay, NULL);
2269 FrameDelay (int time)
2272 XSync(xDisplay, False);
2274 // gtk_main_iteration_do(False);
2277 usleep(time * 1000);
2283 FindLogo (char *place, char *name, char *buf)
2284 { // check if file exists in given place
2286 if(!place) return 0;
2287 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2288 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2296 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2298 char buf[MSG_SIZ], *logoName = buf;
2299 if(appData.logo[n][0]) {
2300 logoName = appData.logo[n];
2301 } else if(appData.autoLogo) {
2302 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2303 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2304 } else { // engine; cascade
2305 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2306 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2307 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2308 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2312 { ASSIGN(cps->programLogo, logoName); }
2316 UpdateLogos (int displ)
2318 if(optList[W_WHITE-1].handle == NULL) return;
2319 LoadLogo(&first, 0, 0);
2320 LoadLogo(&second, 1, appData.icsActive);
2321 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2325 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2336 GtkFileFilter *gtkfilter;
2337 GtkFileFilter *gtkfilter_all;
2339 char fileext[10] = "";
2340 char *result = NULL;
2342 char curDir[MSG_SIZ];
2344 StartDir(filter, NULL); // change to start directory for this file type
2346 if(def && *def && def[strlen(def)-1] == '/') {
2347 getcwd(curDir, MSG_SIZ);
2352 /* make a copy of the filter string, so that strtok can work with it*/
2353 cp = strdup(filter);
2355 /* add filters for file extensions */
2356 gtkfilter = gtk_file_filter_new();
2357 gtkfilter_all = gtk_file_filter_new();
2359 /* one filter to show everything */
2360 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2361 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2363 /* add filter if present */
2364 result = strtok(cp, space);
2365 while( result != NULL ) {
2366 snprintf(fileext,10,"*%s",result);
2367 result = strtok( NULL, space );
2368 gtk_file_filter_add_pattern(gtkfilter, fileext);
2371 /* second filter to only show what's useful */
2372 gtk_file_filter_set_name (gtkfilter,filter);
2374 if (openMode[0] == 'r')
2376 dialog = gtk_file_chooser_dialog_new (label,
2378 GTK_FILE_CHOOSER_ACTION_OPEN,
2379 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2380 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2385 dialog = gtk_file_chooser_dialog_new (label,
2387 GTK_FILE_CHOOSER_ACTION_SAVE,
2388 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2389 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2391 /* add filename suggestions */
2392 if (strlen(def) > 0 )
2393 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2395 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2399 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2400 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2401 /* activate filter */
2402 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2404 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2409 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2412 f = fopen(filename, openMode);
2415 DisplayError(_("Failed to open file"), errno);
2419 /* TODO add indec */
2421 ASSIGN(*name, filename);
2422 ScheduleDelayedEvent(DelayedLoad, 50);
2424 StartDir(filter, filename);
2427 else StartDir(filter, "");
2429 gtk_widget_destroy (dialog);
2432 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);