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 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
69 # if HAVE_SYS_SOCKET_H
70 # include <sys/socket.h>
71 # include <netinet/in.h>
73 # else /* not HAVE_SYS_SOCKET_H */
74 # if HAVE_LAN_SOCKET_H
75 # include <lan/socket.h>
77 # include <lan/netdb.h>
78 # else /* not HAVE_LAN_SOCKET_H */
79 # define OMIT_SOCKETS 1
80 # endif /* not HAVE_LAN_SOCKET_H */
81 # endif /* not HAVE_SYS_SOCKET_H */
82 #endif /* !OMIT_SOCKETS */
87 #else /* not STDC_HEADERS */
88 extern char *getenv();
91 # else /* not HAVE_STRING_H */
93 # endif /* not HAVE_STRING_H */
94 #endif /* not STDC_HEADERS */
97 # include <sys/fcntl.h>
98 #else /* not HAVE_SYS_FCNTL_H */
101 # endif /* HAVE_FCNTL_H */
102 #endif /* not HAVE_SYS_FCNTL_H */
104 #if HAVE_SYS_SYSTEMINFO_H
105 # include <sys/systeminfo.h>
106 #endif /* HAVE_SYS_SYSTEMINFO_H */
108 #if TIME_WITH_SYS_TIME
109 # include <sys/time.h>
113 # include <sys/time.h>
124 # include <sys/wait.h>
129 # define NAMLEN(dirent) strlen((dirent)->d_name)
130 # define HAVE_DIR_STRUCT
132 # define dirent direct
133 # define NAMLEN(dirent) (dirent)->d_namlen
135 # include <sys/ndir.h>
136 # define HAVE_DIR_STRUCT
139 # include <sys/dir.h>
140 # define HAVE_DIR_STRUCT
144 # define HAVE_DIR_STRUCT
152 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
155 #include "frontend.h"
157 #include "backendz.h"
162 #include "xgamelist.h"
163 #include "xhistory.h"
167 #include "engineoutput.h"
177 #define usleep(t) _sleep2(((t)+500)/1000)
181 # define _(s) gettext (s)
182 # define N_(s) gettext_noop (s)
188 int main P((int argc, char **argv));
189 RETSIGTYPE CmailSigHandler P((int sig));
190 RETSIGTYPE IntSigHandler P((int sig));
191 RETSIGTYPE TermSizeSigHandler P((int sig));
193 char *InsertPxlSize P((char *pattern, int targetPxlSize));
194 XFontSet CreateFontSet P((char *base_fnt_lst));
196 char *FindFont P((char *pattern, int targetPxlSize));
198 void DelayedDrag P((void));
199 void ICSInputBoxPopUp P((void));
200 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
202 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
203 void HandlePV P((Widget w, XEvent * event,
204 String * params, Cardinal * nParams));
205 void DrawPositionProc P((Widget w, XEvent *event,
206 String *prms, Cardinal *nprms));
207 void CommentClick P((Widget w, XEvent * event,
208 String * params, Cardinal * nParams));
209 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
210 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
211 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
212 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
213 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
214 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
215 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
216 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
217 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
218 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
220 Boolean TempBackwardActive = False;
221 void DisplayMove P((int moveNumber));
222 void ICSInitScript P((void));
223 void update_ics_width P(());
224 int CopyMemoProc P(());
228 * XBoard depends on Xt R4 or higher
230 int xtVersion = XtSpecificationRelease;
235 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
236 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
237 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
239 XFontSet fontSet, clockFontSet;
242 XFontStruct *clockFontStruct;
244 Font coordFontID, countFontID;
245 XFontStruct *coordFontStruct, *countFontStruct;
246 XtAppContext appContext;
248 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
250 GtkWidget *mainwindow;
252 Option *optList; // contains all widgets of main window
255 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
257 typedef unsigned int BoardSize;
259 Boolean chessProgram;
261 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
262 int smallLayout = 0, tinyLayout = 0,
263 marginW, marginH, // [HGM] for run-time resizing
264 fromX = -1, fromY = -1, toX, toY, commentUp = False,
265 errorExitStatus = -1, defaultLineGap;
267 Dimension textHeight;
268 Pixel timerForegroundPixel, timerBackgroundPixel;
269 Pixel buttonForegroundPixel, buttonBackgroundPixel;
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;
287 /* This magic number is the number of intermediate frames used
288 in each half of the animation. For short moves it's reduced
289 by 1. The total number of frames will be factor * 2 + 1. */
292 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
299 DropMenuEnables dmEnables[] = {
317 XtResource clientResources[] = {
318 { "flashCount", "flashCount", XtRInt, sizeof(int),
319 XtOffset(AppDataPtr, flashCount), XtRImmediate,
320 (XtPointer) FLASH_COUNT },
323 XrmOptionDescRec shellOptions[] = {
324 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
325 { "-flash", "flashCount", XrmoptionNoArg, "3" },
326 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
329 XtActionsRec boardActions[] = {
330 { "DrawPosition", DrawPositionProc },
331 { "HandlePV", HandlePV },
332 { "SelectPV", SelectPV },
333 { "StopPV", StopPV },
334 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
335 { "QuitProc", QuitWrapper },
336 { "ManProc", ManInner },
337 { "TempBackwardProc", TempBackwardProc },
338 { "TempForwardProc", TempForwardProc },
339 { "CommentClick", (XtActionProc) CommentClick },
340 { "GenericPopDown", (XtActionProc) GenericPopDown },
341 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
342 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
343 { "SelectMove", (XtActionProc) SelectMove },
344 { "LoadSelectedProc", LoadSelectedProc },
345 { "SetFilterProc", SetFilterProc },
346 { "TypeInProc", TypeInProc },
347 { "EnterKeyProc", EnterKeyProc },
348 { "UpKeyProc", UpKeyProc },
349 { "DownKeyProc", DownKeyProc },
350 { "WheelProc", WheelProc },
351 { "TabProc", TabProc },
355 char globalTranslations[] =
356 ":<Key>F9: MenuItem(Actions.Resign) \n \
357 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
358 :Meta<Key>V: MenuItem(File.NewVariant) \n \
359 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
360 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
361 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
362 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
363 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
364 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
365 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
366 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
367 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
368 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
369 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
370 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
371 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
372 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
373 :Ctrl<Key>q: MenuItem(File.Quit) \n \
374 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
375 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
376 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
377 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
378 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
379 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
380 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
381 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
382 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
383 :Meta<Key>G: MenuItem(View.GameList) \n \
384 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
385 :<Key>Pause: MenuItem(Mode.Pause) \n \
386 :<Key>F3: MenuItem(Action.Accept) \n \
387 :<Key>F4: MenuItem(Action.Decline) \n \
388 :<Key>F12: MenuItem(Action.Rematch) \n \
389 :<Key>F5: MenuItem(Action.CallFlag) \n \
390 :<Key>F6: MenuItem(Action.Draw) \n \
391 :<Key>F7: MenuItem(Action.Adjourn) \n \
392 :<Key>F8: MenuItem(Action.Abort) \n \
393 :<Key>F10: MenuItem(Action.StopObserving) \n \
394 :<Key>F11: MenuItem(Action.StopExamining) \n \
395 :Ctrl<Key>d: MenuItem(DebugProc) \n \
396 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
397 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
398 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
399 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
400 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
401 :<Key>Left: MenuItem(Edit.Backward) \n \
402 :<Key>Right: MenuItem(Edit.Forward) \n \
403 :<Key>Home: MenuItem(Edit.Revert) \n \
404 :<Key>End: MenuItem(Edit.TruncateGame) \n \
405 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
406 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
407 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
408 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
409 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
410 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
411 #ifndef OPTIONSDIALOG
413 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
414 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
415 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
416 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
417 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
420 :<Key>F1: MenuItem(Help.ManXBoard) \n \
421 :<Key>F2: MenuItem(View.FlipView) \n \
422 :<KeyDown>Return: TempBackwardProc() \n \
423 :<KeyUp>Return: TempForwardProc() \n";
425 char ICSInputTranslations[] =
426 "<Key>Up: UpKeyProc() \n "
427 "<Key>Down: DownKeyProc() \n "
428 "<Key>Return: EnterKeyProc() \n";
430 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
431 // as the widget is destroyed before the up-click can call extend-end
432 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
435 String xboardResources[] = {
436 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
441 /* Max possible square size */
442 #define MAXSQSIZE 256
444 static int xpm_avail[MAXSQSIZE];
446 #ifdef HAVE_DIR_STRUCT
448 /* Extract piece size from filename */
450 xpm_getsize (char *name, int len, char *ext)
458 if ((p=strchr(name, '.')) == NULL ||
459 StrCaseCmp(p+1, ext) != 0)
465 while (*p && isdigit(*p))
472 /* Setup xpm_avail */
474 xpm_getavail (char *dirname, char *ext)
480 for (i=0; i<MAXSQSIZE; ++i)
483 if (appData.debugMode)
484 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
486 dir = opendir(dirname);
489 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
490 programName, dirname);
494 while ((ent=readdir(dir)) != NULL) {
495 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
496 if (i > 0 && i < MAXSQSIZE)
506 xpm_print_avail (FILE *fp, char *ext)
510 fprintf(fp, _("Available `%s' sizes:\n"), ext);
511 for (i=1; i<MAXSQSIZE; ++i) {
517 /* Return XPM piecesize closest to size */
519 xpm_closest_to (char *dirname, int size, char *ext)
522 int sm_diff = MAXSQSIZE;
526 xpm_getavail(dirname, ext);
528 if (appData.debugMode)
529 xpm_print_avail(stderr, ext);
531 for (i=1; i<MAXSQSIZE; ++i) {
534 diff = (diff<0) ? -diff : diff;
535 if (diff < sm_diff) {
543 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
549 #else /* !HAVE_DIR_STRUCT */
550 /* If we are on a system without a DIR struct, we can't
551 read the directory, so we can't collect a list of
552 filenames, etc., so we can't do any size-fitting. */
554 xpm_closest_to (char *dirname, int size, char *ext)
557 Warning: No DIR structure found on this system --\n\
558 Unable to autosize for XPM/XIM pieces.\n\
559 Please report this error to %s.\n\
560 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
563 #endif /* HAVE_DIR_STRUCT */
567 /* Arrange to catch delete-window events */
568 Atom wm_delete_window;
570 CatchDeleteWindow (Widget w, String procname)
573 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
574 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
575 XtAugmentTranslations(w, XtParseTranslationTable(buf));
582 gtk_window_present(GTK_WINDOW(mainwindow));
585 //---------------------------------------------------------------------------------------------------------
586 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
589 #define CW_USEDEFAULT (1<<31)
590 #define ICS_TEXT_MENU_SIZE 90
591 #define DEBUG_FILE "xboard.debug"
592 #define SetCurrentDirectory chdir
593 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
597 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
600 // front-end part of option handling
602 // [HGM] This platform-dependent table provides the location for storing the color info
603 extern char *crWhite, * crBlack;
607 &appData.whitePieceColor,
608 &appData.blackPieceColor,
609 &appData.lightSquareColor,
610 &appData.darkSquareColor,
611 &appData.highlightSquareColor,
612 &appData.premoveHighlightColor,
613 &appData.lowTimeWarningColor,
624 // [HGM] font: keep a font for each square size, even non-stndard ones
627 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
628 char *fontTable[NUM_FONTS][MAX_SIZE];
631 ParseFont (char *name, int number)
632 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
634 if(sscanf(name, "size%d:", &size)) {
635 // [HGM] font: font is meant for specific boardSize (likely from settings file);
636 // defer processing it until we know if it matches our board size
637 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
638 fontTable[number][size] = strdup(strchr(name, ':')+1);
639 fontValid[number][size] = True;
644 case 0: // CLOCK_FONT
645 appData.clockFont = strdup(name);
647 case 1: // MESSAGE_FONT
648 appData.font = strdup(name);
650 case 2: // COORD_FONT
651 appData.coordFont = strdup(name);
656 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
661 { // only 2 fonts currently
662 appData.clockFont = CLOCK_FONT_NAME;
663 appData.coordFont = COORD_FONT_NAME;
664 appData.font = DEFAULT_FONT_NAME;
669 { // no-op, until we identify the code for this already in XBoard and move it here
673 ParseColor (int n, char *name)
674 { // in XBoard, just copy the color-name string
675 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
679 ParseTextAttribs (ColorClass cc, char *s)
681 (&appData.colorShout)[cc] = strdup(s);
685 ParseBoardSize (void *addr, char *name)
687 appData.boardSize = strdup(name);
692 { // In XBoard the sound-playing program takes care of obtaining the actual sound
696 SetCommPortDefaults ()
697 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
700 // [HGM] args: these three cases taken out to stay in front-end
702 SaveFontArg (FILE *f, ArgDescriptor *ad)
705 int i, n = (int)(intptr_t)ad->argLoc;
707 case 0: // CLOCK_FONT
708 name = appData.clockFont;
710 case 1: // MESSAGE_FONT
713 case 2: // COORD_FONT
714 name = appData.coordFont;
719 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
720 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
721 fontTable[n][squareSize] = strdup(name);
722 fontValid[n][squareSize] = True;
725 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
726 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
731 { // nothing to do, as the sounds are at all times represented by their text-string names already
735 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
736 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
737 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
741 SaveColor (FILE *f, ArgDescriptor *ad)
742 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
743 if(colorVariable[(int)(intptr_t)ad->argLoc])
744 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
748 SaveBoardSize (FILE *f, char *name, void *addr)
749 { // wrapper to shield back-end from BoardSize & sizeInfo
750 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
754 ParseCommPortSettings (char *s)
755 { // no such option in XBoard (yet)
762 GetActualPlacement (Widget wg, WindowPlacement *wp)
764 XWindowAttributes winAt;
771 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
772 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
773 wp->x = rx - winAt.x;
774 wp->y = ry - winAt.y;
775 wp->height = winAt.height;
776 wp->width = winAt.width;
777 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
783 { // wrapper to shield use of window handles from back-end (make addressible by number?)
784 // In XBoard this will have to wait until awareness of window parameters is implemented
786 GetActualPlacement(shellWidget, &wpMain);
787 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
788 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
789 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
790 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
791 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
792 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
797 PrintCommPortSettings (FILE *f, char *name)
798 { // This option does not exist in XBoard
802 EnsureOnScreen (int *x, int *y, int minX, int minY)
809 { // [HGM] args: allows testing if main window is realized from back-end
811 return xBoardWindow != 0;
818 PopUpStartupDialog ()
819 { // start menu not implemented in XBoard
823 ConvertToLine (int argc, char **argv)
825 static char line[128*1024], buf[1024];
829 for(i=1; i<argc; i++)
831 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
832 && argv[i][0] != '{' )
833 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
835 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
836 strncat(line, buf, 128*1024 - strlen(line) - 1 );
839 line[strlen(line)-1] = NULLCHAR;
843 //--------------------------------------------------------------------------------------------
846 ResizeBoardWindow (int w, int h, int inhibit)
849 w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
851 shellArgs[0].value = w;
852 shellArgs[1].value = h;
853 shellArgs[4].value = shellArgs[2].value = w;
854 shellArgs[5].value = shellArgs[3].value = h;
855 XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
857 XSync(xDisplay, False);
863 MakeOneColor (char *name, Pixel *color)
866 if (!appData.monoMode) {
867 vFrom.addr = (caddr_t) name;
868 vFrom.size = strlen(name);
869 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
870 if (vTo.addr == NULL) {
871 appData.monoMode = True;
874 *color = *(Pixel *) vTo.addr;
883 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
884 int forceMono = False;
887 if (appData.lowTimeWarning)
888 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
889 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
890 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
897 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
898 { // detervtomine what fonts to use, and create them
903 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
904 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
905 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
906 appData.font = fontTable[MESSAGE_FONT][squareSize];
907 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
908 appData.coordFont = fontTable[COORD_FONT][squareSize];
911 appData.font = InsertPxlSize(appData.font, fontPxlSize);
912 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
913 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
914 fontSet = CreateFontSet(appData.font);
915 clockFontSet = CreateFontSet(appData.clockFont);
917 /* For the coordFont, use the 0th font of the fontset. */
918 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
919 XFontStruct **font_struct_list;
920 XFontSetExtents *fontSize;
921 char **font_name_list;
922 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
923 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
924 coordFontStruct = XQueryFont(xDisplay, coordFontID);
925 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
926 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
929 appData.font = FindFont(appData.font, fontPxlSize);
930 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
931 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
932 clockFontID = XLoadFont(xDisplay, appData.clockFont);
933 clockFontStruct = XQueryFont(xDisplay, clockFontID);
934 coordFontID = XLoadFont(xDisplay, appData.coordFont);
935 coordFontStruct = XQueryFont(xDisplay, coordFontID);
936 // textHeight in !NLS mode!
938 countFontID = coordFontID; // [HGM] holdings
939 countFontStruct = coordFontStruct;
941 xdb = XtDatabase(xDisplay);
943 XrmPutLineResource(&xdb, "*international: True");
944 vTo.size = sizeof(XFontSet);
945 vTo.addr = (XtPointer) &fontSet;
946 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
948 XrmPutStringResource(&xdb, "*font", appData.font);
959 case ArgInt: p = " N"; break;
960 case ArgString: p = " STR"; break;
961 case ArgBoolean: p = " TF"; break;
962 case ArgSettingsFilename:
963 case ArgFilename: p = " FILE"; break;
964 case ArgX: p = " Nx"; break;
965 case ArgY: p = " Ny"; break;
966 case ArgAttribs: p = " TEXTCOL"; break;
967 case ArgColor: p = " COL"; break;
968 case ArgFont: p = " FONT"; break;
969 case ArgBoardSize: p = " SIZE"; break;
970 case ArgFloat: p = " FLOAT"; break;
975 case ArgCommSettings:
986 ArgDescriptor *q, *p = argDescriptors+5;
987 printf("\nXBoard accepts the following options:\n"
988 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
989 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
990 " SIZE = board-size spec(s)\n"
991 " Within parentheses are short forms, or options to set to true or false.\n"
992 " Persistent options (saved in the settings file) are marked with *)\n\n");
994 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
995 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
996 if(p->save) strcat(buf+len, "*");
997 for(q=p+1; q->argLoc == p->argLoc; q++) {
998 if(q->argName[0] == '-') continue;
999 strcat(buf+len, q == p+1 ? " (" : " ");
1000 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1002 if(q != p+1) strcat(buf+len, ")");
1004 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1007 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1011 main (int argc, char **argv)
1013 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1015 XSetWindowAttributes window_attributes;
1017 Dimension boardWidth, boardHeight, w, h;
1020 int boardWidth, boardHeight, w, h;
1022 int forceMono = False;
1023 GError *gtkerror=NULL;
1025 srandom(time(0)); // [HGM] book: make random truly random
1027 setbuf(stdout, NULL);
1028 setbuf(stderr, NULL);
1031 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1032 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1036 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1042 gtk_init (&argc, &argv);
1044 programName = strrchr(argv[0], '/');
1045 if (programName == NULL)
1046 programName = argv[0];
1051 // if (appData.debugMode) {
1052 // fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1055 bindtextdomain(PACKAGE, LOCALEDIR);
1056 textdomain(PACKAGE);
1059 appData.boardSize = "";
1060 InitAppData(ConvertToLine(argc, argv));
1062 if (p == NULL) p = "/tmp";
1063 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1064 gameCopyFilename = (char*) malloc(i);
1065 gamePasteFilename = (char*) malloc(i);
1066 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1067 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1069 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1070 static char buf[MSG_SIZ];
1071 EscapeExpand(buf, appData.firstInitString);
1072 appData.firstInitString = strdup(buf);
1073 EscapeExpand(buf, appData.secondInitString);
1074 appData.secondInitString = strdup(buf);
1075 EscapeExpand(buf, appData.firstComputerString);
1076 appData.firstComputerString = strdup(buf);
1077 EscapeExpand(buf, appData.secondComputerString);
1078 appData.secondComputerString = strdup(buf);
1081 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1084 if (chdir(chessDir) != 0) {
1085 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1091 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1092 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1093 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1094 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1097 setbuf(debugFP, NULL);
1101 if (appData.debugMode) {
1102 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1106 /* [HGM,HR] make sure board size is acceptable */
1107 if(appData.NrFiles > BOARD_FILES ||
1108 appData.NrRanks > BOARD_RANKS )
1109 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1112 /* This feature does not work; animation needs a rewrite */
1113 appData.highlightDragging = FALSE;
1117 gameInfo.variant = StringToVariant(appData.variant);
1118 InitPosition(FALSE);
1122 builder = gtk_builder_new();
1123 filename = get_glade_filename ("mainboard.glade");
1124 if(! gtk_builder_add_from_file (builder, filename, >kerror) )
1127 printf ("Error: %d %s\n",gtkerror->code,gtkerror->message);
1129 mainwindow = GTK_WIDGET(gtk_builder_get_object (builder, "mainwindow"));
1132 XtAppInitialize(&appContext, "XBoard", shellOptions,
1133 XtNumber(shellOptions),
1134 &argc, argv, xboardResources, NULL, 0);
1136 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1137 clientResources, XtNumber(clientResources),
1140 xDisplay = XtDisplay(shellWidget);
1141 xScreen = DefaultScreen(xDisplay);
1142 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1146 * determine size, based on supplied or remembered -size, or screen size
1148 if (isdigit(appData.boardSize[0])) {
1149 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1150 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1151 &fontPxlSize, &smallLayout, &tinyLayout);
1153 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1154 programName, appData.boardSize);
1158 /* Find some defaults; use the nearest known size */
1159 SizeDefaults *szd, *nearest;
1160 int distance = 99999;
1161 nearest = szd = sizeDefaults;
1162 while (szd->name != NULL) {
1163 if (abs(szd->squareSize - squareSize) < distance) {
1165 distance = abs(szd->squareSize - squareSize);
1166 if (distance == 0) break;
1170 if (i < 2) lineGap = nearest->lineGap;
1171 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1172 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1173 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1174 if (i < 6) smallLayout = nearest->smallLayout;
1175 if (i < 7) tinyLayout = nearest->tinyLayout;
1178 SizeDefaults *szd = sizeDefaults;
1179 if (*appData.boardSize == NULLCHAR) {
1181 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1182 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1186 GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
1187 guint screenwidth = gdk_screen_get_width(screen);
1188 guint screenheight = gdk_screen_get_height(screen);
1189 while (screenwidth < szd->minScreenSize ||
1190 screenheight < szd->minScreenSize) {
1194 if (szd->name == NULL) szd--;
1195 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1197 while (szd->name != NULL &&
1198 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1199 if (szd->name == NULL) {
1200 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1201 programName, appData.boardSize);
1205 squareSize = szd->squareSize;
1206 lineGap = szd->lineGap;
1207 clockFontPxlSize = szd->clockFontPxlSize;
1208 coordFontPxlSize = szd->coordFontPxlSize;
1209 fontPxlSize = szd->fontPxlSize;
1210 smallLayout = szd->smallLayout;
1211 tinyLayout = szd->tinyLayout;
1212 // [HGM] font: use defaults from settings file if available and not overruled
1215 defaultLineGap = lineGap;
1216 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1218 /* [HR] height treated separately (hacked) */
1219 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1220 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1223 * Determine what fonts to use.
1226 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1230 * Detect if there are not enough colors available and adapt.
1233 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1234 appData.monoMode = True;
1238 forceMono = MakeColors();
1241 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1243 appData.monoMode = True;
1246 ParseIcsTextColors();
1249 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1256 layoutName = "tinyLayout";
1257 } else if (smallLayout) {
1258 layoutName = "smallLayout";
1260 layoutName = "normalLayout";
1263 optList = BoardPopUp(squareSize, lineGap, (void*)
1273 InitDrawingHandle(optList + W_BOARD);
1274 currBoard = &optList[W_BOARD];
1275 boardWidget = optList[W_BOARD].handle;
1276 menuBarWidget = optList[W_MENU].handle;
1277 dropMenu = optList[W_DROP].handle;
1278 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1280 formWidget = XtParent(boardWidget);
1281 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1282 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1283 XtGetValues(optList[W_WHITE].handle, args, 2);
1284 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1285 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1286 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1287 XtGetValues(optList[W_PAUSE].handle, args, 2);
1292 xBoardWindow = XtWindow(boardWidget);
1295 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1296 // not need to go into InitDrawingSizes().
1304 ReadBitmap(&wIconPixmap, "icon_white.bm",
1305 icon_white_bits, icon_white_width, icon_white_height);
1306 ReadBitmap(&bIconPixmap, "icon_black.bm",
1307 icon_black_bits, icon_black_width, icon_black_height);
1308 iconPixmap = wIconPixmap;
1310 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1311 XtSetValues(shellWidget, args, i);
1315 * Create a cursor for the board widget.
1318 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1319 XChangeWindowAttributes(xDisplay, xBoardWindow,
1320 CWCursor, &window_attributes);
1324 * Inhibit shell resizing.
1327 shellArgs[0].value = (XtArgVal) &w;
1328 shellArgs[1].value = (XtArgVal) &h;
1329 XtGetValues(shellWidget, shellArgs, 2);
1330 shellArgs[4].value = shellArgs[2].value = w;
1331 shellArgs[5].value = shellArgs[3].value = h;
1332 // XtSetValues(shellWidget, &shellArgs[2], 4);
1334 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1335 marginH = h - boardHeight;
1338 CatchDeleteWindow(shellWidget, "QuitProc");
1344 if(appData.logoSize)
1345 { // locate and read user logo
1347 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1348 ASSIGN(userLogo, buf);
1351 if (appData.animate || appData.animateDragging)
1355 XtAugmentTranslations(formWidget,
1356 XtParseTranslationTable(globalTranslations));
1358 XtAddEventHandler(formWidget, KeyPressMask, False,
1359 (XtEventHandler) MoveTypeInProc, NULL);
1360 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1361 (XtEventHandler) EventProc, NULL);
1363 g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1365 /* [AS] Restore layout */
1366 if( wpMoveHistory.visible ) {
1370 if( wpEvalGraph.visible )
1375 if( wpEngineOutput.visible ) {
1376 EngineOutputPopUp();
1381 if (errorExitStatus == -1) {
1382 if (appData.icsActive) {
1383 /* We now wait until we see "login:" from the ICS before
1384 sending the logon script (problems with timestamp otherwise) */
1385 /*ICSInitScript();*/
1386 if (appData.icsInputBox) ICSInputBoxPopUp();
1390 signal(SIGWINCH, TermSizeSigHandler);
1392 signal(SIGINT, IntSigHandler);
1393 signal(SIGTERM, IntSigHandler);
1394 if (*appData.cmailGameName != NULLCHAR) {
1395 signal(SIGUSR1, CmailSigHandler);
1399 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1402 // XtSetKeyboardFocus(shellWidget, formWidget);
1404 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1407 /* check for GTK events and process them */
1410 gtk_main_iteration();
1413 if (appData.debugMode) fclose(debugFP); // [DM] debug
1418 TermSizeSigHandler (int sig)
1424 IntSigHandler (int sig)
1430 CmailSigHandler (int sig)
1435 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1437 /* Activate call-back function CmailSigHandlerCallBack() */
1438 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1440 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1444 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1447 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1449 /**** end signal code ****/
1452 #define Abs(n) ((n)<0 ? -(n) : (n))
1456 InsertPxlSize (char *pattern, int targetPxlSize)
1458 char *base_fnt_lst, strInt[12], *p, *q;
1459 int alternatives, i, len, strIntLen;
1462 * Replace the "*" (if present) in the pixel-size slot of each
1463 * alternative with the targetPxlSize.
1467 while ((p = strchr(p, ',')) != NULL) {
1471 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1472 strIntLen = strlen(strInt);
1473 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1477 while (alternatives--) {
1478 char *comma = strchr(p, ',');
1479 for (i=0; i<14; i++) {
1480 char *hyphen = strchr(p, '-');
1482 if (comma && hyphen > comma) break;
1483 len = hyphen + 1 - p;
1484 if (i == 7 && *p == '*' && len == 2) {
1486 memcpy(q, strInt, strIntLen);
1496 len = comma + 1 - p;
1503 return base_fnt_lst;
1508 CreateFontSet (char *base_fnt_lst)
1511 char **missing_list;
1515 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1516 &missing_list, &missing_count, &def_string);
1517 if (appData.debugMode) {
1519 XFontStruct **font_struct_list;
1520 char **font_name_list;
1521 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1523 fprintf(debugFP, " got list %s, locale %s\n",
1524 XBaseFontNameListOfFontSet(fntSet),
1525 XLocaleOfFontSet(fntSet));
1526 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1527 for (i = 0; i < count; i++) {
1528 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1531 for (i = 0; i < missing_count; i++) {
1532 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1535 if (fntSet == NULL) {
1536 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1542 #else // not ENABLE_NLS
1544 * Find a font that matches "pattern" that is as close as
1545 * possible to the targetPxlSize. Prefer fonts that are k
1546 * pixels smaller to fonts that are k pixels larger. The
1547 * pattern must be in the X Consortium standard format,
1548 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1549 * The return value should be freed with XtFree when no
1553 FindFont (char *pattern, int targetPxlSize)
1555 char **fonts, *p, *best, *scalable, *scalableTail;
1556 int i, j, nfonts, minerr, err, pxlSize;
1559 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1561 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1562 programName, pattern);
1569 for (i=0; i<nfonts; i++) {
1572 if (*p != '-') continue;
1574 if (*p == NULLCHAR) break;
1575 if (*p++ == '-') j++;
1577 if (j < 7) continue;
1580 scalable = fonts[i];
1583 err = pxlSize - targetPxlSize;
1584 if (Abs(err) < Abs(minerr) ||
1585 (minerr > 0 && err < 0 && -err == minerr)) {
1591 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1592 /* If the error is too big and there is a scalable font,
1593 use the scalable font. */
1594 int headlen = scalableTail - scalable;
1595 p = (char *) XtMalloc(strlen(scalable) + 10);
1596 while (isdigit(*scalableTail)) scalableTail++;
1597 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1599 p = (char *) XtMalloc(strlen(best) + 2);
1600 safeStrCpy(p, best, strlen(best)+1 );
1602 if (appData.debugMode) {
1603 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1604 pattern, targetPxlSize, p);
1606 XFreeFontNames(fonts);
1613 EnableNamedMenuItem (char *menuRef, int state)
1615 MenuItem *item = MenuNameToItem(menuRef);
1617 if(item) gtk_widget_set_sensitive(item->handle, state);
1621 EnableButtonBar (int state)
1624 XtSetSensitive(optList[W_BUTTON].handle, state);
1630 SetMenuEnables (Enables *enab)
1632 while (enab->name != NULL) {
1633 EnableNamedMenuItem(enab->name, enab->value);
1638 gboolean KeyPressProc(window, eventkey, data)
1640 GdkEventKey *eventkey;
1644 MoveTypeInProc(eventkey); // pop up for typed in moves
1647 /* check for other key values */
1648 switch(eventkey->keyval) {
1660 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1661 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1663 if(*nprms == 0) return;
1664 item = MenuNameToItem(prms[0]);
1665 if(item) ((MenuProc *) item->proc) ();
1679 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1680 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1681 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1682 dmEnables[i].piece);
1683 XtSetSensitive(entry, p != NULL || !appData.testLegality
1684 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1685 && !appData.icsActive));
1687 while (p && *p++ == dmEnables[i].piece) count++;
1688 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
1690 XtSetArg(args[j], XtNlabel, label); j++;
1691 XtSetValues(entry, args, j);
1697 do_flash_delay (unsigned long msec)
1703 FlashDelay (int flash_delay)
1705 if(flash_delay) do_flash_delay(flash_delay);
1709 Fraction (int x, int start, int stop)
1711 double f = ((double) x - start)/(stop - start);
1712 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1716 static WindowPlacement wpNew;
1720 CoDrag (Widget sh, WindowPlacement *wp)
1723 int j=0, touch=0, fudge = 2;
1724 GetActualPlacement(sh, wp);
1725 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
1726 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
1727 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1728 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
1729 if(!touch ) return; // only windows that touch co-move
1730 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1731 int heightInc = wpNew.height - wpMain.height;
1732 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1733 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1734 wp->y += fracTop * heightInc;
1735 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1736 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1737 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1738 int widthInc = wpNew.width - wpMain.width;
1739 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1740 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1741 wp->y += fracLeft * widthInc;
1742 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1743 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1745 wp->x += wpNew.x - wpMain.x;
1746 wp->y += wpNew.y - wpMain.y;
1747 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1748 if(touch == 3) wp->y += wpNew.height - wpMain.height;
1750 XtSetArg(args[j], XtNx, wp->x); j++;
1751 XtSetArg(args[j], XtNy, wp->y); j++;
1752 XtSetValues(sh, args, j);
1757 ReSize (WindowPlacement *wp)
1760 if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1761 sqx = (wp->width - lineGap - marginW) / BOARD_WIDTH - lineGap;
1762 sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1763 if(sqy < sqx) sqx = sqy;
1764 if(sqx != squareSize) {
1765 squareSize = sqx; // adopt new square size
1766 CreatePNGPieces(); // make newly scaled pieces
1767 InitDrawingSizes(0, 0); // creates grid etc.
1768 } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1769 w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1770 h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1771 if(optList[W_BOARD].max > w) optList[W_BOARD].max = w;
1772 if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1776 static XtIntervalId delayedDragID = 0;
1778 static int delayedDragID = 0;
1788 GetActualPlacement(shellWidget, &wpNew);
1789 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1790 wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1791 busy = 0; return; // false alarm
1794 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1795 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1796 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1797 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1799 DrawPosition(True, NULL);
1800 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1809 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1811 XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1817 EventProc (Widget widget, caddr_t unused, XEvent *event)
1819 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1820 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1825 * event handler for redrawing the board
1829 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1831 DrawPosition(True, NULL);
1837 /* Disable all user input other than deleting the window */
1838 static int frozen = 0;
1844 /* Grab by a widget that doesn't accept input */
1845 gtk_grab_add(optList[W_MESSG].handle);
1849 /* Undo a FreezeUI */
1853 if (!frozen) return;
1854 gtk_grab_remove(optList[W_MESSG].handle);
1861 static int oldPausing = FALSE;
1862 static GameMode oldmode = (GameMode) -1;
1864 if (!boardWidget) return;
1866 if (pausing != oldPausing) {
1867 oldPausing = pausing;
1868 MarkMenuItem("Mode.Pause", pausing);
1870 if (appData.showButtonBar) {
1871 /* Always toggle, don't set. Previous code messes up when
1872 invoked while the button is pressed, as releasing it
1873 toggles the state again. */
1875 gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1876 gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1880 wname = ModeToWidgetName(oldmode);
1881 if (wname != NULL) {
1882 MarkMenuItem(wname, False);
1884 wname = ModeToWidgetName(gameMode);
1885 if (wname != NULL) {
1886 MarkMenuItem(wname, True);
1889 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1891 /* Maybe all the enables should be handled here, not just this one */
1892 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1894 DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1899 * Button/menu procedures
1903 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1904 char *selected_fen_position=NULL;
1907 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1908 Atom *type_return, XtPointer *value_return,
1909 unsigned long *length_return, int *format_return)
1911 char *selection_tmp;
1913 // if (!selected_fen_position) return False; /* should never happen */
1914 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1915 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1916 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1919 if (f == NULL) return False;
1923 selection_tmp = XtMalloc(len + 1);
1924 count = fread(selection_tmp, 1, len, f);
1927 XtFree(selection_tmp);
1930 selection_tmp[len] = NULLCHAR;
1932 /* note: since no XtSelectionDoneProc was registered, Xt will
1933 * automatically call XtFree on the value returned. So have to
1934 * make a copy of it allocated with XtMalloc */
1935 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1936 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1939 *value_return=selection_tmp;
1940 *length_return=strlen(selection_tmp);
1941 *type_return=*target;
1942 *format_return = 8; /* bits per byte */
1944 } else if (*target == XA_TARGETS(xDisplay)) {
1945 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1946 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1947 targets_tmp[1] = XA_STRING;
1948 *value_return = targets_tmp;
1949 *type_return = XA_ATOM;
1952 // This code leads to a read of value_return out of bounds on 64-bit systems.
1953 // Other code which I have seen always sets *format_return to 32 independent of
1954 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1955 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1956 *format_return = 8 * sizeof(Atom);
1957 if (*format_return > 32) {
1958 *length_return *= *format_return / 32;
1959 *format_return = 32;
1962 *format_return = 32;
1971 /* note: when called from menu all parameters are NULL, so no clue what the
1972 * Widget which was clicked on was, or what the click event was
1975 CopySomething (char *src)
1978 selected_fen_position = src;
1980 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1981 * have a notion of a position that is selected but not copied.
1982 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1984 XtOwnSelection(menuBarWidget, XA_PRIMARY,
1986 SendPositionSelection,
1987 NULL/* lose_ownership_proc */ ,
1988 NULL/* transfer_done_proc */);
1989 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
1991 SendPositionSelection,
1992 NULL/* lose_ownership_proc */ ,
1993 NULL/* transfer_done_proc */);
1998 /* function called when the data to Paste is ready */
2000 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2001 Atom *type, XtPointer value, unsigned long *len, int *format)
2004 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2005 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2006 EditPositionPasteFEN(fenstr);
2011 /* called when Paste Position button is pressed,
2012 * all parameters will be NULL */
2014 PastePositionProc ()
2017 XtGetSelectionValue(menuBarWidget,
2018 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2019 /* (XtSelectionCallbackProc) */ PastePositionCB,
2020 NULL, /* client_data passed to PastePositionCB */
2022 /* better to use the time field from the event that triggered the
2023 * call to this function, but that isn't trivial to get
2032 /* note: when called from menu all parameters are NULL, so no clue what the
2033 * Widget which was clicked on was, or what the click event was
2035 /* function called when the data to Paste is ready */
2037 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2038 Atom *type, XtPointer value, unsigned long *len, int *format)
2041 if (value == NULL || *len == 0) {
2042 return; /* nothing had been selected to copy */
2044 f = fopen(gamePasteFilename, "w");
2046 DisplayError(_("Can't open temp file"), errno);
2049 fwrite(value, 1, *len, f);
2052 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
2056 /* called when Paste Game button is pressed,
2057 * all parameters will be NULL */
2062 XtGetSelectionValue(menuBarWidget,
2063 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2064 /* (XtSelectionCallbackProc) */ PasteGameCB,
2065 NULL, /* client_data passed to PasteGameCB */
2067 /* better to use the time field from the event that triggered the
2068 * call to this function, but that isn't trivial to get
2079 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2085 void MoveTypeInProc(eventkey)
2086 GdkEventKey *eventkey;
2090 // ingnore if ctrl or alt is pressed
2091 if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
2095 buf[0]=eventkey->keyval;
2103 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2105 if (!TempBackwardActive) {
2106 TempBackwardActive = True;
2112 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2114 /* Check to see if triggered by a key release event for a repeating key.
2115 * If so the next queued event will be a key press of the same key at the same time */
2116 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2118 XPeekEvent(xDisplay, &next);
2119 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2120 next.xkey.keycode == event->xkey.keycode)
2124 TempBackwardActive = False;
2128 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2129 { // called as key binding
2132 if (nprms && *nprms > 0)
2136 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2143 { // called from menu
2145 ManInner(NULL, NULL, NULL, NULL);
2150 SetWindowTitle (char *text, char *title, char *icon)
2155 if (appData.titleInWindow) {
2157 XtSetArg(args[i], XtNlabel, text); i++;
2158 XtSetValues(titleWidget, args, i);
2161 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
2162 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
2163 XtSetValues(shellWidget, args, i);
2164 XSync(xDisplay, False);
2166 if (appData.titleInWindow) {
2167 SetWidgetLabel(titleWidget, text);
2169 gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
2174 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2180 DisplayIcsInteractionTitle (String message)
2183 if (oldICSInteractionTitle == NULL) {
2184 /* Magic to find the old window title, adapted from vim */
2185 char *wina = getenv("WINDOWID");
2187 Window win = (Window) atoi(wina);
2188 Window root, parent, *children;
2189 unsigned int nchildren;
2190 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2192 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2193 if (!XQueryTree(xDisplay, win, &root, &parent,
2194 &children, &nchildren)) break;
2195 if (children) XFree((void *)children);
2196 if (parent == root || parent == 0) break;
2199 XSetErrorHandler(oldHandler);
2201 if (oldICSInteractionTitle == NULL) {
2202 oldICSInteractionTitle = "xterm";
2205 printf("\033]0;%s\007", message);
2212 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2214 GtkWidget *w = (GtkWidget *) opt->handle;
2220 strcpy(bgcolor, "black");
2221 strcpy(fgcolor, "white");
2223 strcpy(bgcolor, "white");
2224 strcpy(fgcolor, "black");
2227 appData.lowTimeWarning &&
2228 (timer / 1000) < appData.icsAlarmTime) {
2229 strcpy(fgcolor, appData.lowTimeWarningColor);
2232 if (appData.clockMode) {
2233 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
2234 bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2236 markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s </span>",
2237 bgcolor, fgcolor, color);
2239 gtk_label_set_markup(GTK_LABEL(w), markup);
2244 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2248 SetClockIcon (int color)
2252 Pixmap pm = *clockIcons[color];
2253 if (iconPixmap != pm) {
2255 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2256 XtSetValues(shellWidget, args, 1);
2261 #define INPUT_SOURCE_BUF_SIZE 8192
2270 char buf[INPUT_SOURCE_BUF_SIZE];
2275 DoInputCallback(io, cond, data)
2280 /* read input from one of the input source (for example a chess program, ICS, etc).
2281 * and call a function that will handle the input
2288 /* All information (callback function, file descriptor, etc) is
2289 * saved in an InputSource structure
2291 InputSource *is = (InputSource *) data;
2293 if (is->lineByLine) {
2294 count = read(is->fd, is->unused,
2295 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2297 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2300 is->unused += count;
2302 /* break input into lines and call the callback function on each
2305 while (p < is->unused) {
2306 q = memchr(p, '\n', is->unused - p);
2307 if (q == NULL) break;
2309 (is->func)(is, is->closure, p, q - p, 0);
2312 /* remember not yet used part of the buffer */
2314 while (p < is->unused) {
2319 /* read maximum length of input buffer and send the whole buffer
2320 * to the callback function
2322 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2327 (is->func)(is, is->closure, is->buf, count, error);
2329 return True; // Must return true or the watch will be removed
2332 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
2339 GIOChannel *channel;
2340 ChildProc *cp = (ChildProc *) pr;
2342 is = (InputSource *) calloc(1, sizeof(InputSource));
2343 is->lineByLine = lineByLine;
2347 is->fd = fileno(stdin);
2349 is->kind = cp->kind;
2350 is->fd = cp->fdFrom;
2353 is->unused = is->buf;
2357 /* GTK-TODO: will this work on windows?*/
2359 channel = g_io_channel_unix_new(is->fd);
2360 g_io_channel_set_close_on_unref (channel, TRUE);
2361 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
2363 is->closure = closure;
2364 return (InputSourceRef) is;
2369 RemoveInputSource(isr)
2372 InputSource *is = (InputSource *) isr;
2374 if (is->sid == 0) return;
2375 g_source_remove(is->sid);
2382 static Boolean frameWaiting;
2385 FrameAlarm (int sig)
2387 frameWaiting = False;
2388 /* In case System-V style signals. Needed?? */
2389 signal(SIGALRM, FrameAlarm);
2393 FrameDelay (int time)
2395 struct itimerval delay;
2398 frameWaiting = True;
2399 signal(SIGALRM, FrameAlarm);
2400 delay.it_interval.tv_sec =
2401 delay.it_value.tv_sec = time / 1000;
2402 delay.it_interval.tv_usec =
2403 delay.it_value.tv_usec = (time % 1000) * 1000;
2404 setitimer(ITIMER_REAL, &delay, NULL);
2405 while (frameWaiting) pause();
2406 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2407 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2408 setitimer(ITIMER_REAL, &delay, NULL);
2415 FrameDelay (int time)
2418 XSync(xDisplay, False);
2420 // gtk_main_iteration_do(False);
2423 usleep(time * 1000);
2429 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2431 char buf[MSG_SIZ], *logoName = buf;
2432 if(appData.logo[n][0]) {
2433 logoName = appData.logo[n];
2434 } else if(appData.autoLogo) {
2435 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2436 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2437 } else if(appData.directory[n] && appData.directory[n][0]) {
2438 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2442 { ASSIGN(cps->programLogo, logoName); }
2446 UpdateLogos (int displ)
2448 if(optList[W_WHITE-1].handle == NULL) return;
2449 LoadLogo(&first, 0, 0);
2450 LoadLogo(&second, 1, appData.icsActive);
2451 if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2455 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2466 GtkFileFilter *gtkfilter;
2467 GtkFileFilter *gtkfilter_all;
2469 char fileext[10] = "";
2470 char *result = NULL;
2473 /* make a copy of the filter string, so that strtok can work with it*/
2474 cp = strndup(filter,strlen(filter));
2476 /* add filters for file extensions */
2477 gtkfilter = gtk_file_filter_new();
2478 gtkfilter_all = gtk_file_filter_new();
2480 /* one filter to show everything */
2481 gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2482 gtk_file_filter_set_name (gtkfilter_all, "All Files");
2484 /* add filter if present */
2485 result = strtok(cp, space);
2486 while( result != NULL ) {
2487 snprintf(fileext,10,"*%s",result);
2488 result = strtok( NULL, space );
2489 gtk_file_filter_add_pattern(gtkfilter, fileext);
2492 /* second filter to only show what's useful */
2493 gtk_file_filter_set_name (gtkfilter,filter);
2495 if (openMode[0] == 'r')
2497 dialog = gtk_file_chooser_dialog_new (label,
2499 GTK_FILE_CHOOSER_ACTION_OPEN,
2500 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2501 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2506 dialog = gtk_file_chooser_dialog_new (label,
2508 GTK_FILE_CHOOSER_ACTION_SAVE,
2509 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2510 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2512 /* add filename suggestions */
2513 if (strlen(def) > 0 )
2514 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2516 //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2520 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2521 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2522 /* activate filter */
2523 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2525 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2530 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2533 f = fopen(filename, openMode);
2536 DisplayError(_("Failed to open file"), errno);
2540 /* TODO add indec */
2542 ASSIGN(*name, filename);
2543 ScheduleDelayedEvent(DelayedLoad, 50);
2548 gtk_widget_destroy (dialog);