/*
 * Copyright 1991 University of Wisconsin-Madison
 *
 * Permission to use, copy, modify, distribute, and sell this software and
 * its documentation for any purpose is hereby granted without fee, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of the University of Wisconsin-Madison
 * not be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  The University of
 * Wisconsin-Madison makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 *
 * THE UNIVERSITY OF WISCONSIN-MADISON DISCLAIMS ALL WARRANTIES WITH REGARD
 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF WISCONSIN-MADISON BE LIABLE
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Tim Theisen             Department of Computer Sciences
 *          tim@cs.wisc.edu         University of Wisconsin-Madison
 *          uwvax!tim               1210 West Dayton Street
 *          (608)262-0438           Madison, WI   53706
 *
 *
 * Modified 12/91 by Dean Luick.  Tim graciously donated this piece of code
 * from his program ghostview, an X11 front end for ghostscript.
 *
 *    + Make the cancel button optional.
 *    + Put an #ifdef SPECIAL_CMAP around code to fix a colormap bug.
 *      We don't need it here.
 *    + Add the function positionpopup() from another part of ghostview
 *      to this code.
 *
 * Modified 2/93, Various.
 *    + Added workaround for SYSV include problem.
 *    + Changed the default width response text widget to be as wide as the
 *      window itself.  Suggestion from David E. Wexelblat, dwex@goblin.org.
 *
 * Modified 5/2015, anonymous.
 *    + Include nethack's lint.h to get nhStr() macro.
 *    + Use nhStr() on string literals (or macros from <X11/StringDefs.h>
 *      that hide string literals) to cast away implicit 'const' in order
 *      to suppress "warning: assignment discards qualifiers from pointer
 *      target type" issued by 'gcc -Wwrite-strings' as used by nethack.
 *      (For this file, always the second parameter to XtSetArg().)
 *
 * Modified 1/2016, Pat Rankin.
 *    + Added minimum width argument to SetDialogResponse() so that the
 *      text entry widget can be forced to wider than the default response.
 *    + Make 'okay' button same width as 'cancel', and both wider than
 *      default by a small arbitrary amount.
 *
 * $NHDT-Date: 1455157470 2016/02/11 02:24:30 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.9 $
 */

#ifndef SYSV
#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
#endif

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xos.h>
#include <X11/Xaw/Cardinals.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Command.h>

#ifdef PRESERVE_NO_SYSV
#ifdef SYSV
#undef SYSV
#endif
#undef PRESERVE_NO_SYSV
#endif

#define X11_BUILD
#include "config.h" /* #define for const for non __STDC__ compilers */
#undef X11_BUILD

#include "lint.h"   /* for nethack's nhStr() macro */
#include "winX.h"   /* to make sure prototypes match corresponding functions */

/* ":" added to both translations below to allow limited redefining of
 * keysyms before testing for keysym values -- dlc */
static const char okay_accelerators[] = "#override\n\
     :<Key>Return: set() notify() unset()\n";

static const char cancel_accelerators[] = "#override\n\
     :<Key>Escape: set() notify() unset()\n\
     :<Ctrl>[: set() notify() unset()\n"; /* for keyboards w/o an ESC */

/* Create a dialog widget.  It is just a form widget with
 *      a label prompt
 *      a text response
 *      an okay button
 *      an optional cancel button
 */
Widget
CreateDialog(Widget parent, String name, XtCallbackProc okay_callback,
             XtCallbackProc cancel_callback)
{
    Widget form, prompt, response, okay, cancel;
    Arg args[20];
    Cardinal num_args;
    Dimension owidth, cwidth;

    num_args = 0;
#ifdef SPECIAL_CMAP
    if (special_cmap) {
        XtSetArg(args[num_args], nhStr(XtNbackground), white); num_args++;
    }
#endif
    form = XtCreateManagedWidget(name, formWidgetClass, parent,
                                 args, num_args);

    num_args = 0;
#ifdef SPECIAL_CMAP
    if (special_cmap) {
        XtSetArg(args[num_args], nhStr(XtNforeground), black); num_args++;
        XtSetArg(args[num_args], nhStr(XtNbackground), white); num_args++;
    }
#endif
    XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
    XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop); num_args++;
    XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
    XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
    XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
    XtSetArg(args[num_args], nhStr(XtNborderWidth), 0); num_args++;
    prompt = XtCreateManagedWidget("prompt", labelWidgetClass, form,
                                   args, num_args);

    num_args = 0;
