#help_index "Menus"
#help_file "::/Doc/Menus"

CTask *MenuTask()
{
        CTask *res = sys_focus_task;

        while (res && !res->cur_menu)
                res = res->parent_task;

        return res;
}

CMenuEntry *sys_cur_submenu_entry = NULL;

public CMenuEntry *MenuSubEntryFind(CMenuEntry *haystack_first, U8 *needle_entry_name)
{//You probably don't need this. Use dir / and MenuEntryFind().
        while (haystack_first)
        {
                if (!StrCompare(haystack_first->name, needle_entry_name))
                        return haystack_first;
                haystack_first = haystack_first->next;
        }

        return NULL;
}

public CMenuEntry *MenuEntryFind(CMenu *haystack_menu, U8 *needle_full_name)
{//Find pulldown entry. Fs->cur_menu is probably the menu you want.
//Just 2 levels -- across top and down are valid, currently.
        U8                      *st, *st2;
        CMenuEntry      *tmpse;

        if (!haystack_menu || !needle_full_name)
                return NULL;
        st = StrNew(needle_full_name);
        st2 = StrNew(needle_full_name);
        tmpse = (&haystack_menu->sub)(U8 *) - offset(CMenuEntry.sub);
        while (*st && tmpse)
        {
                StrFirstRemove(st, "/", st2);
                tmpse=MenuSubEntryFind(tmpse->sub, st2);
        }
        Free(st);
        Free(st2);

        return tmpse;
}

CMenuEntry *MenuNewSub(CCompCtrl *cc, CTask *task)
{
        CMenuEntry *tmpme = NULL, *tmpse;

        if (cc->token==TK_IDENT)
        {
                tmpme = CAlloc(sizeof(CMenuEntry),task);
                if (StrLen(cc->cur_str) > 31)
                        cc->cur_str[31] = 0;
                StrCopy(tmpme->name, cc->cur_str);
                if (Lex(cc) == '(')
                {
                        tmpme->message_code = MESSAGE_KEY_DOWN_UP;
                        if (Lex(cc) != ',' && cc->token != ')')
                                tmpme->message_code = LexExpressionI64(cc);
                        if (cc->token == ',')
                                Lex(cc);
                        if (cc->token != ',' && cc->token != ')')
                                tmpme->arg1 = LexExpressionI64(cc);
                        if (cc->token == ',')
                                Lex(cc);
                        if (cc->token != ',' && cc->token != ')')
                                tmpme->arg2 = LexExpressionI64(cc);
                        if (cc->token != ')')
                                LexExcept(cc, "Missing ')' at ");
                        if (Lex(cc) != ';')
                                LexExcept(cc, "Missing ';' at");
                        Lex(cc); //Skip ;
                }
                else if (cc->token == '{')
                {
                        Lex(cc); //Skip {
                        tmpme->dir = TRUE;
                        tmpse = &tmpme->sub;
                        while (tmpse && cc->token != '}')
                                tmpse = tmpse->next = MenuNewSub(cc, task);
                        if (cc->token != '}')
                                LexExcept(cc, "Missing '}' at ");
                        else
                                Lex(cc); //Skip }
                }
                else
                        LexExcept(cc, "Expecting '{' at ");
        }
        return tmpme;
}

public CMenu *MenuNew(U8 *st, I64 flags = 0, CTask *task = NULL)
{//Parse a menu. You probably don't need this.
        CMenu           *m;
        CMenuEntry      *tmpse;
        CCompCtrl       *cc = CompCtrlNew(st, CCF_DONT_FREE_BUF);

        if (!task)
                task = Fs;
        Lex(cc);
        m = CAlloc(sizeof(CMenu), task);
        m->task  = task;
        m->flags = flags;
        m->attr  = BLUE << 4 + YELLOW;
        tmpse = &m->sub;
        while (tmpse)
                tmpse = tmpse->next = MenuNewSub(cc, task);
        CompCtrlDel(cc);

        return m;
}

public CMenu *MenuFile(U8 *filename, I64 flags = 0, CTask *task = NULL)
{//Parse a pulldown menu file. You probably don't need this.
        CMenu   *m;
        U8              *st = MStrPrint("#include \"%s\"", filename);

        m = MenuNew(st, flags, task);
        Free(st);

        return m;
}

U0 MenuDelSub(CMenuEntry *tmpme)
{
        CMenuEntry *tmpse, *tmpse1;

        if (tmpme)
        {
                tmpse = tmpme->sub;
                while (tmpse)
                {
                        tmpse1 = tmpse->next;
                        MenuDelSub(tmpse);
                        tmpse = tmpse1;
                }
                Free(tmpme);
        }
}

