
/*************************************************************************
 * FRMDSMEN.C - The frm_dsmenu() routine.
 *************************************************************************/

#include "gemfintl.h"
#include <osbind.h>

/*-------------------------------------------------------------------------
 * The tedinfos for the dialog body text.
 *   We have to use our own tedinfos instead of sharing the common array
 *   of them in FRMNLDSU.C, because we're non-modal, and other users of
 *   the common array can be invoked while the progress display is up.
 *-----------------------------------------------------------------------*/

static TEDINFO GFAR local_tedinfos[] = {
  {NULL, NULL, NULL, 3, 6, 0, 0x11F0, 0,  0,   1,   1},
  {NULL, NULL, NULL, 3, 6, 0, 0x11F0, 0,  0,   1,   1},
  {NULL, NULL, NULL, 3, 6, 0, 0x11F0, 0,  0,   1,   1},
  {NULL, NULL, NULL, 3, 6, 0, 0x11F0, 0,  0,   1,   1},
  {NULL, NULL, NULL, 3, 6, 0, 0x11F0, 0,  0,   1,   1},
  {NULL, NULL, NULL, 3, 6, 0, 0x11F0, 0,  0,   1,   1},
  {NULL, NULL, NULL, 3, 6, 0, 0x11F0, 0,  0,   1,   1},
  {NULL, NULL, NULL, 3, 6, 0, 0x11F0, 0,  0,   1,   1},
  {NULL, NULL, NULL, 3, 6, 0, 0x11F0, 0,  0,   1,   1},
  {NULL, NULL, NULL, 3, 6, 0, 0x11F0, 0,  0,   1,   1}
};

/*------------------------------------------------------------------------
 * the dialog tree...
 *-----------------------------------------------------------------------*/

#define OUTLINED_BOX_SPEC 0x00021100L
#define SHADOWED_BOX_SPEC 0x00FF1100L

static OBJECT GFAR progress_dialog[] = {
 { -1,  1, 13, G_BOX,      NONE,       OUTLINED, (_Ob_spec_t)0x00021100L, 0x0000, 0x0000, 0x0001, 0x0000},
 { 12,  2, 11, G_IBOX,     NONE,       NORMAL,   (_Ob_spec_t)0x00001100L, 0x0001, 0x0000, 0x0001, 0x0000},
 {  3, -1, -1, G_BOXTEXT,  NONE,       NORMAL,   (_Ob_spec_t)0L,          0x0000, 0x0001, 0x0001, 0x0001},
 {  4, -1, -1, G_BOXTEXT,  NONE,       NORMAL,   (_Ob_spec_t)0L,          0x0000, 0x0002, 0x0001, 0x0001},
 {  5, -1, -1, G_BOXTEXT,  NONE,       NORMAL,   (_Ob_spec_t)0L,          0x0000, 0x0003, 0x0001, 0x0001},
 {  6, -1, -1, G_BOXTEXT,  NONE,       NORMAL,   (_Ob_spec_t)0L,          0x0000, 0x0004, 0x0001, 0x0001},
 {  7, -1, -1, G_BOXTEXT,  NONE,       NORMAL,   (_Ob_spec_t)0L,          0x0000, 0x0005, 0x0001, 0x0001},
 {  8, -1, -1, G_BOXTEXT,  NONE,       NORMAL,   (_Ob_spec_t)0L,          0x0000, 0x0006, 0x0001, 0x0001},
 {  9, -1, -1, G_BOXTEXT,  NONE,       NORMAL,   (_Ob_spec_t)0L,          0x0000, 0x0007, 0x0001, 0x0001},
 { 10, -1, -1, G_BOXTEXT,  NONE,       NORMAL,   (_Ob_spec_t)0L,          0x0000, 0x0008, 0x0001, 0x0001},
 { 11, -1, -1, G_BOXTEXT,  NONE,       NORMAL,   (_Ob_spec_t)0L,          0x0000, 0x0009, 0x0001, 0x0001},
 {  1, -1, -1, G_BOXTEXT,  NONE,       NORMAL,   (_Ob_spec_t)0L,          0x0000, 0x000A, 0x0001, 0x0001},
 { 13, -1, -1, G_BOX,      NONE,       NORMAL,   (_Ob_spec_t)0x00FF1131L, 0x0001, 0x0000, 0x0001, 0x0001},
 {  0, -1, -1, G_BUTTON,   0x0027,     NORMAL,   (_Ob_spec_t)0L,          0x0000, 0x0000, 0x0001, 0x0001}
};

