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 Shift<Btn3Down>: select-start() SelectPV(1) \n \
219 Any<Btn3Down>: select-start() SelectPV(0) \n \
220 <Btn3Up>: extend-end() StopPV() \n";
223 SelectPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
224 { // [HGM] pv: translate click to PV line, and load it for display
227 XawTextPosition index, dummy;
231 x = event->xmotion.x; y = event->xmotion.y;
232 currentPV = (w == outputField[1][nMemo]);
233 XawTextGetSelectionPos(w, &index, &dummy);
234 XtSetArg(arg, XtNstring, &val);
235 XtGetValues(w, &arg, 1);
236 shiftKey = strcmp(params[0], "0");
237 if(LoadMultiPV(x, y, val, index, &start, &end)) {
238 XawTextSetSelection( outputField[currentPV][nMemo], start, end );
239 highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
244 StopPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
245 { // [HGM] pv: on right-button release, stop displaying PV
246 XawTextUnsetSelection( w );
247 highTextStart[currentPV] = highTextEnd[currentPV] = 0;
252 MemoCB(Widget w, XtPointer client_data, Atom *selection,
253 Atom *type, XtPointer value, unsigned long *len, int *format)
255 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
256 selected_fen_position = value;
257 selected_fen_position[*len]='\0'; /* normally this string is terminated, but be safe */
258 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
260 SendPositionSelection,
261 NULL/* lose_ownership_proc */ ,
262 NULL/* transfer_done_proc */);
265 void CopyMemoProc(w, event, prms, nprms)
271 if(appData.pasteSelection) return;
272 if (selected_fen_position) free(selected_fen_position);
273 XtGetSelectionValue(menuBarWidget,
274 XA_PRIMARY, XA_STRING,
275 /* (XtSelectionCallbackProc) */ MemoCB,
276 NULL, /* client_data passed to PastePositionCB */
278 /* better to use the time field from the event that triggered the
279 * call to this function, but that isn't trivial to get
285 // The following routines are mutated clones of the commentPopUp routines
287 void PositionControlSet(which, shell, form, bw_width)
293 Widget edit, NameWidget, ColorWidget, ModeWidget, MoveWidget, NodesWidget;
296 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
297 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
298 XtSetArg(args[j], XtNtop, XtChainTop); j++;
299 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
300 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
301 XtSetArg(args[j], XtNright, XtChainLeft); j++;
302 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
303 XtSetArg(args[j], XtNwidth, (XtArgVal) 17); j++;
304 outputField[which][nColorIcon] = ColorWidget =
305 XtCreateManagedWidget("Color", labelWidgetClass,
309 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
310 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyLeft); j++;
311 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ColorWidget); j++;
312 XtSetArg(args[j], XtNtop, XtChainTop); j++;
313 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
314 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
315 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
316 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width/2 - 57); j++;
317 outputField[which][nLabel] = NameWidget =
318 XtCreateManagedWidget("Engine", labelWidgetClass,
322 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
323 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
324 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) NameWidget); j++;
325 XtSetArg(args[j], XtNtop, XtChainTop); j++;
326 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
327 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
328 XtSetArg(args[j], XtNwidth, (XtArgVal) 20); j++;
329 outputField[which][nStateIcon] = ModeWidget =
330 XtCreateManagedWidget("Mode", labelWidgetClass,
334 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
335 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyLeft); j++;
336 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
337 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ModeWidget); j++;
338 XtSetArg(args[j], XtNtop, XtChainTop); j++;
339 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
340 XtSetArg(args[j], XtNright, XtChainRight); j++;
341 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
342 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width/2 - 102); j++;
343 outputField[which][nStateData] = MoveWidget =
344 XtCreateManagedWidget("Move", labelWidgetClass,
348 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
349 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyRight); j++;
350 XtSetArg(args[j], XtNlabel, (XtArgVal) _("NPS")); j++;
351 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) MoveWidget); j++;
352 XtSetArg(args[j], XtNtop, XtChainTop); j++;
353 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
354 XtSetArg(args[j], XtNleft, XtChainRight); j++;
355 XtSetArg(args[j], XtNright, XtChainRight); j++;
356 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
357 XtSetArg(args[j], XtNwidth, (XtArgVal) 100); j++;
358 outputField[which][nLabelNPS] = NodesWidget =
359 XtCreateManagedWidget("Nodes", labelWidgetClass,
362 // create "text" within "form"
365 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
366 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
368 XtSetArg(args[j], XtNstring, ""); j++;
369 XtSetArg(args[j], XtNdisplayCaret, False); j++;
370 XtSetArg(args[j], XtNtop, XtChainTop); j++;
371 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
372 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
373 XtSetArg(args[j], XtNright, XtChainRight); j++;
374 XtSetArg(args[j], XtNresizable, True); j++;
375 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
376 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
377 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
378 XtSetArg(args[j], XtNscrollHorizontal, XawtextScrollWhenNeeded); j++;
379 // XtSetArg(args[j], XtNautoFill, True); j++;
380 // XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
381 outputField[which][nMemo] = edit =
382 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
384 XtOverrideTranslations(edit, XtParseTranslationTable(memoTranslations));
385 XtAddEventHandler(edit, ButtonPressMask, False, SetFocus, (XtPointer) shell);
388 XtSetArg(args[j], XtNfromVert, ColorWidget); j++;
389 // XtSetArg(args[j], XtNresizable, (XtArgVal) True); j++;
390 XtSetValues(edit, args, j);
393 Widget EngineOutputCreate(name, text)
397 Widget shell, layout, form, form2;
398 Dimension bw_width, bw_height;
403 XtSetArg(args[j], XtNwidth, &bw_width); j++;
404 XtSetArg(args[j], XtNheight, &bw_height); j++;
405 XtGetValues(boardWidget, args, j);
407 // define form within layout within shell.
409 XtSetArg(args[j], XtNresizable, True); j++;
412 XtCreatePopupShell(name, topLevelShellWidgetClass,
414 XtCreatePopupShell(name, transientShellWidgetClass,
416 shellWidget, args, j);
418 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
419 layoutArgs, XtNumber(layoutArgs));
420 // divide window vertically into two equal parts, by creating two forms
422 XtCreateManagedWidget("form", formWidgetClass, layout,
423 formArgs, XtNumber(formArgs));
425 XtCreateManagedWidget("form2", formWidgetClass, layout,
426 formArgs, XtNumber(formArgs));
428 XtSetArg(args[j], XtNfromVert, (XtArgVal) form); j++;
429 XtSetValues(form2, args, j);
430 // make sure width is known in advance, for better placement of child widgets
432 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width-16); j++;
433 XtSetArg(args[j], XtNheight, (XtArgVal) bw_height/2); j++;
434 XtSetValues(shell, args, j);
436 // fill up both forms with control elements
437 PositionControlSet(0, shell, form, bw_width);
438 PositionControlSet(1, shell, form2, bw_width);
440 XtRealizeWidget(shell);
442 if(wpEngineOutput.width > 0) {
443 engineOutputW = wpEngineOutput.width;
444 engineOutputH = wpEngineOutput.height;
445 engineOutputX = wpEngineOutput.x;
446 engineOutputY = wpEngineOutput.y;
449 if (engineOutputX == -1) {
453 engineOutputH = bw_height/2;
454 engineOutputW = bw_width-16;
456 XSync(xDisplay, False);
458 /* This code seems to tickle an X bug if it is executed too soon
459 after xboard starts up. The coordinates get transformed as if
460 the main window was positioned at (0, 0).
462 XtTranslateCoords(shellWidget,
463 (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
464 &engineOutputX, &engineOutputY);
466 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
467 RootWindowOfScreen(XtScreen(shellWidget)),
468 (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
473 if (engineOutputY < 0) engineOutputY = 0; /*avoid positioning top offscreen*/
476 XtSetArg(args[j], XtNheight, engineOutputH); j++;
477 XtSetArg(args[j], XtNwidth, engineOutputW); j++;
478 XtSetArg(args[j], XtNx, engineOutputX); j++;
479 XtSetArg(args[j], XtNy, engineOutputY); j++;
480 XtSetValues(shell, args, j);
485 void ResizeWindowControls(mode)
491 Dimension ew_height, tmp;
492 Widget shell = engineOutputShell;
494 form1 = XtNameToWidget(shell, "*form");
495 form2 = XtNameToWidget(shell, "*form2");
498 XtSetArg(args[j], XtNheight, (XtArgVal) &ew_height); j++;
499 XtGetValues(form1, args, j);
501 XtSetArg(args[j], XtNheight, (XtArgVal) &tmp); j++;
502 XtGetValues(form2, args, j);
503 ew_height += tmp; // total height
507 XtSetArg(args[j], XtNheight, (XtArgVal) 5); j++;
508 XtSetValues(form2, args, j);
510 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height-5)); j++;
511 XtSetValues(form1, args, j);
514 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
515 XtSetValues(form1, args, j);
517 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
518 XtSetValues(form2, args, j);
528 static int needInit = TRUE;
529 static char *title = N_("Engine output"), *text = N_("This feature is experimental");
531 if (engineOutputShell == NULL) {
533 EngineOutputCreate(_(title), _(text));
534 XtRealizeWidget(engineOutputShell);
535 CatchDeleteWindow(engineOutputShell, "EngineOutputPopDown");
537 InitializeEngineOutput();
540 SetEngineColorIcon( 0 );
541 SetEngineColorIcon( 1 );
542 SetEngineState( 0, STATE_IDLE, "" );
543 SetEngineState( 1, STATE_IDLE, "" );
545 edit = XtNameToWidget(engineOutputShell, "*form.text");
547 XtSetArg(args[j], XtNstring, text); j++;
548 XtSetValues(edit, args, j);
550 XtSetArg(args[j], XtNiconName, (XtArgVal) _(title)); j++;
551 XtSetArg(args[j], XtNtitle, (XtArgVal) _(title)); j++;
552 XtSetValues(engineOutputShell, args, j);
555 XtPopup(engineOutputShell, XtGrabNone);
556 XSync(xDisplay, False);
559 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
560 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
563 engineOutputDialogUp = True;
564 ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
567 void EngineOutputPopDown()
572 if (!engineOutputDialogUp) return;
575 XtSetArg(args[j], XtNx, &engineOutputX); j++;
576 XtSetArg(args[j], XtNy, &engineOutputY); j++;
577 XtSetArg(args[j], XtNwidth, &engineOutputW); j++;
578 XtSetArg(args[j], XtNheight, &engineOutputH); j++;
579 XtGetValues(engineOutputShell, args, j);
580 wpEngineOutput.x = engineOutputX - 4;
581 wpEngineOutput.y = engineOutputY - 23;
582 wpEngineOutput.width = engineOutputW;
583 wpEngineOutput.height = engineOutputH;
584 XtPopdown(engineOutputShell);
585 XSync(xDisplay, False);
587 XtSetArg(args[j], XtNleftBitmap, None); j++;
588 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
591 engineOutputDialogUp = False;
592 ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
595 int EngineOutputIsUp()
597 return engineOutputDialogUp;
600 int EngineOutputDialogExists()
602 return engineOutputShell != NULL;
606 EngineOutputProc(w, event, prms, nprms)
612 if (engineOutputDialogUp) {
613 EngineOutputPopDown();