#help_index "DolDoc"

public Bool DocLock(CDoc *doc)
{//Make this task have exclusive access to this doc.
        if (!Bt(&doc->locked_flags, DOClf_LOCKED) || doc->owning_task != Fs)
        {
                while (LBts(&doc->locked_flags, DOClf_LOCKED))
                        Yield;
                if (doc->owning_task != Fs)
                        LBEqual(&doc->flags, DOCf_BREAK_UNLOCKED, BreakLock(Fs));
                doc->owning_task = Fs;
                return TRUE;
        }
        else
                return FALSE;
}

public Bool DocUnlock(CDoc *doc)
{//Release exclusive lock on access to doc.
        Bool unlock_break;

        if (Bt(&doc->locked_flags, DOClf_LOCKED) && doc->owning_task == Fs)
        {
                doc->owning_task = 0;
                unlock_break = Bt(&doc->flags, DOCf_BREAK_UNLOCKED);
                LBtr(&doc->locked_flags, DOClf_LOCKED);
                if (unlock_break)
                        BreakUnlock(Fs);
                return TRUE;
        }
        else
                return FALSE;
}

Bool IsEditableText(CDocEntry *doc_e)
{
        if (doc_e->type_u8 == DOCT_TEXT && !(doc_e->de_flags & DOCEG_DONT_EDIT))
                return TRUE;
        else
                return FALSE;
}

CDocEntry *DocEntryNewBase(CDoc *doc, I64 type, I64 de_flags=0, I64 x=0, I64 y=0, I64 page_line_num=0)
{//See also MAllocIdent and CDocEntry.
        CDocEntry *res = CAlloc(sizeof(CDocEntryBase), doc->mem_task);

        res->type                       = type;
        res->de_flags           = de_flags | doldoc.default_de_flags[type.u8[0]];
        res->x                          = x;
        res->y                          = y;
        res->page_line_num      = page_line_num;

        return res;
}

CDocEntry *DocEntryNewTag(CDoc *doc, CDocEntry *doc_ce, U8 *tag)
{
        I64                      l = StrLen(tag);
        CDocEntry       *res = DocEntryNewBase(doc, doc_ce->type, doc_ce->de_flags, doc_ce->x, doc_ce->y, doc_ce->page_line_num);

        res->de_flags   = doc_ce->de_flags; //Override
        res->max_col    = l;
        res->tag                = MAlloc(l + 1, doc->mem_task);
        MemCopy(res->tag, tag, l + 1);
        MemCopy(&res->settings, &doc_ce->settings, sizeof(CDocSettings));

        return res;
}

public U0 DocEntryDel(CDoc *doc, CDocEntry *doc_e)
{//Free entry and all parts of entry.
        if (!doc || doc == doc_e)
                RawPrint(3000, "DocEntryDel");
        else
        {
                if (doc->cur_entry == doc_e)
                        doc->cur_entry = doc_e->next;
                QueueRemove(doc_e);
                if (doc_e->de_flags & DOCEF_TAG)
                        Free(doc_e->tag);
                if (doc_e->de_flags & DOCEF_AUX_STR)
                        Free(doc_e->aux_str);
                if (doc_e->de_flags & DOCEF_DEFINE)
                        Free(doc_e->define_str);
                if (doc_e->de_flags & DOCEF_HTML_LINK)
                        Free(doc_e->html_link);
                if (doc_e->de_flags & DOCEF_LEFT_MACRO)
                        Free(doc_e->left_macro);
                if (doc_e->de_flags & DOCEF_RIGHT_MACRO)
                        Free(doc_e->right_macro);
                if (doc_e->de_flags & DOCEF_BIN_PTR_LINK)
                        Free(doc_e->bin_ptr_link);
                if (doc_e->de_flags & DOCEF_HAS_BIN)
                        DocBinDel(doc, doc_e->bin_data);
                if (doc_e->de_flags & DOCEF_REMALLOC_DATA)
                        Free(doc_e->data);
                Free(doc_e);
        }
}

