Give LoadListBox two extra parameters
[xboard.git] / xoptions.c
index 528ab3a..9d4f122 100644 (file)
@@ -47,30 +47,13 @@ extern char *getenv();
 #endif
 #include <stdint.h>
 
-#include <X11/Intrinsic.h>
-#include <X11/StringDefs.h>
-#include <X11/Shell.h>
-#include <X11/Xatom.h>
-#include <X11/Xaw/Dialog.h>
-#include <X11/Xaw/Form.h>
-#include <X11/Xaw/List.h>
-#include <X11/Xaw/Label.h>
-#include <X11/Xaw/SimpleMenu.h>
-#include <X11/Xaw/SmeBSB.h>
-#include <X11/Xaw/SmeLine.h>
-#include <X11/Xaw/Box.h>
-#include <X11/Xaw/Paned.h>
-#include <X11/Xaw/MenuButton.h>
-#include <X11/cursorfont.h>
-#include <X11/Xaw/Text.h>
-#include <X11/Xaw/AsciiText.h>
-#include <X11/Xaw/Viewport.h>
-#include <X11/Xaw/Toggle.h>
-#include <X11/Xaw/Scrollbar.h>
+#include <cairo/cairo.h>
+#include <cairo/cairo-xlib.h>
 
 #include "common.h"
 #include "backend.h"
 #include "xboard.h"
+#include "xboard2.h"
 #include "dialogs.h"
 #include "menus.h"
 #include "gettext.h"
@@ -91,6 +74,7 @@ static Option *currentOption;
 void
 UnCaret ()
 {
+#ifdef TODO_GTK
     Arg args[2];
 
     if(previous) {
@@ -98,11 +82,13 @@ UnCaret ()
        XtSetValues(previous, args, 1);
     }
     previous = NULL;
+#endif
 }
 
 void
 SetFocus (Widget w, XtPointer data, XEvent *event, Boolean *b)
 {
+#ifdef TODO_GTK
     Arg args[2];
     char *s;
     int j;
@@ -116,80 +102,123 @@ SetFocus (Widget w, XtPointer data, XEvent *event, Boolean *b)
     XtSetValues(w, args, j);
     XtSetKeyboardFocus((Widget) data, w);
     previous = w;
+#endif
 }
 
 void
 BoardFocus ()
 {
+#ifdef TODO_GTK
     XtSetKeyboardFocus(shellWidget, formWidget);
+#endif
 }
 
 //--------------------------- Engine-specific options menu ----------------------------------
 
 int dialogError;
-static Boolean browserUp;
 Option *dialogOptions[NrOfDialogs];
 
+#ifdef TODO_GTK
+static Arg layoutArgs[] = {
+    { XtNborderWidth, 0 },
+    { XtNdefaultDistance, 0 },
+};
+
+static Arg formArgs[] = {
+    { XtNborderWidth, 0 },
+    { XtNresizable, (XtArgVal) True },
+};
+#endif
+
+void
+MarkMenuItem (char *menuRef, int state)
+{
+    MenuItem *item = MenuNameToItem(menuRef);
+
+#ifdef TODO_GTK
+    if(item) {
+       Arg args[2];
+       XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
+       XtSetValues(item->handle, args, 1);
+    }
+#endif
+}
+
 void
 GetWidgetText (Option *opt, char **buf)
 {
+#ifdef TODO_GTK
     Arg arg;
     XtSetArg(arg, XtNstring, buf);
     XtGetValues(opt->handle, &arg, 1);
+#endif
 }
 
 void
 SetWidgetText (Option *opt, char *buf, int n)
 {
+#ifdef TODO_GTK
     Arg arg;
     XtSetArg(arg, XtNstring, buf);
     XtSetValues(opt->handle, &arg, 1);
     if(n >= 0) SetFocus(opt->handle, shells[n], NULL, False);
+#endif
 }
 
 void
 GetWidgetState (Option *opt, int *state)
 {
+#ifdef TODO_GTK
     Arg arg;
     XtSetArg(arg, XtNstate, state);
     XtGetValues(opt->handle, &arg, 1);
+#endif
 }
 
 void
 SetWidgetState (Option *opt, int state)
 {
+#ifdef TODO_GTK
     Arg arg;
     XtSetArg(arg, XtNstate, state);
     XtSetValues(opt->handle, &arg, 1);
+#endif
 }
 
 void
 SetWidgetLabel (Option *opt, char *buf)
 {
+#ifdef TODO_GTK
     Arg arg;
     XtSetArg(arg, XtNlabel, (XtArgVal) buf);
     XtSetValues(opt->handle, &arg, 1);
+#endif
 }
 
 void
 SetDialogTitle (DialogClass dlg, char *title)
 {
+#ifdef TODO_GTK
     Arg args[16];
     XtSetArg(args[0], XtNtitle, title);
     XtSetValues(shells[dlg], args, 1);
+#endif
 }
 
 void