#ifdef SPECIAL_CMAP
    if (special_cmap) {
        XtSetArg(args[num_args], nhStr(XtNforeground), black); num_args++;
        XtSetArg(args[num_args], nhStr(XtNbackground), white); num_args++;
    }
#endif
    XtSetArg(args[num_args], nhStr(XtNfromVert), prompt); num_args++;
    XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
    XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop); num_args++;
    XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
    XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
    XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
    XtSetArg(args[num_args], nhStr(XtNeditType), XawtextEdit); num_args++;
    XtSetArg(args[num_args], nhStr(XtNresize), XawtextResizeWidth); num_args++;
    XtSetArg(args[num_args], nhStr(XtNstring), ""); num_args++;
    response = XtCreateManagedWidget("response", asciiTextWidgetClass, form,
                                     args, num_args);

    num_args = 0;
#ifdef SPECIAL_CMAP
    if (special_cmap) {
        XtSetArg(args[num_args], nhStr(XtNforeground), black); num_args++;
        XtSetArg(args[num_args], nhStr(XtNbackground), white); num_args++;
    }
#endif
    XtSetArg(args[num_args], nhStr(XtNfromVert), response); num_args++;
    XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
    XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop); num_args++;
    XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
    XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
    XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
    XtSetArg(args[num_args], nhStr(XtNaccelerators),
             XtParseAcceleratorTable(okay_accelerators)); num_args++;
    okay = XtCreateManagedWidget("okay", commandWidgetClass, form,
                                 args, num_args);
    XtAddCallback(okay, XtNcallback, okay_callback, form);
    XtSetArg(args[0], XtNwidth, &owidth);
    XtGetValues(okay, args, ONE);

    /* Only create cancel button if there is a callback for it. */
    if (cancel_callback) {
        num_args = 0;
#ifdef SPECIAL_CMAP
        if (special_cmap) {
            XtSetArg(args[num_args], nhStr(XtNforeground), black); num_args++;
            XtSetArg(args[num_args], nhStr(XtNbackground), white); num_args++;
        }
#endif
        XtSetArg(args[num_args], nhStr(XtNfromVert), response); num_args++;
        XtSetArg(args[num_args], nhStr(XtNfromHoriz), okay); num_args++;
        XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++;
        XtSetArg(args[num_args], nhStr(XtNbottom), XtChainTop); num_args++;
        XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++;
        XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++;
        XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++;
        XtSetArg(args[num_args], nhStr(XtNaccelerators),
                 XtParseAcceleratorTable(cancel_accelerators)); num_args++;
        cancel = XtCreateManagedWidget("cancel", commandWidgetClass, form,
                                       args, num_args);
        XtAddCallback(cancel, XtNcallback, cancel_callback, form);
        XtInstallAccelerators(response, cancel);
        XtSetArg(args[0], XtNwidth, &cwidth);
        XtGetValues(cancel, args, ONE);
        /* widen the cancel button */
        cwidth += 25;
        XtSetArg(args[0], XtNwidth, cwidth);
        XtSetValues(cancel, args, ONE);
    } else
        cwidth = owidth + 25;

    /* make okay button same width as cancel, or widen it if no cancel */
    XtSetArg(args[0], XtNwidth, cwidth);
    XtSetValues(okay, args, ONE);

    XtInstallAccelerators(response, okay);
    XtSetKeyboardFocus(form, response);

    return form;
}

#if 0
/* get the prompt from the dialog box.  Used a startup time to
 * save away the initial prompt */
String
GetDialogPrompt(Widget w)
{
    Arg args[1];
    Widget label;
    String s;

    label = XtNameToWidget(w, "prompt");
    XtSetArg(args[0], nhStr(XtNlabel), &s);
    XtGetValues(label, args, ONE);
    return XtNewString(s);
}
#endif