public I64 DocEntrySize(CDoc *, CDocEntry *doc_e)
{//Mem size of entry and all parts.
        I64 res;

        if (!doc_e)
                return 0;
        res = MSize2(doc_e);
        if (doc_e->de_flags & DOCEF_TAG)
                res += MSize2(doc_e->tag);
        if (doc_e->de_flags & DOCEF_AUX_STR)
                res += MSize2(doc_e->aux_str);
        if (doc_e->de_flags & DOCEF_DEFINE)
                res += MSize2(doc_e->define_str);
        if (doc_e->de_flags & DOCEF_HTML_LINK)
                res += MSize2(doc_e->html_link);
        if (doc_e->de_flags & DOCEF_LEFT_MACRO)
                res += MSize2(doc_e->left_macro);
        if (doc_e->de_flags & DOCEF_RIGHT_MACRO)
                res += MSize2(doc_e->right_macro);
        if (doc_e->de_flags & DOCEF_BIN_PTR_LINK)
                res += MSize2(doc_e->bin_ptr_link);
        if (doc_e->de_flags & DOCEF_REMALLOC_DATA)
                res += MSize2(doc_e->data);

        return res;
}

U0 DocUndoDel(CDoc *, CDocUndo *u)
{
        Free(u->body);
        Free(u);
}

U0 DocUndoCountSet(CDoc *doc)
{
        Bool             unlock = DocLock(doc);
        CDocUndo        *u = doc->undo_head.next;

        doc->undo_count = 0;
        while (u != &doc->undo_head)
        {
                doc->undo_count++;
                u = u->next;
        }
        if (unlock)
                DocUnlock(doc);
}

public CDocEntry *DocEntryCopy(CDoc *doc, CDocEntry *doc_e)
{//Make copy of entry and all parts of entry.
        CDocEntry       *doc_ne;
        CDocBin         *tmpb;
        CTask           *task = doc->mem_task;

        doc_ne = MAllocIdent(doc_e, task);
        doc_ne->next = doc_ne;
        doc_ne->last = doc_ne;
        if (doc_e->de_flags & DOCEF_TAG)
                doc_ne->tag = MAllocIdent(doc_e->tag, task);
        if (doc_e->de_flags & DOCEF_AUX_STR)
                doc_ne->aux_str = MAllocIdent(doc_e->aux_str, task);
        if (doc_e->de_flags & DOCEF_DEFINE)
                doc_ne->define_str = MAllocIdent(doc_e->define_str, task);
        if (doc_e->de_flags & DOCEF_HTML_LINK)
                doc_ne->html_link = MAllocIdent(doc_e->html_link, task);
        if (doc_e->de_flags & DOCEF_LEFT_MACRO)
                doc_ne->left_macro = MAllocIdent(doc_e->left_macro, task);
        if (doc_e->de_flags & DOCEF_RIGHT_MACRO)
                doc_ne->right_macro = MAllocIdent(doc_e->right_macro, task);
        if (doc_e->de_flags & DOCEF_BIN_PTR_LINK)
                doc_ne->bin_ptr_link = MAllocIdent(doc_e->bin_ptr_link, task);
        if (doc_e->de_flags & DOCEF_HAS_BIN)
        {
                tmpb = MAllocIdent(doc_e->bin_data, task);
                tmpb->data = MAllocIdent(doc_e->bin_data->data, task);
                doc_ne->bin_num = doc->cur_bin_num;
                tmpb->num = doc->cur_bin_num++;
                doc_ne->bin_data = tmpb;
                if (doc_e->de_flags & DOCEF_TAG && doc_e->tag && *doc_e->tag)
                        tmpb->tag = StrNew(doc_e->tag, task);
                else
                        tmpb->tag = NULL;
                QueueInsert(tmpb, doc->bin_head.last);
        }
        if (doc_e->de_flags & DOCEF_REMALLOC_DATA)
                doc_ne->data = MAllocIdent(doc_e->data, task);

        return doc_ne;
}

