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 SetEngineOutputTitle(char *title)
173 XtSetArg(arg, XtNtitle, (XtArgVal) title);
174 XtSetValues(engineOutputShell, &arg, 1);
177 void InsertIntoMemo( int which, char * text, int where )
182 /* the backend adds \r\n, which is needed for winboard,
183 * for xboard we delete them again over here */
184 if(t.ptr = strchr(text, '\r')) *t.ptr = ' ';
186 t.ptr = text; t.firstPos = 0; t.length = strlen(text); t.format = XawFmt8Bit;
187 edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");
188 XawTextReplace(edit, where, where, &t);
189 if(where < highTextStart[which]) { // [HGM] multiPVdisplay: move highlighting
190 int len = strlen(text);
191 highTextStart[which] += len; highTextEnd[which] += len;
192 XawTextSetSelection( outputField[which][nMemo], highTextStart[which], highTextEnd[which] );
196 void SetIcon( int which, int field, int nIcon )
201 XtSetArg(arg, XtNleftBitmap, (XtArgVal) icons[nIcon]);
202 XtSetValues(outputField[which][field], &arg, 1);
206 void DoClearMemo(int which)
208 Widget edit = XtNameToWidget(engineOutputShell, which ? "*form2.text" : "*form.text");
210 // XtCallActionProc(edit, "select-all", NULL, NULL, 0);
211 // XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
212 XtSetArg(arg, XtNstring, ""); // clear without disturbing selection!
213 XtSetValues(edit, &arg, 1);
216 // cloned from CopyPositionProc. Abuse selected_fen_position to hold selection
218 Boolean SendPositionSelection(Widget w, Atom *selection, Atom *target,
219 Atom *type_return, XtPointer *value_return,
220 unsigned long *length_return, int *format_return); // from xboard.c
221 void SetFocus(Widget w, XtPointer data, XEvent *event, Boolean *b); // from xoptions.c
223 char memoTranslations[] =
224 ":Ctrl<Key>c: CopyMemoProc() \n \
225 <Btn3Motion>: HandlePV() \n \
226 Shift<Btn3Down>: select-start() SelectPV(1) \n \
227 Any<Btn3Down>: select-start() SelectPV(0) \n \
228 <Btn3Up>: extend-end() StopPV() \n";
231 SelectPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
232 { // [HGM] pv: translate click to PV line, and load it for display
235 XawTextPosition index, dummy;
239 x = event->xmotion.x; y = event->xmotion.y;
240 currentPV = (w == outputField[1][nMemo]);
241 XawTextGetSelectionPos(w, &index, &dummy);
242 XtSetArg(arg, XtNstring, &val);
243 XtGetValues(w, &arg, 1);
244 shiftKey = strcmp(params[0], "0");
245 if(LoadMultiPV(x, y, val, index, &start, &end)) {
246 XawTextSetSelection( outputField[currentPV][nMemo], start, end );
247 highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
252 StopPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
253 { // [HGM] pv: on right-button release, stop displaying PV
254 XawTextUnsetSelection( w );
255 highTextStart[currentPV] = highTextEnd[currentPV] = 0;
260 MemoCB(Widget w, XtPointer client_data, Atom *selection,
261 Atom *type, XtPointer value, unsigned long *len, int *format)
263 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
264 selected_fen_position = value;
265 selected_fen_position[*len]='\0'; /* normally this string is terminated, but be safe */
266 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
268 SendPositionSelection,
269 NULL/* lose_ownership_proc */ ,
270 NULL/* transfer_done_proc */);
273 void CopyMemoProc(w, event, prms, nprms)
279 if(appData.pasteSelection) return;
280 if (selected_fen_position) free(selected_fen_position);
281 XtGetSelectionValue(menuBarWidget,
282 XA_PRIMARY, XA_STRING,
283 /* (XtSelectionCallbackProc) */ MemoCB,
284 NULL, /* client_data passed to PastePositionCB */
286 /* better to use the time field from the event that triggered the
287 * call to this function, but that isn't trivial to get
293 // The following routines are mutated clones of the commentPopUp routines
295 void PositionControlSet(which, shell, form, bw_width)
301 Widget edit, NameWidget, ColorWidget, ModeWidget, MoveWidget, NodesWidget;
304 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
305 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
306 XtSetArg(args[j], XtNtop, XtChainTop); j++;
307 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
308 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
309 XtSetArg(args[j], XtNright, XtChainLeft); j++;
310 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
311 XtSetArg(args[j], XtNwidth, (XtArgVal) 17); j++;
312 outputField[which][nColorIcon] = ColorWidget =
313 XtCreateManagedWidget("Color", labelWidgetClass,
317 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
318 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyLeft); j++;
319 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ColorWidget); j++;
320 XtSetArg(args[j], XtNtop, XtChainTop); j++;
321 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
322 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
323 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
324 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width/2 - 57); j++;
325 outputField[which][nLabel] = NameWidget =
326 XtCreateManagedWidget("Engine", labelWidgetClass,
330 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
331 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
332 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) NameWidget); j++;
333 XtSetArg(args[j], XtNtop, XtChainTop); j++;
334 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
335 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
336 XtSetArg(args[j], XtNwidth, (XtArgVal) 20); j++;
337 outputField[which][nStateIcon] = ModeWidget =
338 XtCreateManagedWidget("Mode", labelWidgetClass,
342 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
343 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyLeft); j++;
344 XtSetArg(args[j], XtNlabel, (XtArgVal) ""); j++;
345 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) ModeWidget); j++;
346 XtSetArg(args[j], XtNtop, XtChainTop); j++;
347 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
348 XtSetArg(args[j], XtNright, XtChainRight); j++;
349 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
350 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width/2 - 102); j++;
351 outputField[which][nStateData] = MoveWidget =
352 XtCreateManagedWidget("Move", labelWidgetClass,
356 XtSetArg(args[j], XtNborderWidth, (XtArgVal) 0); j++;
357 XtSetArg(args[j], XtNjustify, (XtArgVal) XtJustifyRight); j++;
358 XtSetArg(args[j], XtNlabel, (XtArgVal) _("NPS")); j++;
359 XtSetArg(args[j], XtNfromHoriz, (XtArgVal) MoveWidget); j++;
360 XtSetArg(args[j], XtNtop, XtChainTop); j++;
361 XtSetArg(args[j], XtNbottom, XtChainTop); j++;
362 XtSetArg(args[j], XtNleft, XtChainRight); j++;
363 XtSetArg(args[j], XtNright, XtChainRight); j++;
364 XtSetArg(args[j], XtNheight, (XtArgVal) 16); j++;
365 XtSetArg(args[j], XtNwidth, (XtArgVal) 100); j++;
366 outputField[which][nLabelNPS] = NodesWidget =
367 XtCreateManagedWidget("Nodes", labelWidgetClass,
370 // create "text" within "form"
373 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
374 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
376 XtSetArg(args[j], XtNstring, ""); j++;
377 XtSetArg(args[j], XtNdisplayCaret, False); j++;
378 XtSetArg(args[j], XtNtop, XtChainTop); j++;
379 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
380 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
381 XtSetArg(args[j], XtNright, XtChainRight); j++;
382 XtSetArg(args[j], XtNresizable, True); j++;
383 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
384 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
385 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
386 XtSetArg(args[j], XtNscrollHorizontal, XawtextScrollWhenNeeded); j++;
387 // XtSetArg(args[j], XtNautoFill, True); j++;
388 // XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
389 outputField[which][nMemo] = edit =
390 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
392 XtOverrideTranslations(edit, XtParseTranslationTable(memoTranslations));
393 XtAddEventHandler(edit, ButtonPressMask, False, SetFocus, (XtPointer) shell);
396 XtSetArg(args[j], XtNfromVert, ColorWidget); j++;
397 // XtSetArg(args[j], XtNresizable, (XtArgVal) True); j++;
398 XtSetValues(edit, args, j);
401 Widget EngineOutputCreate(name, text)
405 Widget shell, layout, form, form2;
406 Dimension bw_width, bw_height;
411 XtSetArg(args[j], XtNwidth, &bw_width); j++;
412 XtSetArg(args[j], XtNheight, &bw_height); j++;
413 XtGetValues(boardWidget, args, j);
415 // define form within layout within shell.
417 XtSetArg(args[j], XtNresizable, True); j++;
420 XtCreatePopupShell(name, topLevelShellWidgetClass,
422 XtCreatePopupShell(name, transientShellWidgetClass,
424 shellWidget, args, j);
426 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
427 layoutArgs, XtNumber(layoutArgs));
428 // divide window vertically into two equal parts, by creating two forms
430 XtCreateManagedWidget("form", formWidgetClass, layout,
431 formArgs, XtNumber(formArgs));
433 XtCreateManagedWidget("form2", formWidgetClass, layout,
434 formArgs, XtNumber(formArgs));
436 XtSetArg(args[j], XtNfromVert, (XtArgVal) form); j++;
437 XtSetValues(form2, args, j);
438 // make sure width is known in advance, for better placement of child widgets
440 XtSetArg(args[j], XtNwidth, (XtArgVal) bw_width-16); j++;
441 XtSetArg(args[j], XtNheight, (XtArgVal) bw_height/2); j++;
442 XtSetValues(shell, args, j);
444 // fill up both forms with control elements
445 PositionControlSet(0, shell, form, bw_width);
446 PositionControlSet(1, shell, form2, bw_width);
448 XtRealizeWidget(shell);
450 if(wpEngineOutput.width > 0) {
451 engineOutputW = wpEngineOutput.width;
452 engineOutputH = wpEngineOutput.height;
453 engineOutputX = wpEngineOutput.x;
454 engineOutputY = wpEngineOutput.y;
457 if (engineOutputX == -1) {
461 engineOutputH = bw_height/2;
462 engineOutputW = bw_width-16;
464 XSync(xDisplay, False);
466 /* This code seems to tickle an X bug if it is executed too soon
467 after xboard starts up. The coordinates get transformed as if
468 the main window was positioned at (0, 0).
470 XtTranslateCoords(shellWidget,
471 (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
472 &engineOutputX, &engineOutputY);
474 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
475 RootWindowOfScreen(XtScreen(shellWidget)),
476 (bw_width - engineOutputW) / 2, 0 - engineOutputH / 2,
481 if (engineOutputY < 0) engineOutputY = 0; /*avoid positioning top offscreen*/
484 XtSetArg(args[j], XtNheight, engineOutputH); j++;
485 XtSetArg(args[j], XtNwidth, engineOutputW); j++;
486 XtSetArg(args[j], XtNx, engineOutputX); j++;
487 XtSetArg(args[j], XtNy, engineOutputY); j++;
488 XtSetValues(shell, args, j);
493 void ResizeWindowControls(mode)
499 Dimension ew_height, tmp;
500 Widget shell = engineOutputShell;
502 form1 = XtNameToWidget(shell, "*form");
503 form2 = XtNameToWidget(shell, "*form2");
506 XtSetArg(args[j], XtNheight, (XtArgVal) &ew_height); j++;
507 XtGetValues(form1, args, j);
509 XtSetArg(args[j], XtNheight, (XtArgVal) &tmp); j++;
510 XtGetValues(form2, args, j);
511 ew_height += tmp; // total height
515 XtSetArg(args[j], XtNheight, (XtArgVal) 5); j++;
516 XtSetValues(form2, args, j);
518 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height-5)); j++;
519 XtSetValues(form1, args, j);
522 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
523 XtSetValues(form1, args, j);
525 XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
526 XtSetValues(form2, args, j);
536 static int needInit = TRUE;
537 static char *title = N_("Engine output"), *text = N_("This feature is experimental");
539 if (engineOutputShell == NULL) {
541 EngineOutputCreate(_(title), _(text));
542 XtRealizeWidget(engineOutputShell);
543 CatchDeleteWindow(engineOutputShell, "EngineOutputPopDown");
545 InitializeEngineOutput();
548 SetEngineColorIcon( 0 );
549 SetEngineColorIcon( 1 );
550 SetEngineState( 0, STATE_IDLE, "" );
551 SetEngineState( 1, STATE_IDLE, "" );
553 edit = XtNameToWidget(engineOutputShell, "*form.text");
555 XtSetArg(args[j], XtNstring, text); j++;
556 XtSetValues(edit, args, j);
558 XtSetArg(args[j], XtNiconName, (XtArgVal) _(title)); j++;
559 XtSetArg(args[j], XtNtitle, (XtArgVal) _(title)); j++;
560 XtSetValues(engineOutputShell, args, j);
563 XtPopup(engineOutputShell, XtGrabNone);
564 XSync(xDisplay, False);
567 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
568 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
571 engineOutputDialogUp = True;
572 ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
575 void EngineOutputPopDown()
580 if (!engineOutputDialogUp) return;
583 XtSetArg(args[j], XtNx, &engineOutputX); j++;
584 XtSetArg(args[j], XtNy, &engineOutputY); j++;
585 XtSetArg(args[j], XtNwidth, &engineOutputW); j++;
586 XtSetArg(args[j], XtNheight, &engineOutputH); j++;
587 XtGetValues(engineOutputShell, args, j);
588 wpEngineOutput.x = engineOutputX - 4;
589 wpEngineOutput.y = engineOutputY - 23;
590 wpEngineOutput.width = engineOutputW;
591 wpEngineOutput.height = engineOutputH;
592 XtPopdown(engineOutputShell);
593 XSync(xDisplay, False);
595 XtSetArg(args[j], XtNleftBitmap, None); j++;
596 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Engine Output"),
599 engineOutputDialogUp = False;
600 ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
603 int EngineOutputIsUp()
605 return engineOutputDialogUp;
608 int EngineOutputDialogExists()
610 return engineOutputShell != NULL;
614 EngineOutputProc(w, event, prms, nprms)
620 if (engineOutputDialogUp) {
621 EngineOutputPopDown();