#help_index "DolDoc/Form"

U0 DocFormFwd(CDoc *doc, Bool giveup=FALSE)
{
    CDocEntry *doc_e = doc->cur_entry, *doc_e2 = doc_e;
    if (doc->flags & DOCF_FORM)
    {
        if (doc_e == doc)
            goto ff_recover;
        while (!Bt(doldoc.type_flags_form, doc_e->type_u8) && !(doc_e->de_flags & DOCEF_LINK) ||
                doc_e->de_flags & DOCEF_SKIP_IN_FORM)
        {
            doc_e = doc_e->next;
            if (doc_e == doc)
            {
ff_recover:
                doc->cur_col = 0;
                if (!giveup)
                {
                    doc->cur_entry = doc_e->last;
                    DocFormBwd(doc, TRUE);
                }
                else
                    doc->cur_entry = doc;
                return;
            }
        }
    }
    while (doc_e->type_u8 == DOCT_INDENT)
        doc_e = doc_e->next;
    if (doc_e != doc_e2)
    {
        doc->cur_col    = doc_e->min_col;
        doc->cur_entry  = doc_e;
    }
}

U0 DocFormBwd(CDoc *doc, Bool giveup=FALSE)
{
    CDocEntry *doc_e = doc->cur_entry, *doc_e2 = doc_e;

    if (doc->flags & DOCF_FORM)
    {
        while (!Bt(doldoc.type_flags_form, doc_e->type_u8) && !(doc_e->de_flags & DOCEF_LINK) ||
                doc_e->de_flags & DOCEF_SKIP_IN_FORM)
        {
            doc_e = doc_e->last;
            if (doc_e == doc)
            {
                doc->cur_col = 0;
                if (!giveup)
                {
                    doc->cur_entry = doc_e->next;
                    DocFormFwd(doc, TRUE);
                }
                else
                    doc->cur_entry = doc;
                return;
            }
        }
    }
    while (doc_e->type_u8 == DOCT_INDENT)
        doc_e = doc_e->next;
    if (doc_e != doc_e2)
    {
        doc->cur_col    = doc_e->min_col;
        doc->cur_entry  = doc_e;
    }
}

U0 DocDataFormat(CDoc *doc, CDocEntry *doc_e, I64 d=DOCM_CANCEL)
{
    I64              i;
    U8              *ptr, *ptr2;
    CHashDefineStr  *tmph;

    if (doc_e->type_u8 == DOCT_DATA && doc_e->de_flags & DOCEF_AUX_STR ||
        doc_e->type_u8 == DOCT_CHECK_BOX ||
        doc_e->de_flags & DOCEF_LIST)
    {
        if (d == DOCM_CANCEL)
        {
            if (doc_e->de_flags & DOCEF_DEREF_DATA && !(doc_e->de_flags & DOCEF_REMALLOC_DATA))
            {
                if (!(ptr = doc_e->data))
                    return;
            }
            else
                ptr = &doc_e->data;
            switch (doc_e->raw_type)
            {
                case RT_I0:
                case RT_U0:     d = 0;              break;
                case RT_I8:     d = *ptr(I8 *);     break;
                case RT_U8:     d = *ptr(U8 *);     break;
                case RT_I16:    d = *ptr(I16 *);    break;
                case RT_U16:    d = *ptr(U16 *);    break;
                case RT_I32:    d = *ptr(I32 *);    break;
                case RT_U32:    d = *ptr(U32 *);    break;
                default:        d = *ptr(I64 *);
            }
        }
        if (doc_e->type_u8 == DOCT_DATA)
        {
            if (doc_e->de_flags & DOCEF_REMALLOC_DATA)
            {
                ptr = MStrPrint(doc_e->aux_str, d, doc_e->my_format_data);
                i = StrLen(ptr);
                if (!doc_e->data)
                {
                    doc_e->data = CAlloc(2, doc->mem_task);
                    doc_e->len  = MSize(doc_e->data) - 2;
                }
                if (doc_e->len + doc_e->min_col > i)
                    MemCopy(doc_e->tag, ptr, i + 1);
                else
                {
                    ptr2 = MAlloc(i + 8, doc->mem_task);
                    doc_e->len = MSize(ptr2) - doc_e->min_col - 2;   //See DataTagWidth
                    MemCopy(ptr2, ptr, i + 1);
                    Free(doc_e->tag);
                    doc_e->tag = ptr2;
                }
                Free(ptr);
            }
            else
            {
                StrPrint(doc_e->tag, doc_e->aux_str, d, doc_e->my_format_data);
                i = StrLen(doc_e->tag);
            }
            if (doc_e->de_flags & DOCEF_HAS_TERMINATOR)
            {
                doc_e->tag[i++] = '_';
                doc_e->tag[i]   = 0;
            }
            doc_e->max_col = i;
        }
        else if (doc_e->de_flags & DOCEF_LIST)
        {
            if (doc_e->de_flags & DOCEF_DEFINE &&
                (tmph = HashFind(doc_e->define_str, doc->win_task->hash_table, HTT_DEFINE_STR)) &&
                0 <= d < tmph->count)
            {
                ptr = MStrPrint("[%s]", tmph->sub_idx[d]);
                Free(doc_e->tag);
                doc_e->tag = StrNew(ptr, doc->mem_task);
                Free(ptr);
            }
            else
            {
                Free(doc_e->tag);
                doc_e->tag = StrNew("[]", doc->mem_task);
            }
        }
        else
        {
            if (d)
                doc_e->de_flags |= DOCEF_CHECKED_COLLAPSED;
            else
                doc_e->de_flags &= ~DOCEF_CHECKED_COLLAPSED;
        }
    }
}