U0 DocRemSoftNewLines(CDoc *doc=NULL, CDocEntry *doc_e=NULL)
{
        CDocEntry       *doc_e2, *saved_ll = doc_e;
        Bool             unlock;

        if (!doc && !(doc = DocPut))
                return;
        unlock = DocLock(doc);
        if (!doc_e)
                doc_e = doc->head.next;
        while (doc_e != doc)
        {
                doc_e2 = doc_e->next;
                if (doc_e->type_u8 == DOCT_SOFT_NEW_LINE)
                {
                        if (doc->cur_entry == doc_e)
                        {
                                doc->cur_entry  = doc_e2;
                                doc->cur_col    = doc->cur_entry->min_col;
                        }
                        DocEntryDel(doc, doc_e);
                }
                else if (saved_ll && doc_e->type_u8 == DOCT_NEW_LINE)
                        break;
                doc_e = doc_e2;
        }
        if (unlock)
                DocUnlock(doc);
}

public U0 DocInsEntry(CDoc *doc, CDocEntry *doc_e)
{//Insert entry into doc, updating its vals.
        U8                      *dst;
        Bool             unlock = DocLock(doc);
        CDocEntry       *doc_ce = doc->cur_entry, *doc_ne;

        doc_e->x                         = doc_ce->x;
        doc_e->y                         = doc_ce->y;
        doc_e->page_line_num = doc_ce->page_line_num;
        MemCopy(&doc_e->settings, &doc_ce->settings, sizeof(CDocSettings));
        if (doc->cur_col > 0 &&
                doc_ce->type_u8 == DOCT_TEXT &&
                !(doc_ce->de_flags & (DOCEF_TAG_CB | DOCEF_DEFINE | DOCEF_AUX_STR | DOCEF_HTML_LINK | DOCEF_BIN_PTR_LINK)) &&
                doc->cur_col < doc_ce->max_col)
        {
                dst = doc_ce->tag + doc->cur_col;
                doc_ne = DocEntryNewTag(doc, doc_ce, dst);
                *dst = 0;
                doc_ne->type    = DOCT_TEXT | doc_ce->type & 0xFFFFFF00;
                doc_ce->max_col = doc->cur_col;
                QueueInsert(doc_ne, doc_ce);
                doc->cur_col = 0;
                doc_ce = doc_ne;
        }
        if (doc_ce->type_u8 == DOCT_TEXT && doc->cur_col >= doc_ce->max_col)
        {
                QueueInsert(doc_e, doc_ce);
                doc->cur_entry = doc_e->next;
        }
        else
        {
                QueueInsert(doc_e, doc_ce->last);
                doc->cur_entry = doc_ce;
        }
        doc->cur_col = doc->cur_entry->min_col;
        DocRemSoftNewLines(doc, doc->cur_entry);
        if (unlock)
                DocUnlock(doc);
}