/* set the prompt.  This is used to put error information in the prompt */
void
SetDialogPrompt(Widget w, String newprompt)
{
    Arg args[1];
    Widget label;

    label = XtNameToWidget(w, "prompt");
    XtSetArg(args[0], nhStr(XtNlabel), newprompt);
    XtSetValues(label, args, ONE);
}

/* get what the user typed; caller must free the response */
String
GetDialogResponse(Widget w)
{
    Arg args[1];
    Widget response;
    String s;

    response = XtNameToWidget(w, "response");
    XtSetArg(args[0], nhStr(XtNstring), &s);
    XtGetValues(response, args, ONE);
    return XtNewString(s);
}

/* set the default response */
void
SetDialogResponse(Widget w, String s, unsigned ln)
{
    Arg args[4];
    Widget response;
    XFontStruct *font;
    Dimension width, nwidth, leftMargin, rightMargin;
    unsigned s_len = strlen(s);

    if (s_len < ln)
        s_len = ln;
    response = XtNameToWidget(w, "response");
    XtSetArg(args[0], nhStr(XtNfont), &font);
    XtSetArg(args[1], nhStr(XtNleftMargin), &leftMargin);
    XtSetArg(args[2], nhStr(XtNrightMargin), &rightMargin);
    XtSetArg(args[3], nhStr(XtNwidth), &width);
    XtGetValues(response, args, FOUR);
    /* width includes margins as per Xaw documentation */
    nwidth = font->max_bounds.width * (s_len + 1) + leftMargin + rightMargin;
    if (nwidth < width)
        nwidth = width;

    XtSetArg(args[0], nhStr(XtNstring), s);
    XtSetArg(args[1], nhStr(XtNwidth), nwidth);
    XtSetValues(response, args, TWO);
    XawTextSetInsertionPoint(response, strlen(s));
}

#if 0
/* clear the response */
void
ClearDialogResponse(Widget w)
{
    Arg args[2];
    Widget response;

    response = XtNameToWidget(w, "response");
    XtSetArg(args[0], nhStr(XtNstring), "");
    XtSetArg(args[1], nhStr(XtNwidth), 100);
    XtSetValues(response, args, TWO);
}
#endif

/* Not a part of the original dialogs.c from ghostview -------------------- */

/* position popup window under the cursor */
void
positionpopup(Widget w, boolean bottom) /* position y on bottom? */
{
    Arg args[3];
    Cardinal num_args;
    Dimension width, height, b_width;
    int x, y, max_x, max_y;
    Window root, child;
    XSizeHints *hints;
    int dummyx, dummyy;
    unsigned int dummymask;
    extern Widget toplevel;

    /* following line deals with a race condition w/brain-damaged WM's -dlc */
    XtUnrealizeWidget(w);

    XQueryPointer(XtDisplay(toplevel), XtWindow(toplevel), &root, &child, &x,
                  &y, &dummyx, &dummyy, &dummymask);
    num_args = 0;
    XtSetArg(args[num_args], XtNwidth, &width); num_args++;
    XtSetArg(args[num_args], XtNheight, &height); num_args++;
    XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++;
    XtGetValues(w, args, num_args);

    /* position so that the cursor is center,center or center,bottom */
    width += 2 * b_width;
    x -= ((Position) width / 2);
    if (x < 0)
        x = 0;
    if (x > (max_x = (Position) (XtScreen(w)->width - width)))
        x = max_x;

    if (bottom) {
        y -= (height + b_width - 1);
        height += 2 * b_width;
    } else {
        height += 2 * b_width;
        y -= ((Position) height / 2);
    }
    if (y < 0)
        y = 0;
    if (y > (max_y = (Position) (XtScreen(w)->height - height)))
        y = max_y;

    num_args = 0;
    XtSetArg(args[num_args], XtNx, x); num_args++;
    XtSetArg(args[num_args], XtNy, y); num_args++;
    XtSetValues(w, args, num_args);

    /* Some older window managers ignore XtN{x,y}; hint the same values.
       {x,y} are not used by newer window managers; older ones need them. */
    XtRealizeWidget(w);
    hints = XAllocSizeHints();
    hints->flags = USPosition;
    hints->x = x;
    hints->y = y;
    XSetWMNormalHints(XtDisplay(w), XtWindow(w), hints);
    XFree(hints);
}

/*dialogs.c*/