U0 DocDataScan(CDoc *doc, CDocEntry *doc_e)
{
    I64              i, d;
    U8              *ptr, *ptr1, *ptr2;
    CHashDefineStr  *tmph;

    if (doc_e->type_u8 == DOCT_DATA && doc_e->de_flags & DOCEF_AUX_STR ||
        doc_e->type_u8 == DOCT_CHECK_BOX ||
        doc_e->de_flags & DOCEF_LIST)
    {
        if (doc_e->de_flags & DOCEF_DEREF_DATA && !(doc_e->de_flags & DOCEF_REMALLOC_DATA))
        {
            if (!(ptr = doc_e->data))
                return;
        }
        else
            ptr = &doc_e->data;
        if (doc_e->type_u8 == DOCT_DATA)
        {
            i = StrLen(doc_e->tag);
            if (doc_e->de_flags & DOCEF_HAS_TERMINATOR)
                doc_e->tag[--i] = 0;
            if (i > doc_e->len + doc_e->min_col)
                doc_e->tag[doc_e->len + doc_e->min_col] = 0;
            if (RT_I8 <= doc_e->raw_type <= RT_U32)
            {
                StrScan(doc_e->tag, doc_e->aux_str, &d, doc_e->my_format_data);
                if (doc_e->de_flags & DOCEF_HAS_TERMINATOR)
                    doc_e->tag[i] = '_';
            }
            else if (RT_I64 <= doc_e->raw_type <= RT_UF64)
            {
                if (doc_e->de_flags & DOCEF_REMALLOC_DATA)
                {
                    ptr = MAlloc(i - doc_e->min_col + 8, doc->mem_task);
                    MemCopy(ptr, doc_e->tag + doc_e->min_col, i - doc_e->min_col + 1);
                    Free(doc_e->data);
                    doc_e->data = ptr;
                    doc_e->len  = MSize(ptr) - 1;
                }
                else
                    StrScan(doc_e->tag, doc_e->aux_str, ptr, doc_e->my_format_data);
                if (doc_e->de_flags & DOCEF_HAS_TERMINATOR)
                    doc_e->tag[i] = '_';
                return;
            }
        }
        else if (doc_e->de_flags & DOCEF_LIST)
        {
            d = 0;
            if (doc_e->tag && doc_e->de_flags & DOCEF_DEFINE &&
                (tmph = HashFind(doc_e->define_str, doc->win_task->hash_table, HTT_DEFINE_STR)))
            {
                ptr1 = ptr2 = StrNew(doc_e->tag);
                if (*ptr2 == '[')
                {
                    ptr2++;
                    i = StrLen(ptr2);
                    if (ptr2[i - 1] == ']')
                        ptr2[i - 1] = 0;
                }
                d = ListMatch(ptr2, tmph->data);
                Free(ptr1);
            }
        }
        else
        {
            if (doc_e->de_flags & DOCEF_CHECKED_COLLAPSED)
                d = TRUE;
            else
                d = FALSE;
        }
        switch (doc_e->raw_type)
        {
            case RT_I8:
            case RT_U8:
                *ptr(U8 *) = d;
            case RT_I0:
            case RT_U0:
                break;

            case RT_I16:
            case RT_U16:
                *ptr(U16 *) = d;
                break;

            case RT_I32:
            case RT_U32:
                *ptr(U32 *) = d;
                break;

            default:
                *ptr(I64 *) = d;
        }
    }
}