-LoadListBox (Option *opt, char *emptyText)
+LoadListBox (Option *opt, char *emptyText, int n1, int n2)
 {
+#ifdef TODO_GTK
     static char *dummyList[2];
     dummyList[0] = emptyText; // empty listboxes tend to crash X, so display user-supplied warning string instead
     XawListChange(opt->handle, *(char*)opt->target ? opt->target : dummyList, 0, 0, True);
+#endif
 }
 
 int
 ReadScroll (Option *opt, float *top, float *bottom)
 {   // retreives fractions of top and bottom of thumb
+#ifdef TODO_GTK
     Arg args[16];
     Widget w = XtParent(opt->handle); // viewport
     Widget v = XtNameToWidget(w, "vertical");
@@ -200,12 +229,14 @@ ReadScroll (Option *opt, float *top, float *bottom)
     XtSetArg(args[j], XtNtopOfThumb, top); j++;
     XtGetValues(v, args, j);
     *bottom = *top + h;
+#endif
     return TRUE;
 }
 
 void
 SetScroll (Option *opt, float f)
 {   // sets top of thumb to given fraction
+#ifdef TODO_GTK
     static char *params[3] = { "", "Continuous", "Proportional" };
     static XEvent event;
     Widget w = XtParent(opt->handle); // viewport
@@ -216,52 +247,65 @@ SetScroll (Option *opt, float f)
     XtCallActionProc(v, "NotifyThumb", &event, params, 0);
 //    XtCallActionProc(v, "NotifyScroll", &event, params+2, 1);
     XtCallActionProc(v, "EndScroll", &event, params, 0);
+#endif
 }
 
 void
 HighlightListBoxItem (Option *opt, int nr)
 {
+#ifdef TODO_GTK
     XawListHighlight(opt->handle, nr);
+#endif
 }
 
 void
 HighlightWithScroll (Option *opt, int sel, int max)
 {
+#ifdef TODO_GTK
     float top, bottom, f, g;
     HighlightListBoxItem(opt, sel);
     if(!ReadScroll(opt, &top, &bottom)) return; // no scroll bar
-    bottom = bottom*max - 1.;
+    bottom = bottom*max - 1.f;
     f = g = top;
     top *= max;
-    if(sel > (top + 3*bottom)/4) f = (sel - 0.75*(bottom-top))/max; else
-    if(sel < (3*top + bottom)/4) f = (sel - 0.25*(bottom-top))/max;
-    if(f < 0.) f = 0.; if(f + 1./max > 1.) f = 1. - 1./max;
+    if(sel > (top + 3*bottom)/4) f = (sel - 0.75f*(bottom-top))/max; else
+    if(sel < (3*top + bottom)/4) f = (sel - 0.25f*(bottom-top))/max;
+    if(f < 0.f) f = 0.; if(f + 1.f/max > 1.f) f = 1. - 1./max;
     if(f != g) SetScroll(opt, f);
+#endif
 }
 
 int
 SelectedListBoxItem (Option *opt)
 {
+#ifdef TODO_GTK
     XawListReturnStruct *rs;
     rs = XawListShowCurrent(opt->handle);
     return rs->list_index;
+#else
+    return 0;
+#endif
 }
 
 void
 FocusOnWidget (Option *opt, DialogClass dlg)
 {
     UnCaret();
+#ifdef TODO_GTK
     XtSetKeyboardFocus(shells[dlg], opt->handle);
+#endif
 }
 
 void
 SetIconName (DialogClass dlg, char *name)
 {
+#ifdef TODO_GTK
        Arg args[16];
        int j = 0;
        XtSetArg(args[j], XtNiconName, (XtArgVal) name);  j++;
 //     XtSetArg(args[j], XtNtitle, (XtArgVal) name);  j++;
        XtSetValues(shells[dlg], args, j);
+#endif
 }
 
 static void
@@ -279,6 +323,7 @@ CheckCallback (Widget ww, XtPointer client_data, XEvent *event, Boolean *b)
 static void
 SpinCallback (Widget w, XtPointer client_data, XtPointer call_data)
 {
+#ifdef TODO_GTK
     String name, val;
     Arg args[16];
     char buf[MSG_SIZ], *p;
@@ -296,16 +341,7 @@ SpinCallback (Widget w, XtPointer client_data, XtPointer call_data)
        for(r = ""; *q; q++) if(*q == '.') r = q; else if(*q == '/') r = ""; // last dot after last slash
        if(!strcmp(r, "") && !currentCps && opt->type == FileName && opt->textValue)
                r = opt->textValue;
-       browserUp = True;
-       if(XsraSelFile(shells[TransientDlg], opt->name, NULL, NULL, "", "", r,
-                                 opt->type == PathName ? "p" : "f", NULL, &p)) {
-               int len = strlen(p);
-               if(len && p[len-1] == '/') p[len-1] = NULLCHAR;
-               XtSetArg(args[0], XtNstring, p);
-               XtSetValues(opt->handle, args, 1);
-       }
-       browserUp = False;
-       SetFocus(opt->handle, shells[TransientDlg], (XEvent*) NULL, False);
+       Browse(data>>8, opt->name, NULL, r, opt->type == PathName, "", &p, (FILE**) opt);
        return;
     } else
     if (strcmp(name, "+") == 0) {
@@ -316,11 +352,13 @@ SpinCallback (Widget w, XtPointer client_data, XtPointer call_data)
     } else return;
     snprintf(buf, MSG_SIZ,  "%d", j);
     SetWidgetText(opt, buf, TransientDlg);