public U0 DocReset(CDoc *doc, Bool is_old)
{//Del all entries and set doc to defaults.
        Bool                     unlock;
        CDocEntry               *doc_e, *doc_e2;
        CDocUndo                *u, *u8;
        CDocSettings    *s;
        CDocBin                 *b, *b1;

        if (!doc && !(doc = DocPut))
                return;
        unlock = DocLock(doc);
        if (is_old)
        {
                doc_e = doc->head.next;
                while (doc_e != doc)
                {
                        doc_e2 = doc_e->next;
                        DocEntryDel(doc, doc_e);
                        doc_e = doc_e2;
                }
                u = doc->undo_head.next;
                while (u != &doc->undo_head)
                {
                        u8 = u->next;
                        DocUndoDel(doc, u);
                        u = u8;
                }
                b = doc->bin_head.next;
                while (b != &doc->bin_head)
                {
                        b1 = b->next;
                        QueueRemove(b);
                        Free(b->data);
                        Free(b);
                        b = b1;
                }
        }
//Check DocInsDoc
        doc->flags &= DOCF_BREAK_UNLOCKED;
        doc->head.next = doc->head.last = doc;
        QueueInit(&doc->bin_head);
        QueueInit(&doc->undo_head);
        doc->undo_head.time_stamp       = 0;
        doc->undo_count                         = 0;
        doc->cur_bin_num                        = 1;
        doc->dollar_buf_ptr                     = 0;
        doc->cmd_U8                                     = CH_SPACE;
        doc->page_line_num                      = 0;
        doc->best_d                                     = I64_MAX;

        s = &doc->settings_head;
        s->left_margin          = DOC_DEFAULT;
        s->right_margin         = DOC_DEFAULT;
        s->indent                       = 0;
        s->page_len                     = 66;
        s->header                       = DOC_DEFAULT;
        s->footer                       = DOC_DEFAULT;
        s->state                        = DOCSS_NORMAL;
        s->comment_depth        = 0;
        s->paren_depth          = 0;
        s->brace_depth          = 0;
        s->shifted_x            = 0;
        s->shifted_y            = 0;
        s->cur_text_attr        = s->default_text_attr = DOC_ATTR_DEFAULT_TEXT;

        doc_e = &doc->head;
        doc_e->type                             = DOCT_ERROR;
        doc_e->de_flags                 = 0;
        doc_e->x                                = 0;
        doc_e->y                                = 0;
        doc_e->min_col                  = 0;
        doc_e->max_col                  = 0;
        doc_e->page_line_num    = doc->page_line_num;
        MemCopy(&doc_e->settings, s, sizeof(CDocSettings));

        DocTop(doc);
        if (unlock)
                DocUnlock(doc);
}

public U0 DocDel(CDoc *doc)
{//Free entire doc and entries.
        if (!doc || doc->doc_signature != DOC_SIGNATURE_VAL)
                return;
        DocLock(doc);
        doc->doc_signature = 0;
        DocReset(doc, TRUE);
        Free(doc->find_replace);
        Free(doc->dollar_buf);
        DocUnlock(doc);
        Free(doc);
}

public I64 DocSize(CDoc *doc)
{//Mem size of doc and all its entries.
        Bool             unlock;
        CDocEntry       *doc_e;
        CDocUndo        *u;
        CDocBin         *b;
        I64                      res = 0;

        if (!doc || doc->doc_signature != DOC_SIGNATURE_VAL)
                return 0;
        unlock = DocLock(doc);

        doc_e = doc->head.next;
        while (doc_e != doc)
        {
                res += DocEntrySize(doc, doc_e);
                doc_e = doc_e->next;
        }

        u = doc->undo_head.next;
        while (u != &doc->undo_head)
        {
                res += MSize2(u->body);
                res += MSize2(u);
                u = u->next;
        }

        b = doc->bin_head.next;
        while (b != &doc->bin_head)
        {
                res += MSize2(b->data);
                res += MSize2(b);
                b = b->next;
        }

        res += MSize2(doc->find_replace);
        res += MSize2(doc->dollar_buf);
        res += MSize2(doc);
        if (unlock)
                DocUnlock(doc);

        return res;
}

#help_index "DolDoc"
public CDoc *DocNew(U8 *filename=NULL, CTask *task=NULL)
{//MAlloc new DolDoc. (Begin a new doc.)
        CDoc *doc;

        if (!task)
                task = Fs;
        doc = CAlloc(sizeof(CDoc), task);
        if (filename)
                StrCopy(doc->filename.name, filename);
        else
                StrCopy(doc->filename.name, blkdev.tmp_filename);
        doc->find_replace               = CAlloc(sizeof(CEdFindText), task);
        doc->find_replace->scan_fwd             = TRUE;
        doc->find_replace->match_case   = TRUE;
        doc->find_replace->prompt               = TRUE;
        doc->left_click_link    = &EdLeftClickLink;
        doc->dollar_buf_size    = 84;
        doc->dollar_buf                 = MAlloc(doc->dollar_buf_size, task);
        doc->max_entries                = I64_MAX;
        doc->win_task                   = task;
        doc->mem_task                   = task;
        DocReset(doc, FALSE);
        doc->doc_signature              = DOC_SIGNATURE_VAL;

        return doc;
}