#help_index "DolDoc/Input;StdIn/DolDoc"
public Bool DocForm(U8 *_d, U8 *class_name=lastclass, I64 dof_flags=0, U8 *header=NULL, U8 *footer=NULL)
{//User input. Supply a class name that has format definitions.
//See ::/Demo/DolDoc/Form.ZC and ::/Demo/LastClass.ZC.
    CMemberList *ml;
    CDocEntry   *doc_e;
    U8          *format;
    CHashClass  *tmpc, *tmpc2;
    CDoc        *doc;
    Bool         res = FALSE;
    I64          old_border_src = Fs->border_src, has_action;

    if (!(tmpc = HashFind(class_name, Fs->hash_table, HTT_CLASS)))
        return FALSE;
    doc = DocNew;
    doc->desc = 'Form';
    if (header)
        DocPrint(doc, "%s", header);
    doc->flags |= DOCF_OVERSTRIKE|DOCF_FORM;
    if (dof_flags & DOF_SIZE_MIN)
        doc->flags |= DOCF_SIZE_MIN;
    ml = tmpc->member_list_and_root;
    while (ml)
    {
        if ((format = MemberMetaData("format", ml)) && (doc_e = DocPrint(doc, "%s", format)))
        {
            tmpc2 = ml->member_class;
            if ((doc_e->type_u8 == DOCT_DATA || doc_e->type_u8 == DOCT_LIST || doc_e->type_u8 == DOCT_CHECK_BOX) &&
                !tmpc2->ptr_stars_count)
            {
                tmpc2 = OptClassFwd(tmpc2);
                tmpc2 -= tmpc2->ptr_stars_count;
                if (tmpc2->type & HTT_INTERNAL_TYPE)
                {
                    if (ml->dim.next)
                    { //Array
                        if (tmpc2->raw_type == RT_U8 && LBtr(&doc_e->de_flags, &DOCEf_DEFAULT_LEN))
                        {
                            doc_e->len = ml->dim.total_count;
                            if (doc_e->de_flags & DOCEF_HAS_TERMINATOR)
                                doc_e->len--;
                            Free(doc_e->tag);  //See DataTagWidth
                            doc_e->tag = MAlloc(doc_e->len + doc_e->min_col + 2, doc->mem_task); //+2 because "_\0"
                        }
                    }
                    else if (LBtr(&doc_e->de_flags, DOCEf_DEFAULT_RAW_TYPE))
                        doc_e->raw_type = tmpc2->raw_type;
                }
            }
            if (doc_e->de_flags & DOCEF_REMALLOC_DATA)
            {
                doc_e->user_data = _d + ml->offset;
                doc_e->data      = *doc_e->user_data(U8 **);
            }
            else
                doc_e->data = _d + ml->offset;
            doc_e->my_format_data = MemberMetaData("data", ml);
            DocDataFormat(doc, doc_e);
        }
        ml = ml->next;
    }
    if (footer)
        DocPrint(doc, "%s", footer);
    if (doc->head.next != doc)
    {
        Fs->border_src = BDS_CONST;
        DocRecalc(doc);
        if (DocEd(doc, dof_flags))
        {
            doc_e = doc->cur_entry;
            res = TRUE;
            if (doc_e != doc)
            {
                if (DocEntryRun(doc, doc_e, TRUE, &has_action) == DOCM_CANCEL && has_action)
                    res = FALSE;
                DocUnlock(doc);
            }
        }
    }
    doc_e = doc->head.next;
    while (doc_e != doc)
    {
        if (doc_e->de_flags & DOCEF_REMALLOC_DATA)
        {
            *doc_e->user_data(U8 **) = doc_e->data;
            doc_e->data = NULL;
        }
        doc_e = doc_e->next;
    }
    DocDel(doc);
    Fs->border_src = old_border_src;

    return res;
}

U0 DocMenuEndTaskCB()
{
    WinToTop;
    throw;
}

public I64 DocMenu(CDoc *m, I64 dof_flags=0)
{//Run menu chooser doc. Returns menu doc unlocked.
    U8          *old_end_cb = Fs->task_end_cb;
    Bool         old_break_shift_esc = LBts(&Fs->task_flags, TASKf_BREAK_TO_SHIFT_ESC);
    CDocEntry   *doc_e;
    I64          old_border_src = Fs->border_src, res = DOCM_CANCEL, has_action;

    Fs->task_end_cb = &DocMenuEndTaskCB;
    try
    {
        if (m)
        {
            m->desc = 'Menu';
            Fs->border_src = BDS_CONST;
dm_restart:
            if (DocEd(m, dof_flags))
            {
                doc_e = m->cur_entry;
                if (doc_e != m)
                {
                    res = DocEntryRun(m, doc_e, TRUE, &has_action);
                    DocUnlock(m);
                    if (!has_action)
                    {
                        res = DOCM_CANCEL;
                        dof_flags |= DOF_DONT_HOME;
                        goto dm_restart;
                    }
                }
            }
        }
    }
    catch
    {
        if (!Fs->except_ch)
        {
            if (!(dof_flags & DOF_INTERCEPT_TASK_END))
                Exit;
            Fs->catch_except = TRUE;
        }
    }
    LBEqual(&Fs->task_flags, TASKf_BREAK_TO_SHIFT_ESC, old_break_shift_esc);
    Fs->border_src  = old_border_src;
    Fs->task_end_cb = old_end_cb;

    return res;
}

public I64 PopUpMenu(CDoc *doc, I64 dof_flags=0)
{//Run menu chooser doc in PopUp win task.
    doc->flags |= DOCF_SIZE_MIN | DOCF_FORM;

    return PopUpPrint("DocMenu(0x%X,0x%X);", doc, dof_flags);
}

public Bool PopUpForm(U8 *_d, U8 *class_name=lastclass, I64 dof_flags=DOF_SIZE_MIN, U8 *header=NULL, U8 *footer=NULL)
{//See ::/Demo/DolDoc/Form.ZC and ::/Demo/LastClass.ZC.
    return PopUpPrint("DocForm(0x%X,0x%X,0x%X,0x%X,0x%X);", _d, class_name, dof_flags, header, footer);
}