+#endif
 }
 
 static void
 ComboSelect (Widget w, caddr_t addr, caddr_t index) // callback for all combo items
 {
+#ifdef TODO_GTK
     Arg args[16];
     Option *opt = dialogOptions[((intptr_t)addr)>>24]; // applicable option list
     int i = ((intptr_t)addr)>>16 & 255; // option number
@@ -328,7 +366,7 @@ ComboSelect (Widget w, caddr_t addr, caddr_t index) // callback for all combo it
 
     values[i] = j; // store selected value in Option struct, for retrieval at OK
 
-    if(opt[i].type == Graph || opt[i].min & COMBO_CALLBACK && !currentCps) {
+    if(opt[i].type == Graph || opt[i].min & COMBO_CALLBACK && (!currentCps || shellUp[BrowserDlg])) {
        ((ButtonCallback*) opt[i].target)(i);
        return;
     }
@@ -339,11 +377,13 @@ ComboSelect (Widget w, caddr_t addr, caddr_t index) // callback for all combo it
       XtSetArg(args[0], XtNlabel, _(((char**)opt[i].choice)[j]));
 
     XtSetValues(opt[i].handle, args, 1);
+#endif
 }
 
 Widget
 CreateMenuItem (Widget menu, char *msg, XtCallbackProc CB, int n)
 {
+#ifdef TODO_GTK
     int j=0;
     Widget entry;
     Arg args[16];
@@ -354,12 +394,16 @@ CreateMenuItem (Widget menu, char *msg, XtCallbackProc CB, int n)
     entry = XtCreateManagedWidget("item", smeBSBObjectClass, menu, args, j+1);
     XtAddCallback(entry, XtNcallback, CB, (caddr_t)(intptr_t) n);
     return entry;
+#else
+    return NULL;
+#endif
 }
 
 static Widget
 CreateComboPopup (Widget parent, Option *opt, int n, int fromList, int def)
 {   // fromList determines if the item texts are taken from a list of strings, or from a menu table
-    int i, j;
+#ifdef TODO_GTK
+    int i;
     Widget menu, entry;
     Arg arg;
     MenuItem *mb = (MenuItem *) opt->choice;
@@ -370,7 +414,7 @@ CreateComboPopup (Widget parent, Option *opt, int n, int fromList, int def)
 
     for (i=0; 1; i++) 
       {
-       char *msg = fromList ? list[i] : mb[i].string, *msg2;
+       char *msg = fromList ? list[i] : mb[i].string;
        if(!msg) break;
        entry = CreateMenuItem(menu, opt->min & NO_GETTEXT ? msg : _(msg), (XtCallbackProc) ComboSelect, (n<<16)+i);
        if(!fromList) mb[i].handle = (void*) entry; // save item ID, for enabling / checkmarking
@@ -380,6 +424,9 @@ CreateComboPopup (Widget parent, Option *opt, int n, int fromList, int def)
        }
       }
       return menu;
+#else
+    return NULL;
+#endif
 }
 
 char moveTypeInTranslations[] =
@@ -387,27 +434,36 @@ char moveTypeInTranslations[] =
     "<Key>Escape: TypeInProc(0) \n";
 extern char filterTranslations[];
 extern char gameListTranslations[];
+extern char memoTranslations[];
 
 
-char *translationTable[] = {
+char *translationTable[] = { // beware: order is essential!
    historyTranslations, commentTranslations, moveTypeInTranslations, ICSInputTranslations,
-   filterTranslations, gameListTranslations,
+   filterTranslations, gameListTranslations, memoTranslations
 };
 
-void
+void * shells[NrOfDialogs];
+
 AddHandler (Option *opt, int nr)
 {
+#ifdef TODO_GTK
     XtOverrideTranslations(opt->handle, XtParseTranslationTable(translationTable[nr]));
+#endif
 }
 
 //----------------------------Generic dialog --------------------------------------------
 
 // cloned from Engine Settings dialog (and later merged with it)
 
+#ifdef TODO_GTK
 Widget shells[NrOfDialogs];
+#else
+void *shells[NrOfDialogs];
+#endif
 DialogClass parents[NrOfDialogs];
 WindowPlacement *wp[NrOfDialogs] = { // Beware! Order must correspond to DialogClass enum
-    NULL, &wpComment, &wpTags, NULL, NULL, NULL, NULL, &wpMoveHistory, &wpGameList, &wpEngineOutput
+    NULL, &wpComment, &wpTags, NULL, NULL, NULL, NULL, &wpMoveHistory, &wpGameList, &wpEngineOutput, &wpEvalGraph,
+    NULL, NULL, NULL, NULL, /*&wpMain*/ NULL
 };
 
 int
@@ -416,9 +472,38 @@ DialogExists (DialogClass n)
     return shells[n] != NULL;
 }
 