#define TEXT_BOX        1
#define FIRST_TEXT_LINE 2
#define THERMO_BAR      12
#define EXIT_BUTTON     13

#define MAXLINES Array_els(local_tedinfos)

/*------------------------------------------------------------------------
 * misc static data...
 *-----------------------------------------------------------------------*/

static FormControl GFAR progress_ctl;
static short       initdone = FALSE;
static char        *formatted_text;
static short       lastline;
static short       oldmouse;

/*------------------------------------------------------------------------
 * dummy_do - The form_do() handler when the dialog isn't abortable.
 *-----------------------------------------------------------------------*/

#ifdef GEMFAST_PROTOS
  static short dummy_do(FormControl *ctl)
#else
  static short dummy_do(ctl)
    FormControl *ctl;
#endif
{
    (void)ctl;
    return NO_OBJECT;
}

/*------------------------------------------------------------------------
 * watchbutton_do - The form_do() handler to watch the abort button.
 *  This is a non-modal handler.  It checks the keyboard and mouse button,
 *  and if neither is active, it returns NO_OBJECT.
 *-----------------------------------------------------------------------*/

#ifdef GEMFAST_PROTOS
  static short watchbutton_do(FormControl *ctl)
#else
  static short watchbutton_do(ctl)
    FormControl *ctl;
#endif
{
    short    obj;
    short    mouseobj;
    XMULTI xm;

    xm.mflags    = MU_BUTTON | MU_KEYBD | MU_TIMER;
    xm.mbstate   = 1;
    xm.mbmask    = 1;
    xm.mbclicks  = 1;
    xm.mtcount   = 1;

    obj = NO_OBJECT;
    evnx_multi(&xm);

    if ((xm.mwhich & MU_KEYBD)
     && (xm.mkreturn & 0x00FF) == '\r') {
        obj = EXIT_BUTTON;
        obj_stchange(ctl->ptree, obj, SELECTED, OBJ_CLIPDRAW, ctl->pboundrect);
    }

    if (xm.mwhich & MU_BUTTON) {
        mouseobj = objc_find(ctl->ptree, ROOT, MAX_DEPTH, xm.mmox, xm.mmoy);
        if (mouseobj == NO_OBJECT) {
            Bconout(2, 7); /* ding!                                                   */
        } else if (mouseobj == EXIT_BUTTON) {
            if (graf_watchbox(ctl->ptree, EXIT_BUTTON, SELECTED, NORMAL)) {
                obj = EXIT_BUTTON;
            }
        } else if (mouseobj == ctl->moverobj) {
            obj = mouseobj;
        }
    }

    return obj;
}

/*------------------------------------------------------------------------
 * setup_dialog - Construct the dynamic dialog, paint it.
 *-----------------------------------------------------------------------*/

#ifdef GEMFAST_PROTOS
  static short setup_dialog(long options, short increments,
                            char *button, char *fmt, va_list args)
#else
  static short setup_dialog(options, increments, button, fmt, args)
    long        options;
    short         increments;
    char       *button;
    char       *fmt;
    va_list     args;
