4 * Author: Alessandro Scotti (Dec 2005)
6 * Copyright 2005 Alessandro Scotti
8 * Enhancements Copyright 2009, 2010, 2011 Free Software Foundation, Inc.
10 * ------------------------------------------------------------------------
12 * GNU XBoard is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or (at
15 * your option) any later version.
17 * GNU XBoard is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see http://www.gnu.org/licenses/.
25 * ------------------------------------------------------------------------
26 ** See the file ChangeLog for a revision history. */
33 #include <sys/types.h>
38 #else /* not STDC_HEADERS */
39 extern char *getenv();
42 # else /* not HAVE_STRING_H */
44 # endif /* not HAVE_STRING_H */
45 #endif /* not STDC_HEADERS */
51 #include <X11/Intrinsic.h>
52 #include <X11/StringDefs.h>
53 #include <X11/Shell.h>
54 #include <X11/Xaw/Dialog.h>
55 #include <X11/Xaw/Form.h>
56 #include <X11/Xaw/List.h>
57 #include <X11/Xaw/Label.h>
58 #include <X11/Xaw/SimpleMenu.h>
59 #include <X11/Xaw/SmeBSB.h>
60 #include <X11/Xaw/SmeLine.h>
61 #include <X11/Xaw/Box.h>
62 #include <X11/Xaw/Paned.h>
63 #include <X11/Xaw/MenuButton.h>
64 #include <X11/cursorfont.h>
65 #include <X11/Xaw/Text.h>
66 #include <X11/Xaw/AsciiText.h>
67 #include <X11/Xaw/Viewport.h>
68 #include <X11/Xatom.h>
69 #include <X11/Xmu/Atoms.h>
75 #include "engineoutput.h"
79 # define _(s) gettext (s)
80 # define N_(s) gettext_noop (s)
88 // [HGM] pixmaps of some ICONS used in the engine-outut window
89 #include "pixmaps/WHITE_14.xpm"
90 #include "pixmaps/BLACK_14.xpm"
91 #include "pixmaps/CLEAR_14.xpm"
92 #include "pixmaps/UNKNOWN_14.xpm"
93 #include "pixmaps/THINKING_14.xpm"
94 #include "pixmaps/PONDER_14.xpm"
95 #include "pixmaps/ANALYZING_14.xpm"
103 Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
104 Widget outputField[2][7]; // [HGM] front-end array to translate output field to window handle
106 void EngineOutputPopDown();
107 void engineOutputPopUp();
108 int EngineOutputIsUp();
109 void SetEngineColorIcon( int which );
111 //extern WindowPlacement wpEngineOutput;
113 Position engineOutputX = -1, engineOutputY = -1;
114 Dimension engineOutputW, engineOutputH;
115 Widget engineOutputShell;
116 static int engineOutputDialogUp;
118 /* Module variables */
120 static int currentPV, highTextStart[2], highTextEnd[2];
135 //static void UpdateControls( EngineOutputData * ed );
137 void ReadIcon(char *pixData[], int iconNr)
141 if ((r=XpmCreatePixmapFromData(xDisplay, XtWindow(outputField[0][nColorIcon]),
144 NULL, NULL /*&attr*/)) != 0) {
145 fprintf(stderr, _("Error %d loading icon image\n"), r);
150 static void InitializeEngineOutput()
152 ReadIcon(WHITE_14, nColorWhite);
153 ReadIcon(BLACK_14, nColorBlack);
154 ReadIcon(UNKNOWN_14, nColorUnknown);
156 ReadIcon(CLEAR_14, nClear);
157 ReadIcon(PONDER_14, nPondering);
158 ReadIcon(THINK_14, nThinking);
159 ReadIcon(ANALYZE_14, nAnalyzing);
162 void DoSetWindowText(int which, int field, char *s_label)
166 XtSetArg(arg, XtNlabel, (XtArgVal) s_label);
167 XtSetValues(outputField[which][field], &arg, 1);
170 void InsertIntoMemo( int which, char * text, int where )
175 /* the backend adds \r\n, which is needed for winboard,
176 * for xboard we delete them again over here */
177 if(t.ptr = strchr(text, '\r')) *t.ptr = ' ';
179 t.ptr = text; t.firstPos = 0; t.length = strlen(text); t.format = XawFmt8Bit;
180 edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");
181 XawTextReplace(edit, where, where, &t);
182 if(where < highTextStart[which]) { // [HGM] multiPVdisplay: move highlighting
183 int len = strlen(text);
184 highTextStart[which] += len; highTextEnd[which] += len;
185 XawTextSetSelection( outputField[which][nMemo], highTextStart[which], highTextEnd[which] );
189 void SetIcon( int which, int field, int nIcon )
194 XtSetArg(arg, XtNleftBitmap, (XtArgVal) icons[nIcon]);
195 XtSetValues(outputField[which][field], &arg, 1);
199 void DoClearMemo(int which)
201 Widget edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");
203 // XtCallActionProc(edit, "select-all", NULL, NULL, 0);
204 // XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
205 XtSetArg(arg, XtNstring, ""); // clear without disturbing selection!
206 XtSetValues(edit, &arg, 1);
209 // cloned from CopyPositionProc. Abuse selected_fen_position to hold selection
211 Boolean SendPositionSelection(Widget w, Atom *selection, Atom *target,
212 Atom *type_return, XtPointer *value_return,
213 unsigned long *length_return, int *format_return); // from xboard.c
214 void SetFocus(Widget w, XtPointer data, XEvent *event, Boolean *b); // from xoptions.c
216 char memoTranslations[] =
217 ":Ctrl<Key>c: CopyMemoProc() \n \
218 <Btn3Motion>: HandlePV() \n \
219 Shift<Btn3Down>: select-start() SelectPV(1) \n \
220 Any<Btn3Down>: select-start() SelectPV(0) \n \
221 <Btn3Up>: extend-end() StopPV() \n";
224 SelectPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
225 { // [HGM] pv: translate click to PV line, and load it for display
228 XawTextPosition index, dummy;
232 x = event->xmotion.x; y = event->xmotion.y;
233 currentPV = (w == outputField[1][nMemo]);
234 XawTextGetSelectionPos(w, &index, &dummy);
235 XtSetArg(arg, XtNstring, &val);
236 XtGetValues(w, &arg, 1);
237 shiftKey = strcmp(params[0], "0");
238 if(LoadMultiPV(x, y, val, index, &start, &end)) {
239 XawTextSetSelection( outputField[currentPV][nMemo], start, end );
240 highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
245 StopPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
246 { // [HGM] pv: on right-button release, stop displaying PV
247 XawTextUnsetSelection( w );
248 highTextStart[currentPV] = highTextEnd[currentPV] = 0;
253 MemoCB(Widget w, XtPointer client_data, Atom *selection,
254 Atom *type, XtPointer value, unsigned long *len, int *format)
256 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
257 selected_fen_position = value;
258 selected_fen_position[*len]='\0'; /* normally this string is terminated, but be safe */
259 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
261 SendPositionSelection,
262 NULL/* lose_ownership_proc */ ,
263 NULL/* transfer_done_proc */);
266 void CopyMemoProc(w, event, prms, nprms)
272 if(appData.pasteSelection) return;
273 if (selected_fen_position) free(selected_fen_position);
274 XtGetSelectionValue(menuBarWidget,
275 XA_PRIMARY, XA_STRING,
276 /* (XtSelectionCallbackProc) */ MemoCB,
277 NULL, /* client_data passed to PastePositionCB */
279 /* better to use the time field from the event that triggered the
280 * call to this function, but that isn't trivial to get
286 // The following routines are mutated clones of the commentPopUp routines
288 void PositionControlSet(which, shell, form, bw_width)
294 Widget edit, NameWidget, ColorWidget, ModeWidget, MoveWidget, NodesWidget;
297 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
298 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
299 XtSetArg(args[j], XtNtop, XtChainTop); j++;
300 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
301 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
302 XtSetArg(args[j], XtNright, XtChainLeft); j++;
303 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
304 XtSetArg(args[j], XtNwidth, (XtArgVal) 17); j++;
305 outputField[which][nColorIcon] = ColorWidget =
306 XtCreateManagedWidget("Color", labelWidgetClass,
310 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
311 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyLeft); j++;
312 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ColorWidget); j++;
313 XtSetArg(args[j], XtNtop, XtChainTop); j++;
314 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
315 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
316 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
317 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width/2 - 57); j++;
318 outputField[which][nLabel] = NameWidget =
319 XtCreateManagedWidget("Engine", labelWidgetClass,
323 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
324 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
325 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) NameWidget); j++;
326 XtSetArg(args[j], XtNtop, XtChainTop); j++;
327 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
328 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
329 XtSetArg(args[j], XtNwidth, (XtArgVal) 20); j++;
330 outputField[which][nStateIcon] = ModeWidget =
331 XtCreateManagedWidget("Mode", labelWidgetClass,
335 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
336 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyLeft); j++;
337 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
338 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ModeWidget); j++;
339 XtSetArg(args[j], XtNtop, XtChainTop); j++;
340 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
341 XtSetArg(args[j], XtNright, XtChainRight); j++;
342 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
343 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width/2 - 102); j++;
344 outputField[which][nStateData] = MoveWidget =
345 XtCreateManagedWidget("Move", labelWidgetClass,
349 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
350 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyRight); j++;
351 XtSetArg(args[j], XtNlabel, (XtArgVal) _("NPS")); j++;
352 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) MoveWidget); j++;
353 XtSetArg(args[j], XtNtop, XtChainTop); j++;
354 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
355 XtSetArg(args[j], XtNleft, XtChainRight); j++;
356 XtSetArg(args[j], XtNright, XtChainRight); j++;
357 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
358 XtSetArg(args[j], XtNwidth, (XtArgVal) 100); j++;
359 outputField[which][nLabelNPS] = NodesWidget =
360 XtCreateManagedWidget("Nodes", labelWidgetClass,
363 // create "text" within "form"
366 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
367 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
369 XtSetArg(args[j], XtNstring, ""); j++;
370 XtSetArg(args[j], XtNdisplayCaret, False); j++;
371 XtSetArg(args[j], XtNtop, XtChainTop); j++;
372 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
373 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
374 XtSetArg(args[j], XtNright, XtChainRight); j++;
375 XtSetArg(args[j], XtNresizable, True); j++;
376 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
377 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
378 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
379 XtSetArg(args[j], XtNscrollHorizontal, XawtextScrollWhenNeeded); j++;
380 // XtSetArg(args[j], XtNautoFill, True); j++;
381 // XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
382 outputField[which][nMemo] = edit =
383 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
385 XtOverrideTranslations(edit, XtParseTranslationTable(memoTranslations));
386 XtAddEventHandler(edit, ButtonPressMask, False, SetFocus, (XtPointer) shell);
389 XtSetArg(args[j], XtNfromVert, ColorWidget); j++;
390 // XtSetArg(args[j], XtNresizable, (XtArgVal) True); j++;
391 XtSetValues(edit, args, j);
394 Widget EngineOutputCreate(name, text)
398 Widget shell, layout, form, form2;
399 Dimension bw_width, bw_height;
404 XtSetArg(args[j], XtNwidth, &bw_width); j++;
405 XtSetArg(args[j], XtNheight, &bw_height); j++;
406 XtGetValues(boardWidget, args, j);
408 // define form within layout within shell.
410 XtSetArg(args[j], XtNresizable, True); j++;
413 XtCreatePopupShell(name, topLevelShellWidgetClass,
415 XtCreatePopupShell(name, transientShellWidgetClass,
417 shellWidget, args, j);
419 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
420 layoutArgs, XtNumber(layoutArgs));
421 // divide window vertically into two equal parts, by creating two forms
423 XtCreateManagedWidget("form", formWidgetClass, layout,
424 formArgs, XtNumber(formArgs));
426 XtCreateManagedWidget("form2", formWidgetClass, layout,
427 formArgs, XtNumber(formArgs));
429 XtSetArg(args[j], XtNfromVert, (XtArgVal) form); j++;
430 XtSetValues(form2, args, j);
431 // make sure width is known in advance, for better placement of child widgets
433 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width-16); j++;
434 XtSetArg(args[j], XtNheight, (XtArgVal) bw_height/2); j++;
435 XtSetValues(shell, args, j);
437 // fill up both forms with control elements
438 PositionControlSet(0, shell, form, bw_width);
439 PositionControlSet(1, shell, form2, bw_width);
441 XtRealizeWidget(shell);
443 if(wpEngineOutput.width > 0) {
444 engineOutputW = wpEngineOutput.width;
445 engineOutputH = wpEngineOutput.height;
446 engineOutputX = wpEngineOutput.x;
447 engineOutputY = wpEngineOutput.y;
450 if (engineOutputX == -1) {
454 engineOutputH = bw_height/2;
455 engineOutputW = bw_width-16;
457 XSync(xDisplay, False);
459 /* This code seems to tickle an X bug if it is executed too soon
460 after xboard starts up. The coordinates get transformed as if
461 the main window was positioned at (0, 0).
463 XtTranslateCoords(shellWidget,
464 (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
465 &engineOutputX, &engineOutputY);
467 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
468 RootWindowOfScreen(XtScreen(shellWidget)),
469 (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
474 if (engineOutputY < 0) engineOutputY = 0; /*avoid positioning top offscreen*/
477 XtSetArg(args[j], XtNheight, engineOutputH); j++;
478 XtSetArg(args[j], XtNwidth, engineOutputW); j++;
479 XtSetArg(args[j], XtNx, engineOutputX); j++;
480 XtSetArg(args[j], XtNy, engineOutputY); j++;
481 XtSetValues(shell, args, j);
486 void ResizeWindowControls(mode)
492 Dimension ew_height, tmp;
493 Widget shell = engineOutputShell;
495 form1 = XtNameToWidget(shell, "*form");
496 form2 = XtNameToWidget(shell, "*form2");
499 XtSetArg(args[j], XtNheight, (XtArgVal) &ew_height); j++;
500 XtGetValues(form1, args, j);
502 XtSetArg(args[j], XtNheight, (XtArgVal) &tmp); j++;
503 XtGetValues(form2, args, j);
504 ew_height += tmp; // total height
508 XtSetArg(args[j], XtNheight, (XtArgVal) 5); j++;
509 XtSetValues(form2, args, j);
511 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height-5)); j++;
512 XtSetValues(form1, args, j);
515 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
516 XtSetValues(form1, args, j);
518 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
519 XtSetValues(form2, args, j);
529 static int needInit = TRUE;
530 static char *title = N_("Engine output"), *text = N_("This feature is experimental");
532 if (engineOutputShell == NULL) {
534 EngineOutputCreate(_(title), _(text));
535 XtRealizeWidget(engineOutputShell);
536 CatchDeleteWindow(engineOutputShell, "EngineOutputPopDown");
538 InitializeEngineOutput();
541 SetEngineColorIcon( 0 );
542 SetEngineColorIcon( 1 );
543 SetEngineState( 0, STATE_IDLE, "" );
544 SetEngineState( 1, STATE_IDLE, "" );
546 edit = XtNameToWidget(engineOutputShell, "*form.text");
548 XtSetArg(args[j], XtNstring, text); j++;
549 XtSetValues(edit, args, j);
551 XtSetArg(args[j], XtNiconName, (XtArgVal) _(title)); j++;
552 XtSetArg(args[j], XtNtitle, (XtArgVal) _(title)); j++;
553 XtSetValues(engineOutputShell, args, j);
556 XtPopup(engineOutputShell, XtGrabNone);
557 XSync(xDisplay, False);
560 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
561 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
564 engineOutputDialogUp = True;
565 ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
568 void EngineOutputPopDown()
573 if (!engineOutputDialogUp) return;
576 XtSetArg(args[j], XtNx, &engineOutputX); j++;
577 XtSetArg(args[j], XtNy, &engineOutputY); j++;
578 XtSetArg(args[j], XtNwidth, &engineOutputW); j++;
579 XtSetArg(args[j], XtNheight, &engineOutputH); j++;
580 XtGetValues(engineOutputShell, args, j);
581 wpEngineOutput.x = engineOutputX - 4;
582 wpEngineOutput.y = engineOutputY - 23;
583 wpEngineOutput.width = engineOutputW;
584 wpEngineOutput.height = engineOutputH;
585 XtPopdown(engineOutputShell);
586 XSync(xDisplay, False);
588 XtSetArg(args[j], XtNleftBitmap, None); j++;
589 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
592 engineOutputDialogUp = False;
593 ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
596 int EngineOutputIsUp()
598 return engineOutputDialogUp;
601 int EngineOutputDialogExists()
603 return engineOutputShell != NULL;
607 EngineOutputProc(w, event, prms, nprms)
613 if (engineOutputDialogUp) {
614 EngineOutputPopDown();