+void
+RaiseWindow (DialogClass dlg)
+{
+#ifdef TODO_GTK
+    static XEvent xev;
+    Window root = RootWindow(xDisplay, DefaultScreen(xDisplay));
+    Atom atom = XInternAtom (xDisplay, "_NET_ACTIVE_WINDOW", False);
+
+    xev.xclient.type = ClientMessage;
+    xev.xclient.serial = 0;
+    xev.xclient.send_event = True;
+    xev.xclient.display = xDisplay;
+    xev.xclient.window = XtWindow(shells[dlg]);
+    xev.xclient.message_type = atom;
+    xev.xclient.format = 32;
+    xev.xclient.data.l[0] = 1;
+    xev.xclient.data.l[1] = CurrentTime;
+
+    XSendEvent (xDisplay,
+          root, False,
+          SubstructureRedirectMask | SubstructureNotifyMask,
+          &xev);
+
+    XFlush(xDisplay); 
+    XSync(xDisplay, False);
+#endif
+}
+
 int
 PopDown (DialogClass n)
 {   // pops down any dialog created by GenericPopUp (or returns False if it wasn't up), unmarks any associated marked menu
+#ifdef TODO_GTK
     int j;
     Arg args[10];
     Dimension windowH, windowW; Position windowX, windowY;
@@ -443,39 +528,46 @@ PopDown (DialogClass n)
        MarkMenuItem(marked[n], False);
        marked[n] = NULL;
     }
-    if(!n) currentCps = NULL; // if an Engine Settings dialog was up, we must be popping it down now
+    if(!n && n != BrowserDlg) currentCps = NULL; // if an Engine Settings dialog was up, we must be popping it down now
     currentOption = dialogOptions[TransientDlg]; // just in case a transient dialog was up (to allow its check and combo callbacks to work)
-    XtSetKeyboardFocus(shells[parents[n]], n == BoardWindow ? formWidget:  shells[parents[n]]);
+    RaiseWindow(parents[n]);
+    if(parents[n] == BoardWindow) XtSetKeyboardFocus(shellWidget, formWidget);
+#endif
     return 1;
 }
 
+#ifdef TODO_GTK
 void
 GenericPopDown (Widget w, XEvent *event, String *prms, Cardinal *nprms)
 {   // to cause popdown through a translation (Delete Window button!)
     int dlg = atoi(prms[0]);
     Widget sh = shells[dlg];
-    if(browserUp || dialogError) return; // prevent closing dialog when it has an open file-browse daughter
+    if(shellUp[BrowserDlg] && dlg != BrowserDlg || dialogError) return; // prevent closing dialog when it has an open file-browse daughter
     shells[dlg] = w;
     PopDown(dlg);
     shells[dlg] = sh; // restore
 }
+#endif
 
 int
 AppendText (Option *opt, char *s)
 {
+    int len;
+#ifdef TODO_GTK
     XawTextBlock t;
     char *v;
-    int len;
     GetWidgetText(opt, &v);
     len = strlen(v);
     t.ptr = s; t.firstPos = 0; t.length = strlen(s); t.format = XawFmt8Bit;
     XawTextReplace(opt->handle, len, len, &t);
+#endif
     return len;
 }
 
 void
 SetColor (char *colorName, Option *box)
 {       // sets the color of a widget
+#ifdef TODO_GTK
        Arg args[5];
        Pixel buttonColor;
        XrmValue vFrom, vTo;
@@ -491,34 +583,63 @@ SetColor (char *colorName, Option *box)
        } else buttonColor = timerBackgroundPixel;
        XtSetArg(args[0], XtNbackground, buttonColor);;
        XtSetValues(box->handle, args, 1);
