X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=xoptions.c;h=cd18a57e41217b8735fa2a5a1f4a4f8412a3a82c;hb=7e0222e64da6706bf0dc2468f459b98e4346206b;hp=15362a55205135816bac5ec7b4799d0197f9a48a;hpb=8e8f7200a0db9c816e5b508bc0cd377a393bda41;p=xboard.git diff --git a/xoptions.c b/xoptions.c index 15362a5..cd18a57 100644 --- a/xoptions.c +++ b/xoptions.c @@ -68,6 +68,9 @@ extern char *getenv(); #include #include +#include +#include + #include "common.h" #include "backend.h" #include "xboard.h" @@ -127,7 +130,6 @@ BoardFocus () //--------------------------- Engine-specific options menu ---------------------------------- int dialogError; -static Boolean browserUp; Option *dialogOptions[NrOfDialogs]; static Arg layoutArgs[] = { @@ -141,6 +143,18 @@ static Arg formArgs[] = { }; void +MarkMenuItem (char *menuRef, int state) +{ + MenuItem *item = MenuNameToItem(menuRef); + + if(item) { + Arg args[2]; + XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None); + XtSetValues(item->handle, args, 1); + } +} + +void GetWidgetText (Option *opt, char **buf) { Arg arg; @@ -240,12 +254,12 @@ HighlightWithScroll (Option *opt, int sel, int max) 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); } @@ -306,16 +320,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) { @@ -338,7 +343,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; } @@ -369,7 +374,7 @@ CreateMenuItem (Widget menu, char *msg, XtCallbackProc CB, int n) 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; + int i; Widget menu, entry; Arg arg; MenuItem *mb = (MenuItem *) opt->choice; @@ -380,7 +385,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 @@ -419,7 +424,7 @@ Widget shells[NrOfDialogs]; DialogClass parents[NrOfDialogs]; WindowPlacement *wp[NrOfDialogs] = { // Beware! Order must correspond to DialogClass enum NULL, &wpComment, &wpTags, NULL, NULL, NULL, NULL, &wpMoveHistory, &wpGameList, &wpEngineOutput, &wpEvalGraph, - NULL, NULL, NULL, NULL, &wpMain + NULL, NULL, NULL, NULL, /*&wpMain*/ NULL }; int @@ -428,6 +433,32 @@ DialogExists (DialogClass n) return shells[n] != NULL; } +void +RaiseWindow (DialogClass dlg) +{ + 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); +} + int PopDown (DialogClass n) { // pops down any dialog created by GenericPopUp (or returns False if it wasn't up), unmarks any associated marked menu @@ -455,9 +486,10 @@ 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); return 1; } @@ -466,7 +498,7 @@ 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 @@ -518,19 +550,44 @@ GraphEventProc(Widget widget, caddr_t client_data, XEvent *event) { // handle expose and mouse events on Graph widget 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; @@ -548,7 +605,7 @@ 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); @@ -557,6 +614,15 @@ GraphEventProc(Widget widget, caddr_t client_data, XEvent *event) XSync(xDisplay, False); } +void +GraphExpose (Option *opt, int x, int y, int w, int h) +{ + 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 +} + static void GenericCallback (Widget w, XtPointer client_data, XtPointer call_data) { // all Buttons in a dialog (including OK, cancel) invoke this @@ -576,7 +642,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); @@ -611,7 +677,7 @@ WheelProc (Widget w, XEvent *event, String *prms, Cardinal *nprms) 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'; @@ -624,7 +690,7 @@ 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); @@ -643,7 +709,7 @@ 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; + int i, wtot = 0; Dimension widths[20], oldWidths[20]; Arg arg; for(i=1; inrOptions; - 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 @@ -773,7 +838,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: @@ -790,12 +855,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++; @@ -803,7 +872,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)); @@ -835,7 +904,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++; @@ -862,7 +931,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++; @@ -878,7 +947,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 @@ -887,7 +956,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); } @@ -902,12 +971,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++; @@ -944,7 +1013,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); @@ -1090,6 +1161,7 @@ GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent XtSetArg(args[j], XtNy, (Position) (wp[dlgNr]->y)); j++; XtSetValues(popup, args, j); } + RaiseWindow(dlgNr); return 1; // tells caller he must do initialization (e.g. add specific event handlers) } @@ -1130,7 +1202,7 @@ SetInsertPos (Option *opt, int pos) 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); } void