#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.CC and ::/Demo/LastClass.CC.
        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.CC and ::/Demo/LastClass.CC.
        return PopUpPrint("DocForm(0x%X,0x%X,0x%X,0x%X,0x%X);", _d, class_name, dof_flags, header, footer);
}