+#endif
 }
 
 void
 ColorChanged (Widget w, XtPointer data, XEvent *event, Boolean *b)
 {   // for detecting a typed change in color
     char buf[10];
+#ifdef TODO_GTK
     if ( (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) && *buf == '\r' )
        RefreshColor((int)(intptr_t) data, 0);
+#endif
 }
 
 static void
 GraphEventProc(Widget widget, caddr_t client_data, XEvent *event)
 {   // handle expose and mouse events on Graph widget
+#ifdef TODO_GTK
     Dimension w, h;
     Arg args[16];
-    int j, button=10, f=1;
-    Option *opt;
+    int j, button=10, f=1, sizing=0;
+    Option *opt, *graph = (Option *) client_data;
+    PointerCallback *userHandler = graph->target;
+
     if (!XtIsRealized(widget)) return;
 
     switch(event->type) {
-       case Expose:
-           if (((XExposeEvent*)event)->count > 0) return;  // don't bother if further exposure is pending
-           /* Get client area */
+       case Expose: // make handling of expose events generic, just copying from memory buffer (->choice) to display (->textValue)
+           /* Get window size */
            j = 0;
            XtSetArg(args[j], XtNwidth, &w); j++;
            XtSetArg(args[j], XtNheight, &h); j++;
            XtGetValues(widget, args, j);
-           break;
+
+           if(w < graph->max || w > graph->max + 1 || h != graph->value) { // use width fudge of 1 pixel
+               if(((XExposeEvent*)event)->count >= 0) { // suppress sizing on expose for ordered redraw in response to sizing.
+                   sizing = 1;
+                   graph->max = w; graph->value = h; // note: old values are kept if we we don't exceed width fudge
+               }
+           } else w = graph->max;
+
+           if(sizing && ((XExposeEvent*)event)->count > 0) { graph->max = 0; return; } // don't bother if further exposure is pending during resize
+           if(!graph->textValue || sizing) { // create surfaces of new size for display widget
+               if(graph->textValue) cairo_surface_destroy((cairo_surface_t *)graph->textValue);
+               graph->textValue = (char*) cairo_xlib_surface_create(xDisplay, XtWindow(widget), DefaultVisual(xDisplay, 0), w, h);
+           }
+           if(sizing) { // the memory buffer was already created in GenericPopup(),
+                        // to give drawing routines opportunity to use it before first expose event
+                        // (which are only processed when main gets to the event loop, so after all init!)
+                        // so only change when size is no longer good
+               if(graph->choice) cairo_surface_destroy((cairo_surface_t *) graph->choice);
+               graph->choice = (char**) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
+               break;
+           }
+           w = ((XExposeEvent*)event)->width;
+           if(((XExposeEvent*)event)->x + w > graph->max) w--; // cut off fudge pixel
+           if(w) ExposeRedraw(graph, ((XExposeEvent*)event)->x, ((XExposeEvent*)event)->y, w, ((XExposeEvent*)event)->height);
+           return;
        case MotionNotify:
            f = 0;
            w = ((XButtonEvent*)event)->x; h = ((XButtonEvent*)event)->y;
@@ -536,18 +657,31 @@ GraphEventProc(Widget widget, caddr_t client_data, XEvent *event)
            }
     }
     button *= f;
-    opt = ((PointerCallback*) client_data)(button, w, h);
+    opt = userHandler(button, w, h);
     if(opt) { // user callback specifies a context menu; pop it up
        XUngrabPointer(xDisplay, CurrentTime);
        XtCallActionProc(widget, "XawPositionSimpleMenu", event, &(opt->name), 1);
        XtPopupSpringLoaded(opt->handle);
     }
     XSync(xDisplay, False);
+#endif
+}
+
+void
+GraphExpose (Option *opt, int x, int y, int w, int h)
+{
+#ifdef TODO_GTK
+  XExposeEvent e;
+  if(!opt->handle) return;
+  e.x = x; e.y = y; e.width = w; e.height = h; e.count = -1; e.type = Expose; // count = -1: kludge to suppress sizing
+  GraphEventProc(opt->handle, (caddr_t) opt, (XEvent *) &e); // fake expose event
+#endif
 }
 
 static void
 GenericCallback (Widget w, XtPointer client_data, XtPointer call_data)
 {   // all Buttons in a dialog (including OK, cancel) invoke this
+#ifdef TODO_GTK
     String name;
     Arg args[16];
     char buf[MSG_SIZ];
@@ -564,7 +698,7 @@ GenericCallback (Widget w, XtPointer client_data, XtPointer call_data)
         if(GenericReadout(currentOption, -1)) PopDown(dlg); // calls OK-proc after full readout, but no popdown if it returns false
     } else
 
-    if(currentCps) {
+    if(currentCps && dlg != BrowserDlg) {
        XtSetArg(args[0], XtNlabel, &name);
        XtGetValues(w, args, 1);
        if(currentOption[data].type == SaveButton) GenericReadout(currentOption, -1);
@@ -573,11 +707,13 @@ GenericCallback (Widget w, XtPointer client_data, XtPointer call_data)
     } else ((ButtonCallback*) currentOption[data].target)(data);
 
     shells[dlg] = oldSh; // in case of multiple instances, restore previous (as this one could be popped down now)
+#endif
 }
 
 void
 TabProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
 {   // for transfering focus to the next text-edit
+#ifdef TODO_GTK
     Option *opt;
     for(opt = currentOption; opt->type != EndMark; opt++) {
        if(opt->handle == w) {
@@ -591,15 +727,17 @@ TabProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
            }
        }
     }
