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)
203 edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");
204 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
205 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
208 // cloned from CopyPositionProc. Abuse selected_fen_position to hold selection
210 Boolean SendPositionSelection(Widget w, Atom *selection, Atom *target,
211 Atom *type_return, XtPointer *value_return,
212 unsigned long *length_return, int *format_return); // from xboard.c
213 void SetFocus(Widget w, XtPointer data, XEvent *event, Boolean *b); // from xoptions.c
215 char memoTranslations[] =
216 ":Ctrl<Key>c: CopyMemoProc() \n \
217 <Btn3Motion>: HandlePV() \n \
218 <Btn3Down>: select-start() SelectPV() \n \
219 <Btn3Up>: extend-end() StopPV() \n";
222 SelectPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
223 { // [HGM] pv: translate click to PV line, and load it for display
226 XawTextPosition index, dummy;
230 x = event->xmotion.x; y = event->xmotion.y;
231 currentPV = (w == outputField[1][nMemo]);
232 XawTextGetSelectionPos(w, &index, &dummy);
233 XtSetArg(arg, XtNstring, &val);
234 XtGetValues(w, &arg, 1);
235 if(LoadMultiPV(x, y, val, index, &start, &end)) {
236 XawTextSetSelection( outputField[currentPV][nMemo], start, end );
237 highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
242 StopPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
243 { // [HGM] pv: on right-button release, stop displaying PV
244 XawTextUnsetSelection( w );
245 highTextStart[currentPV] = highTextEnd[currentPV] = 0;
250 MemoCB(Widget w, XtPointer client_data, Atom *selection,
251 Atom *type, XtPointer value, unsigned long *len, int *format)
253 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
254 selected_fen_position = value;
255 selected_fen_position[*len]='\0'; /* normally this string is terminated, but be safe */
256 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
258 SendPositionSelection,
259 NULL/* lose_ownership_proc */ ,
260 NULL/* transfer_done_proc */);
263 void CopyMemoProc(w, event, prms, nprms)
269 if(appData.pasteSelection) return;
270 if (selected_fen_position) free(selected_fen_position);
271 XtGetSelectionValue(menuBarWidget,
272 XA_PRIMARY, XA_STRING,
273 /* (XtSelectionCallbackProc) */ MemoCB,
274 NULL, /* client_data passed to PastePositionCB */
276 /* better to use the time field from the event that triggered the
277 * call to this function, but that isn't trivial to get
283 // The following routines are mutated clones of the commentPopUp routines
285 void PositionControlSet(which, shell, form, bw_width)
291 Widget edit, NameWidget, ColorWidget, ModeWidget, MoveWidget, NodesWidget;
294 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
295 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
296 XtSetArg(args[j], XtNtop, XtChainTop); j++;
297 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
298 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
299 XtSetArg(args[j], XtNright, XtChainLeft); j++;
300 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
301 XtSetArg(args[j], XtNwidth, (XtArgVal) 17); j++;
302 outputField[which][nColorIcon] = ColorWidget =
303 XtCreateManagedWidget("Color", labelWidgetClass,
307 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
308 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyLeft); j++;
309 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ColorWidget); j++;
310 XtSetArg(args[j], XtNtop, XtChainTop); j++;
311 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
312 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
313 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
314 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width/2 - 57); j++;
315 outputField[which][nLabel] = NameWidget =
316 XtCreateManagedWidget("Engine", labelWidgetClass,
320 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
321 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
322 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) NameWidget); j++;
323 XtSetArg(args[j], XtNtop, XtChainTop); j++;
324 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
325 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
326 XtSetArg(args[j], XtNwidth, (XtArgVal) 20); j++;
327 outputField[which][nStateIcon] = ModeWidget =
328 XtCreateManagedWidget("Mode", labelWidgetClass,
332 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
333 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyLeft); j++;
334 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
335 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ModeWidget); j++;
336 XtSetArg(args[j], XtNtop, XtChainTop); j++;
337 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
338 XtSetArg(args[j], XtNright, XtChainRight); j++;
339 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
340 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width/2 - 102); j++;
341 outputField[which][nStateData] = MoveWidget =
342 XtCreateManagedWidget("Move", labelWidgetClass,
346 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
347 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyRight); j++;
348 XtSetArg(args[j], XtNlabel, (XtArgVal) _("NPS")); j++;
349 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) MoveWidget); j++;
350 XtSetArg(args[j], XtNtop, XtChainTop); j++;
351 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
352 XtSetArg(args[j], XtNleft, XtChainRight); j++;
353 XtSetArg(args[j], XtNright, XtChainRight); j++;
354 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
355 XtSetArg(args[j], XtNwidth, (XtArgVal) 100); j++;
356 outputField[which][nLabelNPS] = NodesWidget =
357 XtCreateManagedWidget("Nodes", labelWidgetClass,
360 // create "text" within "form"
363 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
364 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
366 XtSetArg(args[j], XtNstring, ""); j++;
367 XtSetArg(args[j], XtNdisplayCaret, False); j++;
368 XtSetArg(args[j], XtNtop, XtChainTop); j++;
369 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
370 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
371 XtSetArg(args[j], XtNright, XtChainRight); j++;
372 XtSetArg(args[j], XtNresizable, True); j++;
373 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
374 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
375 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
376 XtSetArg(args[j], XtNscrollHorizontal, XawtextScrollWhenNeeded); j++;
377 // XtSetArg(args[j], XtNautoFill, True); j++;
378 // XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
379 outputField[which][nMemo] = edit =
380 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
382 XtOverrideTranslations(edit, XtParseTranslationTable(memoTranslations));
383 XtAddEventHandler(edit, ButtonPressMask, False, SetFocus, (XtPointer) shell);
386 XtSetArg(args[j], XtNfromVert, ColorWidget); j++;
387 // XtSetArg(args[j], XtNresizable, (XtArgVal) True); j++;
388 XtSetValues(edit, args, j);
391 Widget EngineOutputCreate(name, text)
395 Widget shell, layout, form, form2;
396 Dimension bw_width, bw_height;
401 XtSetArg(args[j], XtNwidth, &bw_width); j++;
402 XtSetArg(args[j], XtNheight, &bw_height); j++;
403 XtGetValues(boardWidget, args, j);
405 // define form within layout within shell.
407 XtSetArg(args[j], XtNresizable, True); j++;
410 XtCreatePopupShell(name, topLevelShellWidgetClass,
412 XtCreatePopupShell(name, transientShellWidgetClass,
414 shellWidget, args, j);
416 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
417 layoutArgs, XtNumber(layoutArgs));
418 // divide window vertically into two equal parts, by creating two forms
420 XtCreateManagedWidget("form", formWidgetClass, layout,
421 formArgs, XtNumber(formArgs));
423 XtCreateManagedWidget("form2", formWidgetClass, layout,
424 formArgs, XtNumber(formArgs));
426 XtSetArg(args[j], XtNfromVert, (XtArgVal) form); j++;
427 XtSetValues(form2, args, j);
428 // make sure width is known in advance, for better placement of child widgets
430 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width-16); j++;
431 XtSetArg(args[j], XtNheight, (XtArgVal) bw_height/2); j++;
432 XtSetValues(shell, args, j);
434 // fill up both forms with control elements
435 PositionControlSet(0, shell, form, bw_width);
436 PositionControlSet(1, shell, form2, bw_width);
438 XtRealizeWidget(shell);
440 if(wpEngineOutput.width > 0) {
441 engineOutputW = wpEngineOutput.width;
442 engineOutputH = wpEngineOutput.height;
443 engineOutputX = wpEngineOutput.x;
444 engineOutputY = wpEngineOutput.y;
447 if (engineOutputX == -1) {
451 engineOutputH = bw_height/2;
452 engineOutputW = bw_width-16;
454 XSync(xDisplay, False);
456 /* This code seems to tickle an X bug if it is executed too soon
457 after xboard starts up. The coordinates get transformed as if
458 the main window was positioned at (0, 0).
460 XtTranslateCoords(shellWidget,
461 (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
462 &engineOutputX, &engineOutputY);
464 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
465 RootWindowOfScreen(XtScreen(shellWidget)),
466 (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
471 if (engineOutputY < 0) engineOutputY = 0; /*avoid positioning top offscreen*/
474 XtSetArg(args[j], XtNheight, engineOutputH); j++;
475 XtSetArg(args[j], XtNwidth, engineOutputW); j++;
476 XtSetArg(args[j], XtNx, engineOutputX); j++;
477 XtSetArg(args[j], XtNy, engineOutputY); j++;
478 XtSetValues(shell, args, j);
483 void ResizeWindowControls(mode)
489 Dimension ew_height, tmp;
490 Widget shell = engineOutputShell;
492 form1 = XtNameToWidget(shell, "*form");
493 form2 = XtNameToWidget(shell, "*form2");
496 XtSetArg(args[j], XtNheight, (XtArgVal) &ew_height); j++;
497 XtGetValues(form1, args, j);
499 XtSetArg(args[j], XtNheight, (XtArgVal) &tmp); j++;
500 XtGetValues(form2, args, j);
501 ew_height += tmp; // total height
505 XtSetArg(args[j], XtNheight, (XtArgVal) 5); j++;
506 XtSetValues(form2, args, j);
508 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height-5)); j++;
509 XtSetValues(form1, args, j);
512 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
513 XtSetValues(form1, args, j);
515 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
516 XtSetValues(form2, args, j);
526 static int needInit = TRUE;
527 static char *title = N_("Engine output"), *text = N_("This feature is experimental");
529 if (engineOutputShell == NULL) {
531 EngineOutputCreate(_(title), _(text));
532 XtRealizeWidget(engineOutputShell);
533 CatchDeleteWindow(engineOutputShell, "EngineOutputPopDown");
535 InitializeEngineOutput();
538 SetEngineColorIcon( 0 );
539 SetEngineColorIcon( 1 );
540 SetEngineState( 0, STATE_IDLE, "" );
541 SetEngineState( 1, STATE_IDLE, "" );
543 edit = XtNameToWidget(engineOutputShell, "*form.text");
545 XtSetArg(args[j], XtNstring, text); j++;
546 XtSetValues(edit, args, j);
548 XtSetArg(args[j], XtNiconName, (XtArgVal) _(title)); j++;
549 XtSetArg(args[j], XtNtitle, (XtArgVal) _(title)); j++;
550 XtSetValues(engineOutputShell, args, j);
553 XtPopup(engineOutputShell, XtGrabNone);
554 XSync(xDisplay, False);
557 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
558 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
561 engineOutputDialogUp = True;
562 ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
565 void EngineOutputPopDown()
570 if (!engineOutputDialogUp) return;
573 XtSetArg(args[j], XtNx, &engineOutputX); j++;
574 XtSetArg(args[j], XtNy, &engineOutputY); j++;
575 XtSetArg(args[j], XtNwidth, &engineOutputW); j++;
576 XtSetArg(args[j], XtNheight, &engineOutputH); j++;
577 XtGetValues(engineOutputShell, args, j);
578 wpEngineOutput.x = engineOutputX - 4;
579 wpEngineOutput.y = engineOutputY - 23;
580 wpEngineOutput.width = engineOutputW;
581 wpEngineOutput.height = engineOutputH;
582 XtPopdown(engineOutputShell);
583 XSync(xDisplay, False);
585 XtSetArg(args[j], XtNleftBitmap, None); j++;
586 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
589 engineOutputDialogUp = False;
590 ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
593 int EngineOutputIsUp()
595 return engineOutputDialogUp;
598 int EngineOutputDialogExists()
600 return engineOutputShell != NULL;
604 EngineOutputProc(w, event, prms, nprms)
610 if (engineOutputDialogUp) {
611 EngineOutputPopDown();