#endif
{
    short              status;
    short              numlines;
    short              maxwidth;
    short              btnwidth;
    short              cumulative_height;
    short              wchar  = gl_wchar;
    short              hchar2 = gl_hchar * 2;
    char            *strptrs[MAXLINES+1];
    char            *strpatches[MAXLINES+1];
    register OBJECT *ptree = progress_dialog;

    /*--------------------------------------------------------------------
     * do one-time init, validate parms, merge in default options.
     *-------------------------------------------------------------------*/

    if (!initdone) {
        initdone = TRUE;
        rsc_treefix(ptree);
        if (0 != (status = xob_thermo_create(ptree, THERMO_BAR, 1))) {
            return status;
        }
    }

    if (progress_ctl.ptree != NULL) {
        return gfErr_resource_in_use;
    }

    if (increments >= gl_rwdesk.g_w) {
        return gfErr_parameter_range;
    }

    if (!(options & FRM_NODEFAULTS)) {
        options |= _FrmDefaults | (FRM_DEFAULT_DYNOPT & ~FRM_MOUSEARROW);
    }
    options |= FRM_MANDATORY_DYNOPT;

    /*--------------------------------------------------------------------
     * if a button was specified, its string is the initial maxwidth.
     *-------------------------------------------------------------------*/

    if (button == NULL) {
        maxwidth = btnwidth = 0;
    } else {
        maxwidth = btnwidth = (short)strlen(button) + 2;
    }

    /*--------------------------------------------------------------------
     * format the text and load it into the dialog objects.
     * save a pointer to the string pointer for the last text line.
     * this lets easily change the last line of text during an update.
     *-------------------------------------------------------------------*/

    formatted_text = _FrmVFormat(fmt, args, NULL);

    _FrmNL2DS(formatted_text, strptrs, strpatches, MAXLINES);
    numlines = _FrmDS2Obj(strptrs, &ptree[FIRST_TEXT_LINE], local_tedinfos,
                            &maxwidth, MAXLINES);

    lastline = TEXT_BOX + numlines;

    /*--------------------------------------------------------------------
     * from this point on, widths are pixels, not characters.  if the
     * thermo bar is wider than the text, it becomes the maxwidth.
     * at this point, maxwidth is the width of the widest text line or
     * the termo bar, but does not include the whitespace gutters on
     * either side of the text/thermo.
     *-------------------------------------------------------------------*/

    maxwidth *= wchar;
    btnwidth *= wchar;

    if (maxwidth < increments) {
        maxwidth = increments;
    }

    /*--------------------------------------------------------------------
     * set the widths of the dialog and 1st-level children.  set the
     * height of the text strings part of the dialog.
     *-------------------------------------------------------------------*/

    _FrmTrWidths(ptree, TEXT_BOX,   maxwidth);
    cumulative_height           = numlines * gl_hchar;
    ptree[TEXT_BOX].ob_height   = cumulative_height;

    /*--------------------------------------------------------------------
     * if a thermo bar was requested, set it up, else hide it.
     *-------------------------------------------------------------------*/

    if (increments > 0) {
        cumulative_height          += hchar2;
        ptree[THERMO_BAR].ob_y      = cumulative_height;
        ptree[THERMO_BAR].ob_width  = maxwidth;
        xob_thermo_create(ptree, THERMO_BAR, increments);
        ptree[THERMO_BAR].ob_flags &= ~HIDETREE;
    } else {
        ptree[THERMO_BAR].ob_flags |= HIDETREE;
    }

    /*--------------------------------------------------------------------
     * from this point on, maxwidth includes the 2-char whitespace gutter.
     *-------------------------------------------------------------------*/

    maxwidth += 2 * wchar;

    /*--------------------------------------------------------------------
     * if a button was specified, set it up, else hide it.
     *-------------------------------------------------------------------*/

    if (button != NULL) {
        cumulative_height          += hchar2;
        ptree[EXIT_BUTTON].ob_width = btnwidth;
        ptree[EXIT_BUTTON].ob_y     = cumulative_height;
        ptree[EXIT_BUTTON].ob_x     = (maxwidth - btnwidth) / 2;
        ptree[EXIT_BUTTON].ob_flags &= ~HIDETREE;
        ptree[EXIT_BUTTON]._Ob_spec = (_Ob_spec_t)button;
    } else {
        ptree[EXIT_BUTTON].ob_flags |= HIDETREE;
    }

    /*--------------------------------------------------------------------
     * now set the width and height of the overal dialog, and handle the
     * DSHADOWED and DSL1TITLE options.
     *-------------------------------------------------------------------*/

    cumulative_height          += hchar2;
    ptree->ob_width             = maxwidth;
    ptree->ob_height            = cumulative_height;;

    if (options & FRM_DSHADOWED) {
        ptree->ob_state = SHADOWED;
        ptree->_Ob_spec  = (_Ob_spec_t)SHADOWED_BOX_SPEC;
    } else {
        ptree->ob_state = OUTLINED;
        ptree->_Ob_spec  = (_Ob_spec_t)OUTLINED_BOX_SPEC;
    }

    /*--------------------------------------------------------------------
     * grab control of the mouse, then init and paint the dialog.
     * we hold on to the mouse semaphore until the progress dialog is
     * closed, because we (potentially at least) have a screen area
     * saved via blit and we can't allow redraws; also, since we can't
     * respond to redraws and we may be sitting over a window, we can't
     * allow window controls.
     *-------------------------------------------------------------------*/

    wind_update(BEG_MCTRL);
    if (options & FRM_MOUSEARROW) {
        oldmouse = graf_mouse(ARROW, NULL);
    } else {
        oldmouse = -1;
    }

    {
        register FormControl *ctl = &progress_ctl;

        _FrmSetup(ctl, options|FRM_DSTART, ptree, &gl_rwdesk);
        frm_start(ctl);
        frm_draw(ctl, ROOT);

        ctl->form_do = (button == NULL) ? dummy_do : watchbutton_do;
    }

    return 0;
}