+#endif
 }
 
 void
 WheelProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
 {   // for scrolling a widget seen through a viewport with the mouse wheel (ListBox!)
+#ifdef TODO_GTK
     int j=0, n = atoi(prms[0]);
     static char *params[3] = { "", "Continuous", "Proportional" };
     Arg args[16];
-    float f, h, top;
+    float h, top;
     Widget v;
     if(!n) { // transient dialogs also use this for list-selection callback
        n = prms[1][0]-'0';
@@ -612,12 +750,13 @@ WheelProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
     XtSetArg(args[j], XtNshown, &h); j++;
     XtSetArg(args[j], XtNtopOfThumb, &top); j++;
     XtGetValues(v, args, j);
-    top += 0.1*h*n; if(top < 0.) top = 0.;
+    top += 0.1f*h*n; if(top < 0.f) top = 0.;
     XtCallActionProc(v, "StartScroll", event, params+1, 1);
     XawScrollbarSetThumb(v, top, -1.0);
     XtCallActionProc(v, "NotifyThumb", event, params, 0);
 //    XtCallActionProc(w, "NotifyScroll", event, params+2, 1);
     XtCallActionProc(v, "EndScroll", event, params, 0);
+#endif
 }
 
 static char *oneLiner  =
@@ -631,7 +770,8 @@ static char scrollTranslations[] =
 static void
 SqueezeIntoBox (Option *opt, int nr, int width)
 {   // size buttons in bar to fit, clipping button names where necessary
-    int i, j, wtot = 0;
+#ifdef TODO_GTK
+    int i, wtot = 0;
     Dimension widths[20], oldWidths[20];
     Arg arg;
     for(i=1; i<nr; i++) {
@@ -652,12 +792,14 @@ SqueezeIntoBox (Option *opt, int nr, int width)
        XtSetValues(opt[i].handle, &arg, 1);
     }
     opt->min = wtot;
+#endif
 }
 
 int
 SetPositionAndSize (Arg *args, Widget leftNeigbor, Widget topNeigbor, int b, int w, int h, int chaining)
 {   // sizing and positioning most widgets have in common
     int j = 0;
+#ifdef TODO_GTK
     // first position the widget w.r.t. earlier ones
     if(chaining & 1) { // same row: position w.r.t. last (on current row) and lastrow
        XtSetArg(args[j], XtNfromVert, topNeigbor); j++;
@@ -680,26 +822,28 @@ SetPositionAndSize (Arg *args, Widget leftNeigbor, Widget topNeigbor, int b, int
     if(!appData.monoMode) {
        if(!b && appData.dialogColor[0]) XtSetArg(args[j], XtNbackground, dialogColor),  j++;
        if(b == 3 && appData.buttonColor[0]) XtSetArg(args[j], XtNbackground, buttonColor),  j++;
-       if(b == 3) b = 1;
     }
+    if(b == 3) b = 1;
     // border
     XtSetArg(args[j], XtNborderWidth, b);  j++;
+#endif
     return j;
 }
 
 int
 GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent, int modal, int top)
 {
+#ifdef TODO_GTK
     Arg args[24];
     Widget popup, layout, dialog=NULL, edit=NULL, form,  last, b_ok, b_cancel, previousPane = NULL, textField = NULL, oldForm, oldLastRow, oldForeLast;
     Window root, child;
     int x, y, i, j, height=999, width=1, h, c, w, shrink=FALSE, stack = 0, box, chain;
     int win_x, win_y, maxWidth, maxTextWidth;
     unsigned int mask;
-    char def[MSG_SIZ], *msg;
+    char def[MSG_SIZ], *msg, engineDlg = (currentCps != NULL && dlgNr != BrowserDlg);
     static char pane[6] = "paneX";
     Widget texts[100], forelast = NULL, anchor, widest, lastrow = NULL, browse = NULL;
-    Dimension bWidth = 50, m;
+    Dimension bWidth = 50;
 
     if(dlgNr < PromoDlg && shellUp[dlgNr]) return 0; // already up
     if(dlgNr && dlgNr < PromoDlg && shells[dlgNr]) { // reusable, and used before (but popped down)
@@ -713,9 +857,8 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
     // WARNING: this kludge does not work for persistent dialogs, so that these cannot have spin or combo controls!
     currentOption = option;
 
-    if(currentCps) { // Settings popup for engine: format through heuristic
+    if(engineDlg) { // Settings popup for engine: format through heuristic
        int n = currentCps->nrOptions;
-       if(!n) { DisplayNote(_("Engine has no options")); currentCps = NULL; return 0; }
        if(n > 50) width = 4; else if(n>24) width = 2; else width = 1;
        height = n / width + 1;
        if(n && (currentOption[n-1].type == Button || currentOption[n-1].type == SaveButton)) currentOption[n].min = SAME_ROW; // OK on same line
@@ -725,6 +868,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
     XtSetArg(args[i], XtNresizable, True); i++;
     shells[BoardWindow] = shellWidget; parents[dlgNr] = parent;
 
+    if(dlgNr == BoardWindow) popup = shellWidget; else
     popup = shells[dlgNr] =
       XtCreatePopupShell(title, !top || !appData.topLevel ? transientShellWidgetClass : topLevelShellWidgetClass,
                                                            shells[parent], args, i);
@@ -760,7 +904,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
            option[i].value = *(float*)option[i].target;
            goto tBox;
          case Spin:
-           if(!currentCps) option[i].value = *(int*)option[i].target;
+           if(!engineDlg) option[i].value = *(int*)option[i].target;
            snprintf(def, MSG_SIZ,  "%d", option[i].value);
          case TextBox:
          case FileName:
@@ -777,12 +921,16 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
            if(option[i].type == FileName || option[i].type == PathName) w -= 55;
            j = SetPositionAndSize(args, dialog, last, 1 /* border */,
                                   w /* w */, option[i].type == TextBox ? option[i].value : 0 /* h */, 0x91 /* chain full width */);
-           if(option[i].type == TextBox && option[i].value) { // decorations for multi-line text-edits
+           if(option[i].type == TextBox) { // decorations for multi-line text-edits
                if(option[i].min & T_VSCRL) { XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++; }
                if(option[i].min & T_HSCRL) { XtSetArg(args[j], XtNscrollHorizontal, XawtextScrollAlways);  j++; }
                if(option[i].min & T_FILL)  { XtSetArg(args[j], XtNautoFill, True);  j++; }
                if(option[i].min & T_WRAP)  { XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++; }
-               if(option[i].min & T_TOP)   { XtSetArg(args[j], XtNtop, XtChainTop); j++; }
+               if(option[i].min & T_TOP)   { XtSetArg(args[j], XtNtop, XtChainTop); j++;
+                   if(!option[i].value) {    XtSetArg(args[j], XtNbottom, XtChainTop); j++;
+                                             XtSetValues(dialog, args+j-2, 2);
+                   }
+               }
            } else shrink = TRUE;
            XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
            XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
@@ -790,7 +938,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
            XtSetArg(args[j], XtNresizable, True);  j++;
            XtSetArg(args[j], XtNinsertPosition, 9999);  j++;
            XtSetArg(args[j], XtNstring, option[i].type==Spin || option[i].type==Fractional ? def : 
-                               currentCps ? option[i].textValue : *(char**)option[i].target);  j++;
+                               engineDlg ? option[i].textValue : *(char**)option[i].target);  j++;
            edit = last;
            option[i].handle = (void*)
                (textField = last = XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j));
@@ -822,7 +970,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
            XtAddCallback(last, XtNcallback, SpinCallback, (XtPointer)(intptr_t) i + 256*dlgNr);
            break;
          case CheckBox:
-           if(!currentCps) option[i].value = *(Boolean*)option[i].target; // where checkbox callback uses it
+           if(!engineDlg) option[i].value = *(Boolean*)option[i].target; // where checkbox callback uses it
            j = SetPositionAndSize(args, last, lastrow, 1 /* border */,
                                   textHeight/2 /* w */, textHeight/2 /* h */, 0xC0 /* chain both to left edge */);
            XtSetArg(args[j], XtNvertDistance, (textHeight+2)/4 + 3);  j++;
@@ -849,7 +997,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
 #if ENABLE_NLS
            if(option[i].choice) XtSetArg(args[j], XtNfontSet, *(XFontSet*)option[i].choice), j++;
 #else
-           if(option[i].choice) XtSetArg(args[j], XtNfont, *(XFontStruct*)option[i].choice), j++;
+           if(option[i].choice) XtSetArg(args[j], XtNfont, (XFontStruct*)option[i].choice), j++;
 #endif
            XtSetArg(args[j], XtNresizable, False);  j++;
            XtSetArg(args[j], XtNjustify, XtJustifyLeft);  j++;
@@ -865,7 +1013,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
                forelast = lastrow;
            } else chain = 0, shrink = FALSE;
            j = SetPositionAndSize(args, last, lastrow, 3 /* border */,
-                                  option[i].max /* w */, shrink ? textHeight : 0 /* h */, chain /* chain */);
+                                  option[i].max /* w */, shrink ? textHeight : 0 /* h */, option[i].min & 0xE | chain /* chain */);
            XtSetArg(args[j], XtNlabel, _(option[i].name));  j++;
            if(option[i].textValue) { // special for buttons of New Variant dialog
                XtSetArg(args[j], XtNsensitive, appData.noChessProgram || option[i].value < 0
@@ -874,7 +1022,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
            }
            option[i].handle = (void*)
                (dialog = last = XtCreateManagedWidget(option[i].name, commandWidgetClass, form, args, j));
-           if(option[i].choice && ((char*)option[i].choice)[0] == '#' && !currentCps) { // for the color picker default-reset
+           if(option[i].choice && ((char*)option[i].choice)[0] == '#' && !engineDlg) { // for the color picker default-reset
                SetColor( *(char**) option[i-1].target, &option[i]);
                XtAddEventHandler(option[i-1].handle, KeyReleaseMask, False, ColorChanged, (XtPointer)(intptr_t) i-1);
            }
@@ -889,12 +1037,12 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
            texts[h] = dialog = XtCreateManagedWidget(option[i].name, labelWidgetClass, form, args, j);
 
            if(option[i].min & COMBO_CALLBACK) msg = _(option[i].name); else {
-             if(!currentCps) SetCurrentComboSelection(option+i);
+             if(!engineDlg) SetCurrentComboSelection(option+i);
              msg=_(((char**)option[i].choice)[option[i].value]);
            }
 
            j = SetPositionAndSize(args, dialog, last, (option[i].min & 2) == 0 /* border */,
-                                  option[i].max && !currentCps ? option[i].max : 100 /* w */,
+                                  option[i].max && !engineDlg ? option[i].max : 100 /* w */,
                                   textHeight /* h */, 0x91 /* chain */); // same row as its label!
            XtSetArg(args[j], XtNmenuName, XtNewString(option[i].name));  j++;
            XtSetArg(args[j], XtNlabel, msg);  j++;
@@ -931,7 +1079,9 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
            option[i].handle = (void*)
                (last = XtCreateManagedWidget("graph", widgetClass, form, args, j));
            XtAddEventHandler(last, ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask, False,
-                     (XtEventHandler) GraphEventProc, option[i].target); // mandatory user-supplied expose handler
+                     (XtEventHandler) GraphEventProc, &option[i]); // mandatory user-supplied expose handler
+           if(option[i].min & SAME_ROW) last = forelast, forelast = lastrow;
+           option[i].choice = (char**) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, option[i].max, option[i].value); // image buffer
            break;
          case PopUp: // note: used only after Graph, so 'last' refers to the Graph widget
            option[i].handle = (void*) CreateComboPopup(last, option + i, i + 256*dlgNr, TRUE, option[i].value);
@@ -1054,16 +1204,17 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
   }
 
     XtRealizeWidget(popup);
-    XSetWMProtocols(xDisplay, XtWindow(popup), &wm_delete_window, 1);
-    snprintf(def, MSG_SIZ, "<Message>WM_PROTOCOLS: GenericPopDown(\"%d\") \n", dlgNr);
-    XtAugmentTranslations(popup, XtParseTranslationTable(def));
-    XQueryPointer(xDisplay, xBoardWindow, &root, &child,
-                 &x, &y, &win_x, &win_y, &mask);
-
-    XtSetArg(args[0], XtNx, x - 10);
-    XtSetArg(args[1], XtNy, y - 30);
-    XtSetValues(popup, args, 2);
-
+    if(dlgNr != BoardWindow) { // assign close button, and position w.r.t. pointer, if not main window
+       XSetWMProtocols(xDisplay, XtWindow(popup), &wm_delete_window, 1);
+       snprintf(def, MSG_SIZ, "<Message>WM_PROTOCOLS: GenericPopDown(\"%d\") \n", dlgNr);
+       XtAugmentTranslations(popup, XtParseTranslationTable(def));
+       XQueryPointer(xDisplay, xBoardWindow, &root, &child,
+                       &x, &y, &win_x, &win_y, &mask);
+
+       XtSetArg(args[0], XtNx, x - 10);
+       XtSetArg(args[1], XtNy, y - 30);
+       XtSetValues(popup, args, 2);
+    }
     XtPopup(popup, modal ? XtGrabExclusive : XtGrabNone);
     shellUp[dlgNr]++; // count rather than flag
     previous = NULL;
@@ -1076,11 +1227,14 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent
        XtSetArg(args[j], XtNy, (Position) (wp[dlgNr]->y));  j++;
        XtSetValues(popup, args, j);
     }
+    RaiseWindow(dlgNr);
+#endif
     return 1; // tells caller he must do initialization (e.g. add specific event handlers)
 }
 
 
 /* function called when the data to Paste is ready */
