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
179 # undef SETTINGS_FILE
180 # define ICS_LOGON "Library/Preferences/XboardICS.conf"
181 # define LOCALEDIR localeDir
182 # define SETTINGS_FILE masterSettings
183 # define SYNC_MENUBAR gtkosx_application_sync_menubar(theApp)
184 char localeDir[MSG_SIZ];
185 char masterSettings[MSG_SIZ];
189 # define SYNC_MENUBAR
196 #define usleep(t) _sleep2(((t)+500)/1000)
200 # define _(s) gettext (s)
201 # define N_(s) gettext_noop (s)
207 int main P((int argc, char **argv));
208 RETSIGTYPE CmailSigHandler P((int sig));
209 RETSIGTYPE IntSigHandler P((int sig));
210 RETSIGTYPE TermSizeSigHandler P((int sig));
211 char *InsertPxlSize P((char *pattern, int targetPxlSize));
214 XFontSet CreateFontSet P((char *base_fnt_lst));
216 char *FindFont P((char *pattern, int targetPxlSize));
219 void DelayedDrag P((void));
220 void ICSInputBoxPopUp P((void));
221 void MoveTypeInProc P((GdkEventKey *eventkey));
222 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
223 Boolean TempBackwardActive = False;
224 void DisplayMove P((int moveNumber));
225 void update_ics_width P(());
226 int CopyMemoProc P(());
227 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
228 static int FindLogo P((char *place, char *name, char *buf));
232 XFontSet fontSet, clockFontSet;
235 XFontStruct *clockFontStruct;
237 Font coordFontID, countFontID;
238 XFontStruct *coordFontStruct, *countFontStruct;
240 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
241 GtkWidget *mainwindow;
243 Option *optList; // contains all widgets of main window
246 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
249 static GdkPixbuf *mainwindowIcon=NULL;
250 static GdkPixbuf *WhiteIcon=NULL;
251 static GdkPixbuf *BlackIcon=NULL;
253 /* key board accelerators */
254 GtkAccelGroup *GtkAccelerators;
256 typedef unsigned int BoardSize;
258 Boolean chessProgram;
260 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
261 int smallLayout = 0, tinyLayout = 0,
262 marginW, marginH, // [HGM] for run-time resizing
263 fromX = -1, fromY = -1, toX, toY, commentUp = False,
264 errorExitStatus = -1, defaultLineGap;
266 Dimension textHeight;
268 char *chessDir, *programName, *programVersion;
269 Boolean alwaysOnTop = False;
270 char *icsTextMenuString;
272 char *firstChessProgramNames;
273 char *secondChessProgramNames;
275 WindowPlacement wpMain;
276 WindowPlacement wpConsole;
277 WindowPlacement wpComment;
278 WindowPlacement wpMoveHistory;
279 WindowPlacement wpEvalGraph;
280 WindowPlacement wpEngineOutput;
281 WindowPlacement wpGameList;
282 WindowPlacement wpTags;
283 WindowPlacement wpDualBoard;
285 /* This magic number is the number of intermediate frames used
286 in each half of the animation. For short moves it's reduced
287 by 1. The total number of frames will be factor * 2 + 1. */
290 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
297 DropMenuEnables dmEnables[] = {
306 XtResource clientResources[] = {
307 { "flashCount", "flashCount", XtRInt, sizeof(int),
308 XtOffset(AppDataPtr, flashCount), XtRImmediate,
309 (XtPointer) FLASH_COUNT },
313 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
314 char globalTranslations[] =
315 ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
316 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
317 :<KeyDown>Return: TempBackwardProc() \n \
318 :<KeyUp>Return: TempForwardProc() \n";
320 char ICSInputTranslations[] =
321 "<Key>Up: UpKeyProc() \n "
322 "<Key>Down: DownKeyProc() \n "
323 "<Key>Return: EnterKeyProc() \n";
325 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
326 // as the widget is destroyed before the up-click can call extend-end
327 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
330 String xboardResources[] = {
331 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
339 gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
342 //---------------------------------------------------------------------------------------------------------
343 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
346 #define CW_USEDEFAULT (1<<31)
347 #define ICS_TEXT_MENU_SIZE 90
348 #define DEBUG_FILE "xboard.debug"
349 #define SetCurrentDirectory chdir
350 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
354 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
357 // front-end part of option handling
359 // [HGM] This platform-dependent table provides the location for storing the color info
360 extern char *crWhite, * crBlack;
364 &appData.whitePieceColor,
365 &appData.blackPieceColor,
366 &appData.lightSquareColor,
367 &appData.darkSquareColor,
368 &appData.highlightSquareColor,
369 &appData.premoveHighlightColor,
370 &appData.lowTimeWarningColor,
381 // [HGM] font: keep a font for each square size, even non-stndard ones
384 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
385 char *fontTable[NUM_FONTS][MAX_SIZE];
388 ParseFont (char *name, int number)
389 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
391 if(sscanf(name, "size%d:", &size)) {
392 // [HGM] font: font is meant for specific boardSize (likely from settings file);
393 // defer processing it until we know if it matches our board size
394 if(!strstr(name, "-*-") && // ignore X-fonts
395 size >= 0 && size<MAX_SIZE) { // for now, fixed limit
396 fontTable[number][size] = strdup(strchr(name, ':')+1);
397 fontValid[number][size] = True;
402 case 0: // CLOCK_FONT
403 appData.clockFont = strdup(name);
405 case 1: // MESSAGE_FONT
406 appData.font = strdup(name);
408 case 2: // COORD_FONT
409 appData.coordFont = strdup(name);
412 appData.icsFont = strdup(name);
415 appData.tagsFont = strdup(name);
418 appData.commentFont = strdup(name);
420 case MOVEHISTORY_FONT:
421 appData.historyFont = strdup(name);
424 appData.gameListFont = strdup(name);
429 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
434 { // only 2 fonts currently
435 appData.clockFont = strdup(CLOCK_FONT_NAME);
436 appData.coordFont = strdup(COORD_FONT_NAME);
437 appData.font = strdup(DEFAULT_FONT_NAME);
438 appData.icsFont = strdup(CONSOLE_FONT_NAME);
439 appData.tagsFont = strdup(TAGS_FONT_NAME);
440 appData.commentFont = strdup(COMMENT_FONT_NAME);
441 appData.historyFont = strdup(HISTORY_FONT_NAME);
442 appData.gameListFont = strdup(GAMELIST_FONT_NAME);
447 { // no-op, until we identify the code for this already in XBoard and move it here
451 ParseColor (int n, char *name)
452 { // in XBoard, just copy the color-name string
453 if(colorVariable[n] && *name == '#') *(char**)colorVariable[n] = strdup(name);
459 return *(char**)colorVariable[n];
463 ParseTextAttribs (ColorClass cc, char *s)
465 (&appData.colorShout)[cc] = strdup(s);
469 ParseBoardSize (void *addr, char *name)
471 appData.boardSize = strdup(name);
476 { // In XBoard the sound-playing program takes care of obtaining the actual sound
480 SetCommPortDefaults ()
481 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
484 // [HGM] args: these three cases taken out to stay in front-end
486 SaveFontArg (FILE *f, ArgDescriptor *ad)
489 int i, n = (int)(intptr_t)ad->argLoc;
491 case 0: // CLOCK_FONT
492 name = appData.clockFont;
494 case 1: // MESSAGE_FONT
497 case 2: // COORD_FONT
498 name = appData.coordFont;
501 name = appData.icsFont;
504 name = appData.tagsFont;
507 name = appData.commentFont;
509 case MOVEHISTORY_FONT:
510 name = appData.historyFont;
513 name = appData.gameListFont;
518 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
519 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
520 fontTable[n][squareSize] = strdup(name);
521 fontValid[n][squareSize] = True;
524 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
525 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
530 { // nothing to do, as the sounds are at all times represented by their text-string names already
534 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
535 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
536 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
540 SaveColor (FILE *f, ArgDescriptor *ad)
541 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
542 if(colorVariable[(int)(intptr_t)ad->argLoc])
543 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
547 SaveBoardSize (FILE *f, char *name, void *addr)
548 { // wrapper to shield back-end from BoardSize & sizeInfo
549 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
553 ParseCommPortSettings (char *s)
554 { // no such option in XBoard (yet)
560 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
564 gtk_widget_get_allocation(shell, &a);
565 gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
569 wp->height = a.height;
570 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
571 frameX = 3; frameY = 3; // remember to decide if windows touch
575 GetPlacement (DialogClass dlg, WindowPlacement *wp)
576 { // wrapper to shield back-end from widget type
577 if(shellUp[dlg]) GetActualPlacement(shells[dlg], wp);
582 { // wrapper to shield use of window handles from back-end (make addressible by number?)
583 // In XBoard this will have to wait until awareness of window parameters is implemented
584 GetActualPlacement(shellWidget, &wpMain);
585 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
586 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
587 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
588 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
589 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
590 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
591 GetPlacement(ChatDlg, &wpConsole); if(appData.icsActive) wpConsole.visible = shellUp[ChatDlg];
595 PrintCommPortSettings (FILE *f, char *name)
596 { // This option does not exist in XBoard
600 EnsureOnScreen (int *x, int *y, int minX, int minY)
607 { // [HGM] args: allows testing if main window is realized from back-end
608 return DialogExists(BoardWindow);
612 PopUpStartupDialog ()
613 { // start menu not implemented in XBoard
617 ConvertToLine (int argc, char **argv)
619 static char line[128*1024], buf[1024];
623 for(i=1; i<argc; i++)
625 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
626 && argv[i][0] != '{' )
627 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
629 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
630 strncat(line, buf, 128*1024 - strlen(line) - 1 );
633 line[strlen(line)-1] = NULLCHAR;
637 //--------------------------------------------------------------------------------------------
642 ResizeBoardWindow (int w, int h, int inhibit)
646 // if(clockKludge) return; // ignore as long as clock does not have final height
647 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
649 gtk_widget_get_allocation(shellWidget, &a);
650 marginW = a.width - bw;
651 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
652 gtk_widget_set_size_request(optList[W_BOARD].handle, w, h); // protect board widget
653 // w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
654 // h += marginH + a.height + 1;
655 gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
657 gtk_widget_set_size_request(optList[W_BOARD].handle, -1, -1); // liberate board again
662 { // dummy, as the GTK code does not make colors in advance
667 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
668 { // determine what fonts to use, and create them
670 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
671 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
672 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
673 appData.font = fontTable[MESSAGE_FONT][squareSize];
674 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
675 appData.coordFont = fontTable[COORD_FONT][squareSize];
676 if(!fontIsSet[CONSOLE_FONT] && fontValid[CONSOLE_FONT][squareSize])
677 appData.icsFont = fontTable[CONSOLE_FONT][squareSize];
678 if(!fontIsSet[EDITTAGS_FONT] && fontValid[EDITTAGS_FONT][squareSize])
679 appData.tagsFont = fontTable[EDITTAGS_FONT][squareSize];
680 if(!fontIsSet[COMMENT_FONT] && fontValid[COMMENT_FONT][squareSize])
681 appData.commentFont = fontTable[COMMENT_FONT][squareSize];
682 if(!fontIsSet[MOVEHISTORY_FONT] && fontValid[MOVEHISTORY_FONT][squareSize])
683 appData.historyFont = fontTable[MOVEHISTORY_FONT][squareSize];
684 if(!fontIsSet[GAMELIST_FONT] && fontValid[GAMELIST_FONT][squareSize])
685 appData.gameListFont = fontTable[GAMELIST_FONT][squareSize];
687 appData.font = InsertPxlSize(appData.font, coordFontPxlSize);
688 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
689 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
690 appData.icsFont = InsertPxlSize(appData.icsFont, coordFontPxlSize);
691 appData.tagsFont = InsertPxlSize(appData.tagsFont, coordFontPxlSize);
692 appData.commentFont = InsertPxlSize(appData.commentFont, coordFontPxlSize);
693 appData.historyFont = InsertPxlSize(appData.historyFont, coordFontPxlSize);
694 appData.gameListFont = InsertPxlSize(appData.gameListFont, coordFontPxlSize);
700 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
701 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
702 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
703 appData.font = fontTable[MESSAGE_FONT][squareSize];
704 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
705 appData.coordFont = fontTable[COORD_FONT][squareSize];
708 appData.font = InsertPxlSize(appData.font, fontPxlSize);
709 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
710 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
711 fontSet = CreateFontSet(appData.font);
712 clockFontSet = CreateFontSet(appData.clockFont);
714 /* For the coordFont, use the 0th font of the fontset. */
715 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
716 XFontStruct **font_struct_list;
717 XFontSetExtents *fontSize;
718 char **font_name_list;
719 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
720 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
721 coordFontStruct = XQueryFont(xDisplay, coordFontID);
722 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
723 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
726 appData.font = FindFont(appData.font, fontPxlSize);
727 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
728 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
729 clockFontID = XLoadFont(xDisplay, appData.clockFont);
730 clockFontStruct = XQueryFont(xDisplay, clockFontID);
731 coordFontID = XLoadFont(xDisplay, appData.coordFont);
732 coordFontStruct = XQueryFont(xDisplay, coordFontID);
733 // textHeight in !NLS mode!
735 countFontID = coordFontID; // [HGM] holdings
736 countFontStruct = coordFontStruct;
738 xdb = XtDatabase(xDisplay);
740 XrmPutLineResource(&xdb, "*international: True");
741 vTo.size = sizeof(XFontSet);
742 vTo.addr = (XtPointer) &fontSet;
743 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
745 XrmPutStringResource(&xdb, "*font", appData.font);
756 case ArgInt: p = " N"; break;
757 case ArgString: p = " STR"; break;
758 case ArgBoolean: p = " TF"; break;
759 case ArgSettingsFilename:
760 case ArgBackupSettingsFile:
761 case ArgFilename: p = " FILE"; break;
762 case ArgX: p = " Nx"; break;
763 case ArgY: p = " Ny"; break;
764 case ArgAttribs: p = " TEXTCOL"; break;
765 case ArgColor: p = " COL"; break;
766 case ArgFont: p = " FONT"; break;
767 case ArgBoardSize: p = " SIZE"; break;
768 case ArgFloat: p = " FLOAT"; break;
773 case ArgCommSettings:
785 ArgDescriptor *q, *p = argDescriptors+5;
786 printf("\nXBoard accepts the following options:\n"
787 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
788 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
789 " SIZE = board-size spec(s)\n"
790 " Within parentheses are short forms, or options to set to true or false.\n"
791 " Persistent options (saved in the settings file) are marked with *)\n\n");
793 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
794 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
795 if(p->save) strcat(buf+len, "*");
796 for(q=p+1; q->argLoc == p->argLoc; q++) {
797 if(q->argName[0] == '-') continue;
798 strcat(buf+len, q == p+1 ? " (" : " ");
799 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
801 if(q != p+1) strcat(buf+len, ")");
803 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
806 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
810 SlaveResize (Option *opt)
812 static int slaveW, slaveH, w, h;
815 gtk_widget_get_allocation(shells[DummyDlg], &a);
816 w = a.width; h = a.height;
817 gtk_widget_get_allocation(opt->handle, &a);
818 slaveW = w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
819 slaveH = h - a.height + 13;
821 gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
825 LoadIconFile (gchar *svgFilename)
829 snprintf(buf, MSG_SIZ, "%s/%s" IMG, svgDir, svgFilename);
830 return gdk_pixbuf_new_from_file(buf, NULL);
834 static char clickedFile[MSG_SIZ];
838 StartNewXBoard(GtkosxApplication *app, gchar *path, gpointer user_data)
839 { // handler of OSX OpenFile signal, which sends us the filename of clicked file or first argument
842 if(1000*now.sec + now.ms - 1000*started.sec - started.ms < 1000) { // received during first second
843 strncpy(clickedFile, path, MSG_SIZ); // remember file name, but otherwise ignore
844 } else { // we are running something presumably useful
846 snprintf(buf, MSG_SIZ, "open -n -a \"xboard\" --args \"%s\"", path);
847 system(buf); // start new instance on this file
852 GtkosxApplication *theApp;
856 main (int argc, char **argv)
858 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
859 int boardWidth, w, h; //, boardHeight;
861 int forceMono = False;
863 srandom(time(0)); // [HGM] book: make random truly random
865 setbuf(stdout, NULL);
866 setbuf(stderr, NULL);
869 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
870 printf("%s version %s\n\n configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
874 if(argc > 1 && !strcmp(argv[1], "--help" )) {
880 gtk_init (&argc, &argv);
882 { // prepare to catch OX OpenFile signal, which will tell us the clicked file
883 char *path = gtkosx_application_get_bundle_path();
885 char *res_path = gtkosx_application_get_resource_path();
886 snprintf(localeDir, MSG_SIZ, "%s/share/locale", res_path); // redefine locale dir for OSX bundle
888 GetTimeMark(&started); // remember start time
889 theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
890 snprintf(masterSettings, MSG_SIZ, "%s/Contents/Resources/etc/xboard.conf", path);
891 snprintf(dataDir, MSG_SIZ, "%s/Contents/Resources/share/xboard", path);
892 snprintf(manDir, MSG_SIZ, "%s/Contents/Resources/share/man", path);
893 snprintf(svgDir, MSG_SIZ, "%s/themes/default", dataDir);
894 g_signal_connect(theApp, "NSApplicationOpenFile", G_CALLBACK(StartNewXBoard), NULL);
895 g_signal_connect(theApp, "NSApplicationWillTerminate", G_CALLBACK(ExitEvent), NULL);
896 // we must call application ready before we can get the signal,
897 // and supply a (dummy) menu bar before that, to avoid problems with dual apples in it
898 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(gtk_menu_bar_new()));
899 gtkosx_application_ready(theApp);
900 if(argc == 1) { // called without args: OSX open-file signal might follow
901 static char *fakeArgv[3] = {NULL, clickedFile, NULL};
902 usleep(10000); // wait 10 msec (and hope this is long enough).
903 while(gtk_events_pending())
904 gtk_main_iteration(); // process all events that came in upto now
905 if(clickedFile[0]) { // we were sent an open-file signal with filename!
906 fakeArgv[0] = argv[0];
907 argc = 2; argv = fakeArgv; // fake that we were called as "xboard filename"
913 if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
914 typedef struct {char *name, *value; } Config;
915 static Config configList[] = {
916 { "Datadir", dataDir },
917 { "Mandir", manDir },
918 { "Sysconfdir", SYSCONFDIR },
923 for(i=0; configList[i].name; i++) {
924 if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
925 if(argc > 2) printf("%s", configList[i].value);
926 else printf("%-12s: %s\n", configList[i].name, configList[i].value);
931 /* set up keyboard accelerators group */
932 GtkAccelerators = gtk_accel_group_new();
934 programName = strrchr(argv[0], '/');
935 if (programName == NULL)
936 programName = argv[0];
941 // if (appData.debugMode) {
942 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
945 bindtextdomain(PACKAGE, LOCALEDIR);
946 bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
950 appData.boardSize = "";
951 InitAppData(ConvertToLine(argc, argv));
953 if (p == NULL) p = "/tmp";
954 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
955 gameCopyFilename = (char*) malloc(i);
956 gamePasteFilename = (char*) malloc(i);
957 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
958 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
960 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
961 static char buf[MSG_SIZ];
962 snprintf(buf, MSG_SIZ, appData.sysOpen, dataDir);
963 ASSIGN(appData.sysOpen, buf); // expand %s in -openCommand to DATADIR (usefull for OS X configuring)
964 EscapeExpand(buf, appData.firstInitString);
965 appData.firstInitString = strdup(buf);
966 EscapeExpand(buf, appData.secondInitString);
967 appData.secondInitString = strdup(buf);
968 EscapeExpand(buf, appData.firstComputerString);
969 appData.firstComputerString = strdup(buf);
970 EscapeExpand(buf, appData.secondComputerString);
971 appData.secondComputerString = strdup(buf);
974 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
977 if (chdir(chessDir) != 0) {
978 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
984 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
985 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
986 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
987 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
990 setbuf(debugFP, NULL);
994 if (appData.debugMode) {
995 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
999 /* [HGM,HR] make sure board size is acceptable */
1000 if(appData.NrFiles > BOARD_FILES ||
1001 appData.NrRanks > BOARD_RANKS )
1002 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1005 /* This feature does not work; animation needs a rewrite */
1006 appData.highlightDragging = FALSE;
1010 gameInfo.variant = StringToVariant(appData.variant);
1011 InitPosition(FALSE);
1014 * determine size, based on supplied or remembered -size, or screen size
1016 if (isdigit(appData.boardSize[0])) {
1017 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1018 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1019 &fontPxlSize, &smallLayout, &tinyLayout);
1021 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1022 programName, appData.boardSize);
1026 /* Find some defaults; use the nearest known size */
1027 SizeDefaults *szd, *nearest;
1028 int distance = 99999;
1029 nearest = szd = sizeDefaults;
1030 while (szd->name != NULL) {
1031 if (abs(szd->squareSize - squareSize) < distance) {
1033 distance = abs(szd->squareSize - squareSize);
1034 if (distance == 0) break;
1038 if (i < 2) lineGap = nearest->lineGap;
1039 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1040 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1041 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1042 if (i < 6) smallLayout = nearest->smallLayout;
1043 if (i < 7) tinyLayout = nearest->tinyLayout;
1046 SizeDefaults *szd = sizeDefaults;
1047 if (*appData.boardSize == NULLCHAR) {
1048 // GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow)); // TODO: this does not work, as no mainwindow yet
1049 GdkScreen *screen = gdk_screen_get_default();
1050 guint screenwidth = gdk_screen_get_width(screen);
1051 guint screenheight = gdk_screen_get_height(screen);
1052 while (screenwidth < (szd->minScreenSize*BOARD_WIDTH + 4)/8 ||
1053 screenheight < (szd->minScreenSize*BOARD_HEIGHT + 4)/8) {
1056 if (szd->name == NULL) szd--;
1057 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1059 while (szd->name != NULL &&
1060 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1061 if (szd->name == NULL) {
1062 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1063 programName, appData.boardSize);
1067 squareSize = szd->squareSize;
1068 lineGap = szd->lineGap;
1069 clockFontPxlSize = szd->clockFontPxlSize;
1070 coordFontPxlSize = szd->coordFontPxlSize;
1071 fontPxlSize = szd->fontPxlSize;
1072 smallLayout = szd->smallLayout;
1073 tinyLayout = szd->tinyLayout;
1074 // [HGM] font: use defaults from settings file if available and not overruled
1076 if(BOARD_WIDTH != 8) {
1077 squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
1078 lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
1081 defaultLineGap = lineGap;
1082 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1084 /* [HR] height treated separately (hacked) */
1085 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1086 // boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1089 * Determine what fonts to use.
1091 InitializeFonts((2*clockFontPxlSize+1)/3, coordFontPxlSize, fontPxlSize);
1094 * Detect if there are not enough colors available and adapt.
1097 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1098 appData.monoMode = True;
1102 forceMono = MakeColors();
1105 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1107 appData.monoMode = True;
1110 ParseIcsTextColors();
1116 layoutName = "tinyLayout";
1117 } else if (smallLayout) {
1118 layoutName = "smallLayout";
1120 layoutName = "normalLayout";
1123 if(appData.logoSize) appData.logoSize = boardWidth/4-3;
1124 wpMain.width = -1; // prevent popup sizes window
1125 optList = BoardPopUp(squareSize, lineGap, (void*)
1135 InitDrawingHandle(optList + W_BOARD);
1136 shellWidget = shells[BoardWindow];
1137 currBoard = &optList[W_BOARD];
1138 boardWidget = optList[W_BOARD].handle;
1139 menuBarWidget = optList[W_MENU].handle;
1140 dropMenu = optList[W_DROP].handle;
1141 titleWidget = optList[optList[W_TITLE].type != Skip ? W_TITLE : W_SMALL].handle;
1143 formWidget = XtParent(boardWidget);
1144 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1145 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1146 XtGetValues(optList[W_WHITE].handle, args, 2);
1147 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1148 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1149 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1150 XtGetValues(optList[W_PAUSE].handle, args, 2);
1154 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1155 // not need to go into InitDrawingSizes().
1159 // add accelerators to main shell
1160 gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
1163 * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1165 WhiteIcon = LoadIconFile("icon_white");
1166 BlackIcon = LoadIconFile("icon_black");
1167 SetClockIcon(0); // sets white icon
1171 * Create a cursor for the board widget.
1174 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1175 XChangeWindowAttributes(xDisplay, xBoardWindow,
1176 CWCursor, &window_attributes);
1180 * Inhibit shell resizing.
1183 shellArgs[0].value = (XtArgVal) &w;
1184 shellArgs[1].value = (XtArgVal) &h;
1185 XtGetValues(shellWidget, shellArgs, 2);
1186 shellArgs[4].value = shellArgs[2].value = w;
1187 shellArgs[5].value = shellArgs[3].value = h;
1188 // XtSetValues(shellWidget, &shellArgs[2], 4);
1191 // Note: We cannot do sensible sizing here, because the height of the clock widget is not yet known
1192 // It wil only become known asynchronously, when we first write a string into it.
1193 // This will then change the clock widget height, which triggers resizing the top-level window
1194 // and a configure event. Only then can we know the total height of the top-level window,
1195 // and calculate the height we need. The clockKludge flag suppresses all resizing until
1196 // that moment comes, after which the configure event-handler handles it through a (delayed) DragProg.
1199 gtk_widget_get_allocation(shells[BoardWindow], &a);
1200 w = a.width; h = a.height;
1201 gtk_widget_get_allocation(optList[W_WHITE].handle, &a);
1202 clockKludge = hc = a.height;
1203 gtk_widget_get_allocation(boardWidget, &a);
1204 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1205 marginH = h - a.height - hc; // subtract current clock height, so it can be added back dynamically
1211 if(appData.logoSize)
1212 { // locate and read user logo
1213 char buf[MSG_SIZ], name[MSG_SIZ];
1214 snprintf(name, MSG_SIZ, "/home/%s", UserName());
1215 if(!FindLogo(name, ".logo", buf))
1216 FindLogo(appData.logoDir, name + 6, buf);
1217 ASSIGN(userLogo, buf);
1220 if (appData.animate || appData.animateDragging)
1223 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1224 g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1226 /* [AS] Restore layout */
1227 if( wpMoveHistory.visible ) {
1231 if( wpEvalGraph.visible )
1236 if( wpEngineOutput.visible ) {
1237 EngineOutputPopUp();
1240 if( wpConsole.visible && appData.icsActive ) {
1245 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1250 if (errorExitStatus == -1) {
1251 if (appData.icsActive) {
1252 /* We now wait until we see "login:" from the ICS before
1253 sending the logon script (problems with timestamp otherwise) */
1254 /*ICSInitScript();*/
1255 if (appData.icsInputBox) ICSInputBoxPopUp();
1259 signal(SIGWINCH, TermSizeSigHandler);
1261 signal(SIGINT, IntSigHandler);
1262 signal(SIGTERM, IntSigHandler);
1263 if (*appData.cmailGameName != NULLCHAR) {
1264 signal(SIGUSR1, CmailSigHandler);
1269 // XtSetKeyboardFocus(shellWidget, formWidget);
1271 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1274 /* check for GTK events and process them */
1277 gtk_main_iteration();
1280 if (appData.debugMode) fclose(debugFP); // [DM] debug
1287 while(gtk_events_pending()) gtk_main_iteration();
1291 TermSizeSigHandler (int sig)
1297 IntSigHandler (int sig)
1303 CmailSigHandler (int sig)
1308 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1310 /* Activate call-back function CmailSigHandlerCallBack() */
1311 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1313 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1317 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1320 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1322 /**** end signal code ****/
1325 #define Abs(n) ((n)<0 ? -(n) : (n))
1328 InsertPxlSize (char *pattern, int targetPxlSize)
1331 snprintf(buf, MSG_SIZ, pattern, targetPxlSize); // pattern is something like "Sans Bold %d"
1338 InsertPxlSize (char *pattern, int targetPxlSize)
1340 char *base_fnt_lst, strInt[12], *p, *q;
1341 int alternatives, i, len, strIntLen;
1344 * Replace the "*" (if present) in the pixel-size slot of each
1345 * alternative with the targetPxlSize.
1349 while ((p = strchr(p, ',')) != NULL) {
1353 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1354 strIntLen = strlen(strInt);
1355 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1359 while (alternatives--) {
1360 char *comma = strchr(p, ',');
1361 for (i=0; i<14; i++) {
1362 char *hyphen = strchr(p, '-');
1364 if (comma && hyphen > comma) break;
1365 len = hyphen + 1 - p;
1366 if (i == 7 && *p == '*' && len == 2) {
1368 memcpy(q, strInt, strIntLen);
1378 len = comma + 1 - p;
1385 return base_fnt_lst;
1391 CreateFontSet (char *base_fnt_lst)
1394 char **missing_list;
1398 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1399 &missing_list, &missing_count, &def_string);
1400 if (appData.debugMode) {
1402 XFontStruct **font_struct_list;
1403 char **font_name_list;
1404 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1406 fprintf(debugFP, " got list %s, locale %s\n",
1407 XBaseFontNameListOfFontSet(fntSet),
1408 XLocaleOfFontSet(fntSet));
1409 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1410 for (i = 0; i < count; i++) {
1411 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1414 for (i = 0; i < missing_count; i++) {
1415 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1418 if (fntSet == NULL) {
1419 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1425 #else // not ENABLE_NLS
1427 * Find a font that matches "pattern" that is as close as
1428 * possible to the targetPxlSize. Prefer fonts that are k
1429 * pixels smaller to fonts that are k pixels larger. The
1430 * pattern must be in the X Consortium standard format,
1431 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1432 * The return value should be freed with XtFree when no
1437 FindFont (char *pattern, int targetPxlSize)
1439 char **fonts, *p, *best, *scalable, *scalableTail;
1440 int i, j, nfonts, minerr, err, pxlSize;
1442 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1444 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1445 programName, pattern);
1452 for (i=0; i<nfonts; i++) {
1455 if (*p != '-') continue;
1457 if (*p == NULLCHAR) break;
1458 if (*p++ == '-') j++;
1460 if (j < 7) continue;
1463 scalable = fonts[i];
1466 err = pxlSize - targetPxlSize;
1467 if (Abs(err) < Abs(minerr) ||
1468 (minerr > 0 && err < 0 && -err == minerr)) {
1474 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1475 /* If the error is too big and there is a scalable font,
1476 use the scalable font. */
1477 int headlen = scalableTail - scalable;
1478 p = (char *) XtMalloc(strlen(scalable) + 10);
1479 while (isdigit(*scalableTail)) scalableTail++;
1480 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1482 p = (char *) XtMalloc(strlen(best) + 2);
1483 safeStrCpy(p, best, strlen(best)+1 );
1485 if (appData.debugMode) {
1486 fprintf(debugFP, "resolved %s at pixel size %d\n to %s\n",
1487 pattern, targetPxlSize, p);
1489 XFreeFontNames(fonts);
1496 MarkMenuItem (char *menuRef, int state)
1498 MenuItem *item = MenuNameToItem(menuRef);
1500 if(item && item->handle) {
1501 ((GtkCheckMenuItem *) (item->handle))->active = state;
1507 EnableNamedMenuItem (char *menuRef, int state)
1509 MenuItem *item = MenuNameToItem(menuRef);
1511 if(item && item->handle) gtk_widget_set_sensitive(item->handle, state);
1516 EnableButtonBar (int state)
1519 XtSetSensitive(optList[W_BUTTON].handle, state);
1525 SetMenuEnables (Enables *enab)
1527 while (enab->name != NULL) {
1528 EnableNamedMenuItem(enab->name, enab->value);
1533 gboolean KeyPressProc(window, eventkey, data)
1535 GdkEventKey *eventkey;
1539 MoveTypeInProc(eventkey); // pop up for typed in moves
1542 /* check for other key values */
1543 switch(eventkey->keyval) {
1555 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1556 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1558 if(*nprms == 0) return;
1559 item = MenuNameToItem(prms[0]);
1560 if(item) ((MenuProc *) item->proc) ();
1574 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1575 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1576 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1577 dmEnables[i].piece);
1578 XtSetSensitive(entry, p != NULL || !appData.testLegality
1579 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1580 && !appData.icsActive));
1582 while (p && *p++ == dmEnables[i].piece) count++;
1583 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1585 XtSetArg(args[j], XtNlabel, label); j++;
1586 XtSetValues(entry, args, j);
1592 do_flash_delay (unsigned long msec)
1598 FlashDelay (int flash_delay)
1600 if(flash_delay) do_flash_delay(flash_delay);
1604 Fraction (int x, int start, int stop)
1606 double f = ((double) x - start)/(stop - start);
1607 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1611 static WindowPlacement wpNew;
1614 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1616 int touch=0, fudge = 4, f = 3;
1617 GetActualPlacement(sh, wp);
1618 if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x) < fudge) touch = 1; else // right touch
1619 if(abs(wp->x + wp->width + 2*frameX - f - wpMain.x) < fudge) touch = 2; else // left touch
1620 if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1621 if(abs(wp->y + wp->height + frameX + frameY - f - wpMain.y) < fudge) touch = 4; // top touch
1622 //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);
1623 if(!touch ) return; // only windows that touch co-move
1624 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1625 int heightInc = wpNew.height - wpMain.height;
1626 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1627 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1628 wp->y += fracTop * heightInc;
1629 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1631 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1633 wp->height += heightInc;
1634 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1635 int widthInc = wpNew.width - wpMain.width;
1636 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1637 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1638 wp->y += fracLeft * widthInc;
1639 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1641 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1643 wp->width += widthInc;
1645 wp->x += wpNew.x - wpMain.x;
1646 wp->y += wpNew.y - wpMain.y;
1647 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1648 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1650 XtSetArg(args[j], XtNx, wp->x); j++;
1651 XtSetArg(args[j], XtNy, wp->y); j++;
1652 XtSetValues(sh, args, j);
1654 gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1655 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1656 gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1660 ReSize (WindowPlacement *wp)
1663 int sqx, sqy, w, h, lg = lineGap;
1664 static int first = 1;
1665 if(wp->width == wpMain.width && wp->height == wpMain.height && !first) return; // not sized
1666 gtk_widget_get_allocation(optList[W_DROP+1].handle, &a); // table that should contain everything
1667 w = a.width; h = a.height;
1668 gtk_widget_get_allocation(shellWidget, &a);
1669 if(a.width < w || a.height < h) { // outer window smaller than dialog content?
1670 w = a.width - w; h = a.height - h; // subtract matrgins, measured as table minus board dimensions
1671 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1672 w += a.width; h += a.height;
1674 gtk_widget_get_allocation(optList[W_BOARD].handle, &a);
1675 w = a.width; h = a.height;
1677 sqx = (w - lg) / BOARD_WIDTH - lg;
1678 sqy = (h - lg) / BOARD_HEIGHT - lg;
1679 if(sqy < sqx) sqx = sqy;
1680 if(sqx < 20) return;
1681 if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1683 lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1684 sqx = (w - lg) / BOARD_WIDTH - lg;
1685 sqy = (h - lg) / BOARD_HEIGHT - lg;
1686 if(sqy < sqx) sqx = sqy;
1687 lg = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1688 if(sqx == oldSqx + 1 && lg == lineGap + 1) sqx = oldSqx, squareSize = 0; // prevent oscillations, force resize by kludge
1690 if(sqx != squareSize && !first) {
1691 squareSize = sqx; // adopt new square size
1692 CreatePNGPieces(); // make newly scaled pieces
1693 InitDrawingSizes(0, 0); // creates grid etc.
1694 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1695 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1696 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1697 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1698 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1699 first = appData.fixedSize;
1700 if(twoBoards && shellUp[DummyDlg]) {
1701 SlavePopUp(); dualOptions[3].max = 0; DoEvents(); // calls SlaveResize, kludge to force assigning new canvas
1702 partnerUp = !partnerUp; flipView = !flipView;
1703 DrawPosition(True, NULL);
1704 partnerUp = !partnerUp; flipView = !flipView;
1708 static guint delayedDragTag = 0;
1717 GetActualPlacement(shellWidget, &wpNew);
1718 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1719 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1720 busy = 0; return; // false alarm
1723 if(appData.useStickyWindows) {
1724 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1725 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1726 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1727 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1728 if(shellUp[ChatDlg]) CoDrag(shells[ChatDlg], &wpConsole);
1731 DrawPosition(True, NULL);
1732 if(delayedDragTag) g_source_remove(delayedDragTag);
1733 delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1740 //printf("old timr = %d\n", delayedDragTag);
1741 if(delayedDragTag) g_source_remove(delayedDragTag);
1742 delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1743 //printf("new timr = %d\n", delayedDragTag);
1747 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1749 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1751 wpNew.x = event->configure.x;
1752 wpNew.y = event->configure.y;
1753 wpNew.width = event->configure.width;
1754 wpNew.height = event->configure.height;
1755 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1761 /* Disable all user input other than deleting the window */
1762 static int frozen = 0;
1768 /* Grab by a widget that doesn't accept input */
1769 gtk_grab_add(optList[W_MESSG].handle);
1773 /* Undo a FreezeUI */
1777 if (!frozen) return;
1778 gtk_grab_remove(optList[W_MESSG].handle);
1785 static int oldPausing = FALSE;
1786 static GameMode oldMode = (GameMode) -1;
1788 if (!boardWidget) return;
1790 if (pausing != oldPausing) {
1791 oldPausing = pausing;
1792 MarkMenuItem("Mode.Pause", pausing);
1794 if (appData.showButtonBar) {
1795 /* Always toggle, don't set. Previous code messes up when
1796 invoked while the button is pressed, as releasing it
1797 toggles the state again. */
1799 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1800 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1804 wname = ModeToWidgetName(oldMode);
1805 if (wname != NULL) {
1806 MarkMenuItem(wname, False);
1808 wname = ModeToWidgetName(gameMode);
1809 if (wname != NULL) {
1810 MarkMenuItem(wname, True);
1812 if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
1813 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1816 /* Maybe all the enables should be handled here, not just this one */
1817 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1819 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1824 * Button/menu procedures
1827 void CopyFileToClipboard(gchar *filename)
1829 gchar *selection_tmp;
1833 FILE* f = fopen(filename, "r");
1836 if (f == NULL) return;
1840 selection_tmp = g_try_malloc(len + 1);
1841 if (selection_tmp == NULL) {
1842 printf("Malloc failed in CopyFileToClipboard\n");
1845 count = fread(selection_tmp, 1, len, f);
1848 g_free(selection_tmp);
1851 selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1853 // copy selection_tmp to clipboard
1854 GdkDisplay *gdisp = gdk_display_get_default();
1856 g_free(selection_tmp);
1859 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1860 gtk_clipboard_set_text(cb, selection_tmp, -1);
1861 g_free(selection_tmp);
1865 CopySomething (char *src)
1867 GdkDisplay *gdisp = gdk_display_get_default();
1869 if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1870 if (gdisp == NULL) return;
1871 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1872 gtk_clipboard_set_text(cb, src, -1);
1876 PastePositionProc ()
1878 GdkDisplay *gdisp = gdk_display_get_default();
1882 if (gdisp == NULL) return;
1883 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1884 fenstr = gtk_clipboard_wait_for_text(cb);
1885 if (fenstr==NULL) return; // nothing had been selected to copy
1886 EditPositionPasteFEN(fenstr);
1895 guint len=0; int flip = appData.flipView;
1898 // get game from clipboard
1899 GdkDisplay *gdisp = gdk_display_get_default();
1900 if (gdisp == NULL) return;
1901 cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1902 text = gtk_clipboard_wait_for_text(cb);
1903 if (text == NULL) return; // nothing to paste
1906 // write to temp file
1907 if (text == NULL || len == 0) {
1908 return; //nothing to paste
1910 f = fopen(gamePasteFilename, "w");
1912 DisplayError(_("Can't open temp file"), errno);
1915 fwrite(text, 1, len, f);
1919 if(!appData.autoFlipView) appData.flipView = flipView;
1920 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1921 appData.flipView = flip;
1928 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1934 void MoveTypeInProc(eventkey)
1935 GdkEventKey *eventkey;
1939 // ingnore if ctrl, alt, or meta is pressed
1940 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_META_MASK)) {
1944 buf[0]=eventkey->keyval;
1946 if (eventkey->keyval > 32 && eventkey->keyval < 256 || *buf == 27)
1947 ConsoleAutoPopUp (buf);
1952 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1954 if (!TempBackwardActive) {
1955 TempBackwardActive = True;
1961 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1963 /* Check to see if triggered by a key release event for a repeating key.
1964 * If so the next queued event will be a key press of the same key at the same time */
1965 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1967 XPeekEvent(xDisplay, &next);
1968 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1969 next.xkey.keycode == event->xkey.keycode)
1973 TempBackwardActive = False;
1979 { // called from menu
1982 snprintf(buf, MSG_SIZ, "osascript -e 'tell application \"Terminal\"' -e 'activate' -e 'do script \"man %s/../man/man6/xboard.6\"' -e 'end tell'", dataDir);
1985 system("xterm -e man xboard &");
1994 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);
1996 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
2004 SetWindowTitle (char *text, char *title, char *icon)
2009 if (appData.titleInWindow) {
2011 XtSetArg(args[i], XtNlabel, text); i++;
2012 XtSetValues(titleWidget, args, i);
2015 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2016 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2017 XtSetValues(shellWidget, args, i);
2018 XSync(xDisplay, False);
2020 if (appData.titleInWindow) {
2021 SetWidgetLabel(titleWidget, text);
2023 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2028 DisplayIcsInteractionTitle (String message)
2031 if (oldICSInteractionTitle == NULL) {
2032 /* Magic to find the old window title, adapted from vim */
2033 char *wina = getenv("WINDOWID");
2035 Window win = (Window) atoi(wina);
2036 Window root, parent, *children;
2037 unsigned int nchildren;
2038 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2040 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2041 if (!XQueryTree(xDisplay, win, &root, &parent,
2042 &children, &nchildren)) break;
2043 if (children) XFree((void *)children);
2044 if (parent == root || parent == 0) break;
2047 XSetErrorHandler(oldHandler);
2049 if (oldICSInteractionTitle == NULL) {
2050 oldICSInteractionTitle = "xterm";
2053 printf("\033]0;%s\007", message);
2060 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2062 GtkWidget *w = (GtkWidget *) opt->handle;
2069 strcpy(bgcolor, "black");
2070 strcpy(fgcolor, "white");
2072 strcpy(bgcolor, "white");
2073 strcpy(fgcolor, "black");
2076 appData.lowTimeWarning &&
2077 (timer / 1000) < appData.icsAlarmTime) {
2078 strcpy(fgcolor, appData.lowTimeWarningColor);
2081 gdk_color_parse( bgcolor, &col );
2082 gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
2084 if (appData.clockMode) {
2085 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>", appData.clockFont,
2086 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2087 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2088 // bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2090 markup = g_markup_printf_escaped("<span font=\"%s\" background=\"%s\" foreground=\"%s\">%s </span>", appData.clockFont,
2091 bgcolor, fgcolor, color);
2092 // markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2093 // bgcolor, fgcolor, color);
2095 gtk_label_set_markup(GTK_LABEL(w), markup);
2099 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
2102 SetClockIcon (int color)
2104 GdkPixbuf *pm = *clockIcons[color];
2105 if (mainwindowIcon != pm) {
2106 mainwindowIcon = pm;
2108 gtkosx_application_set_dock_icon_pixbuf(theApp, mainwindowIcon);
2110 gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
2115 #define INPUT_SOURCE_BUF_SIZE 8192
2124 char buf[INPUT_SOURCE_BUF_SIZE];
2129 DoInputCallback(io, cond, data)
2134 /* read input from one of the input source (for example a chess program, ICS, etc).
2135 * and call a function that will handle the input
2142 /* All information (callback function, file descriptor, etc) is
2143 * saved in an InputSource structure
2145 InputSource *is = (InputSource *) data;
2147 if (is->lineByLine) {
2148 count = read(is->fd, is->unused,
2149 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2151 if(count == 0 && is->kind == CPReal && shells[ChatDlg]) { // [HGM] absence of terminal is no error if ICS Console present
2152 RemoveInputSource(is); // cease reading stdin
2153 stdoutClosed = TRUE; // suppress future output
2156 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2159 is->unused += count;
2161 /* break input into lines and call the callback function on each
2164 while (p < is->unused) {
2165 q = memchr(p, '\n', is->unused - p);
2166 if (q == NULL) break;
2168 (is->func)(is, is->closure, p, q - p, 0);
2171 /* remember not yet used part of the buffer */
2173 while (p < is->unused) {
2178 /* read maximum length of input buffer and send the whole buffer
2179 * to the callback function
2181 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2186 (is->func)(is, is->closure, is->buf, count, error);
2188 return True; // Must return true or the watch will be removed
2191 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2198 GIOChannel *channel;
2199 ChildProc *cp = (ChildProc *) pr;
2201 is = (InputSource *) calloc(1, sizeof(InputSource));
2202 is->lineByLine = lineByLine;
2206 is->fd = fileno(stdin);
2208 is->kind = cp->kind;
2209 is->fd = cp->fdFrom;
2212 is->unused = is->buf;
2216 /* GTK-TODO: will this work on windows?*/
2218 channel = g_io_channel_unix_new(is->fd);
2219 g_io_channel_set_close_on_unref (channel, TRUE);
2220 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2222 is->closure = closure;
2223 return (InputSourceRef) is;
2228 RemoveInputSource(isr)
2231 InputSource *is = (InputSource *) isr;
2233 if (is->sid == 0) return;
2234 g_source_remove(is->sid);
2241 static Boolean frameWaiting;
2244 FrameAlarm (int sig)
2246 frameWaiting = False;
2247 /* In case System-V style signals. Needed?? */
2248 signal(SIGALRM, FrameAlarm);
2252 FrameDelay (int time)
2254 struct itimerval delay;
2257 frameWaiting = True;
2258 signal(SIGALRM, FrameAlarm);
2259 delay.it_interval.tv_sec =
2260 delay.it_value.tv_sec = time / 1000;
2261 delay.it_interval.tv_usec =
2262 delay.it_value.tv_usec = (time % 1000) * 1000;
2263 setitimer(ITIMER_REAL, &delay, NULL);
2264 while (frameWaiting) pause();
2265 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2266 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2267 setitimer(ITIMER_REAL, &delay, NULL);
2274 FrameDelay (int time)
2277 XSync(xDisplay, False);
2279 // gtk_main_iteration_do(False);
2282 usleep(time * 1000);
2288 FindLogo (char *place, char *name, char *buf)
2289 { // check if file exists in given place
2291 if(!place) return 0;
2292 snprintf(buf, MSG_SIZ, "%s/%s.png", place, name);
2293 if(*place && strcmp(place, ".") && (f = fopen(buf, "r")) ) {
2301 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2303 char buf[MSG_SIZ], *logoName = buf;
2304 if(appData.logo[n][0]) {
2305 logoName = appData.logo[n];
2306 } else if(appData.autoLogo) {
2307 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2308 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2309 } else { // engine; cascade
2310 if(!FindLogo(appData.logoDir, cps->tidy, buf) && // first try user log folder
2311 !FindLogo(appData.directory[n], "logo", buf) && // then engine directory
2312 !FindLogo("/usr/local/share/games/plugins/logos", cps->tidy, buf) ) // then system folders
2313 FindLogo("/usr/share/games/plugins/logos", cps->tidy, buf);
2317 { ASSIGN(cps->programLogo, logoName); }
2321 UpdateLogos (int displ)
2323 if(optList[W_WHITE-1].handle == NULL) return;
2324 LoadLogo(&first, 0, 0);
2325 LoadLogo(&second, 1, appData.icsActive);
2326 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2330 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2341 GtkFileFilter *gtkfilter;
2342 GtkFileFilter *gtkfilter_all;
2344 char fileext[10] = "";
2345 char *result = NULL;
2347 char curDir[MSG_SIZ];
2349 StartDir(filter, NULL); // change to start directory for this file type
2351 if(def && *def && def[strlen(def)-1] == '/') {
2352 getcwd(curDir, MSG_SIZ);
2357 /* make a copy of the filter string, so that strtok can work with it*/
2358 cp = strdup(filter);
2360 /* add filters for file extensions */
2361 gtkfilter = gtk_file_filter_new();
2362 gtkfilter_all = gtk_file_filter_new();
2364 /* one filter to show everything */
2365 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2366 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2368 /* add filter if present */
2369 result = strtok(cp, space);
2370 while( result != NULL ) {
2371 snprintf(fileext,10,"*%s",result);
2372 result = strtok( NULL, space );
2373 gtk_file_filter_add_pattern(gtkfilter, fileext);
2376 /* second filter to only show what's useful */
2377 gtk_file_filter_set_name (gtkfilter,filter);
2379 if (openMode[0] == 'r')
2381 dialog = gtk_file_chooser_dialog_new (label,
2383 GTK_FILE_CHOOSER_ACTION_OPEN,
2384 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2385 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2390 dialog = gtk_file_chooser_dialog_new (label,
2392 GTK_FILE_CHOOSER_ACTION_SAVE,
2393 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2394 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2396 /* add filename suggestions */
2397 if (strlen(def) > 0 )
2398 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2400 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2404 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2405 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2406 /* activate filter */
2407 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2409 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2414 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2417 f = fopen(filename, openMode);
2420 DisplayError(_("Failed to open file"), errno);
2424 /* TODO add indec */
2426 ASSIGN(*name, filename);
2427 ScheduleDelayedEvent(DelayedLoad, 50);
2429 StartDir(filter, filename);
2432 else StartDir(filter, "");
2434 gtk_widget_destroy (dialog);
2437 if(def && *def && def[strlen(def)-1] == '/') chdir(curDir);