/*------------------------------------------------------------------------
 * update_dialog - Update the thermo bar and/or text in the dialog.
 *-----------------------------------------------------------------------*/

#ifdef GEMFAST_PROTOS
  static short update_dialog(short new_position, char *fmt, va_list args)
#else
  static short update_dialog(new_position, fmt, args)
    short      new_position;
    char    *fmt;
    va_list  args;
#endif
{
    register FormControl *ctl = &progress_ctl;
    static char GFAR      linebuf[256];

    if (ctl->ptree == NULL) {
        return FALSE;
    }

    if (fmt != NULL) {
        vsprintf(linebuf, fmt, args);
        rsc_sstrings(ctl->ptree, lastline, linebuf, -1);
        frm_draw(ctl, lastline);
    }

    if (new_position != 0) {
        xob_thermo_update(ctl->ptree, THERMO_BAR, new_position, ctl->pboundrect);
    }

    return (EXIT_BUTTON == frm_do(ctl, ROOT));
}

/*------------------------------------------------------------------------
 * cleanup_dialog - Remove dialog from the screen, release resources.
 *-----------------------------------------------------------------------*/

#ifdef GEMFAST_PROTOS
  static short cleanup_dialog(void)
#else
  static short cleanup_dialog()
#endif
{
    register FormControl *ctl = &progress_ctl;

    if (ctl->ptree != NULL) {
        frm_finish(ctl);
        frm_cleanup(ctl);
        if (oldmouse != -1) {
            graf_mouse(oldmouse, NULL);
        }
        wind_update(END_MCTRL);
        _FrmVFree(formatted_text);
        ctl->ptree = NULL;
    }
    return 0;
}

/*------------------------------------------------------------------------
 * frm_progress - Dispatch the various activities for progress reporting.
 *-----------------------------------------------------------------------*/

#ifdef GEMFAST_PROTOS
  short frm_vprogress(long options, short increments,
                      char *buttons, char *fmt, va_list args)
#else
  short frm_vprogress(options, increments, buttons, fmt, args)
    long options;
    short  increments;
    char *buttons;
    char *fmt;
    va_list args;
#endif
{
    short     rv;

    switch ((options & FRM_ACTIONBITS) >> 16) {
      case FRM_PSTART >> 16:
        rv = setup_dialog(options, increments, buttons, fmt, args);
        break;
      case FRM_PUPDATE >> 16:
        rv = update_dialog(increments, fmt, args);
        break;
      case FRM_PFINISH >> 16:
        rv = cleanup_dialog();
        break;
      default:
        rv = gfErr_action_invalid;
        break;
    }

    return rv;
}


#ifdef GEMFAST_PROTOS
  short frm_progress(long options, short increments,
                     char *buttons, char *fmt, ...)
#else
  short frm_progress(options, increments, buttons, fmt)
    long options;
    short  increments;
    char *buttons;
    char *fmt;
#endif
{
    va_list args;
    short   rv;

    va_start(args, fmt);
    rv = frm_vprogress(options, increments, buttons, fmt, args);
    va_end(args);
    return rv;
}