+#ifdef TODO_GTK
 static void
 SendTextCB (Widget w, XtPointer client_data, Atom *selection,
            Atom *type, XtPointer value, unsigned long *len, int *format)
@@ -1094,10 +1248,12 @@ SendTextCB (Widget w, XtPointer client_data, Atom *selection,
   SendString(buf);
   XtFree(value);
 }
+#endif
 
 void
 SendText (int n)
 {
+#ifdef TODO_GTK
     char *p = (char*) textOptions[n].choice;
     if(strstr(p, "$name")) {
        XtGetSelectionValue(menuBarWidget,
@@ -1107,18 +1263,22 @@ SendText (int n)
          CurrentTime
        );
     } else SendString(p);
+#endif
 }
 
 void
 SetInsertPos (Option *opt, int pos)
 {
+#ifdef TODO_GTK
     Arg args[16];
     XtSetArg(args[0], XtNinsertPosition, pos);
     XtSetValues(opt->handle, args, 1);
 //    SetFocus(opt->handle, shells[InputBoxDlg], NULL, False); // No idea why this does not work, and the following is needed:
-    XSetInputFocus(xDisplay, XtWindow(opt->handle), RevertToPointerRoot, CurrentTime);
+//    XSetInputFocus(xDisplay, XtWindow(opt->handle), RevertToPointerRoot, CurrentTime);
+#endif
 }
 
+#ifdef TODO_GTK
 void
 TypeInProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
 {   // can be used as handler for any text edit in any dialog (from GenericPopUp, that is)
@@ -1131,11 +1291,14 @@ TypeInProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
            GenericCallback (w, (XtPointer)(intptr_t) (30000 + n + (dlgNr<<16)), NULL);
     }
 }
+#endif
 
 void
 HardSetFocus (Option *opt)
 {
+#ifdef TODO_GTK
     XSetInputFocus(xDisplay, XtWindow(opt->handle), RevertToPointerRoot, CurrentTime);
+#endif
 }