public U0 MenuDel(CMenu *m)
{//Delete a manu. You probably don't need this.
        CMenuEntry *tmpme, *tmpme1;

        if (!m)
                return;
        tmpme = m->sub;
        while (tmpme)
        {
                tmpme1 = tmpme->next;
                MenuDelSub(tmpme);
                tmpme = tmpme1;
        }
        Free(m);
}

I64 MenuEntryWidth(CMenuEntry *tmpme)
{
        I64                      res = StrLen(tmpme->name);
        CMenuEntry      *tmpse = tmpme->sub;

        while (tmpse)
        {
                res = MaxI64(res, StrLen(tmpse->name));
                tmpse = tmpse->next;
        }

        return res + 1;
}

public CMenu *MenuPush(U8 *st)
{//Save old pulldown menu and replace with new from str.
        CMenu *m = MenuNew(st);

        m->next = Fs->cur_menu;
        Fs->cur_menu = m;

        return m;
}

public CMenu *MenuFilePush(U8 *filename)
{//Save old pulldown menu and replace with new from file.
        CMenu *m = MenuFile(filename);

        m->next = Fs->cur_menu;
        Fs->cur_menu = m;

        return m;
}

public U0 MenuPop()
{//Restore old pulldown menu. Delete just-deactivated menu.
        CMenu *m = Fs->cur_menu;

        if (!m)
                return;
        Fs->cur_menu = m->next;
        MenuDel(m);
}

U0 DrawMenu(CDC *dc)
{
        CMenu           *m;
        CMenuEntry      *tmpme, *tmpse, *cur_submenu = NULL;
        U8                      *st = NULL;
        CTask           *task = MenuTask;
        I64                      i, w, x0, y0, x1 = mouse.pos.x, y1 = mouse.pos.y;

        if (!TaskValidate(task) || !(m = task->cur_menu))
        {
                sys_cur_submenu_entry = NULL;
                return;
        }
        dc->color = m->attr >> 4;
        GrRect(dc, 0, 0, GR_WIDTH, FONT_HEIGHT);
        x0 = 0;
        tmpme = m->sub;
        while (tmpme)
        {
                w = MenuEntryWidth(tmpme) * FONT_WIDTH;
                if (x0 <= x1 < x0 + w) {
                        if (0 <= y1 < FONT_HEIGHT)
                        {
                                dc->color = m->attr & 15;
                                GrRect(dc, x0, 0, w, FONT_HEIGHT);
                                dc->color = m->attr >> 4;
                        }
                        else
                                dc->color = m->attr & 15;
                        GrPrint(dc, x0, 0, "%s", tmpme->name);
                        y0 = FONT_HEIGHT;
                        tmpse = tmpme->sub;
                        while (tmpse)
                        {
                                if (tmpse->checked)
                                        i = m->attr ^ 0xFF;
                                else
                                        i = m->attr;
                                if (y0 <= y1 < y0 + FONT_HEIGHT)
                                {
                                        if (tmpse->message_code == MESSAGE_KEY_DOWN ||
                                                tmpse->message_code == MESSAGE_KEY_DOWN_UP)
                                        {
                                                if (!tmpse->arg2)
                                                        tmpse->arg2 = Char2ScanCode(tmpse->arg1);
                                                st = ScanCode2KeyName(tmpse->arg2);
                                        }
                                        sys_cur_submenu_entry = cur_submenu = tmpse;
                                        dc->color = i & 15;
                                        GrRect(dc, x0, y0, w, FONT_HEIGHT);
                                        dc->color = i >> 4;
                                        GrPrint(dc, x0, y0, "%s", tmpse->name);
                                        if (st)
                                        {
                                                dc->color = i >> 4;
                                                GrRect(dc, x0 + w, y0 - FONT_HEIGHT,
                                                                (StrLen(st) + 1) * FONT_WIDTH, FONT_HEIGHT * 3);
                                                dc->color = i & 15;
                                                GrPrint(dc, x0 + w, y0, "%s", st);
                                                Free(st);
                                        }
                                }
                                else
                                {
                                        dc->color = i >> 4;
                                        GrRect(dc, x0, y0, w, FONT_HEIGHT);
                                        dc->color = i & 15;
                                        GrPrint(dc, x0, y0, "%s", tmpse->name);
                                }
                                y0 += FONT_HEIGHT;
                                tmpse = tmpse->next;
                        }
                }
                else
                {
                        dc->color = m->attr & 15;
                        GrPrint(dc, x0, 0, "%s", tmpme->name);
                }
                x0 += w;
                tmpme = tmpme->next;
        }
        sys_cur_submenu_entry = cur_submenu;
}