/* ---------------- menubar.c ------------------ */

#include "dflat.h"

static void reset_menubar(WINDOW);

static struct {
    int x1, x2;     /* position in menu bar */
    char sc;        /* shortcut key value   */
} menu[10];
static int mctr;

MBAR *ActiveMenuBar;
static MENU *ActiveMenu;

static WINDOW mwnd;
static BOOL Selecting;

static WINDOW Cascaders[MAXCASCADES];
static int casc;
static WINDOW GetDocFocus(void);

/* ----------- SETFOCUS Message ----------- */
static int SetFocusMsg(WINDOW wnd, PARAM p1)
{
	int rtn;
	rtn = BaseWndProc(MENUBAR, wnd, SETFOCUS, p1, 0);
	if (!(int)p1)
		SendMessage(GetParent(wnd), ADDSTATUS, 0, 0);
	else
		SendMessage(NULL, HIDE_CURSOR, 0, 0);
	return rtn;
}

/* --------- BUILDMENU Message --------- */
static void BuildMenuMsg(WINDOW wnd, PARAM p1)
{
    int offset = 3;
    reset_menubar(wnd);
    mctr = 0;
    ActiveMenuBar = (MBAR *) p1;
    ActiveMenu = ActiveMenuBar->PullDown;
    while (ActiveMenu->Title != NULL &&
            ActiveMenu->Title != (void*)-1)    {
        char *cp;
        if (strlen(GetText(wnd)+offset) <
                strlen(ActiveMenu->Title)+3)
            break;
        GetText(wnd) = DFrealloc(GetText(wnd),
            strlen(GetText(wnd))+5);
        memmove(GetText(wnd) + offset+4, GetText(wnd) + offset,
                strlen(GetText(wnd))-offset+1);
        CopyCommand(GetText(wnd)+offset,ActiveMenu->Title,FALSE,
                wnd->WindowColors [STD_COLOR] [BG]);
        menu[mctr].x1 = offset;
        offset += strlen(ActiveMenu->Title) + (3+MSPACE);
        menu[mctr].x2 = offset-MSPACE;
        cp = strchr(ActiveMenu->Title, SHORTCUTCHAR);
        if (cp)
            menu[mctr].sc = tolower(*(cp+1));
        mctr++;
        ActiveMenu++;
    }
    ActiveMenu = ActiveMenuBar->PullDown;
}

/* ---------- PAINT Message ---------- */
static void PaintMsg(WINDOW wnd)
{
	if (Selecting)
		return;
    if (wnd == inFocus)
        SendMessage(GetParent(wnd), ADDSTATUS, 0, 0);
    SetStandardColor(wnd);
    wputs(wnd, GetText(wnd), 0, 0);
    if (ActiveMenuBar->ActiveSelection != -1 &&
            (wnd == inFocus || mwnd != NULL))    {
        char *sel, *cp;
        int offset, offset1;

        sel = DFmalloc(200);
        offset=menu[ActiveMenuBar->ActiveSelection].x1;
        offset1=menu[ActiveMenuBar->ActiveSelection].x2;
        GetText(wnd)[offset1] = '\0';
        SetReverseColor(wnd);
        memset(sel, '\0', 200);
        strcpy(sel, GetText(wnd)+offset);
        cp = strchr(sel, CHANGECOLOR);
        if (cp != NULL)
            *(cp + 2) = background | 0x80;
        wputs(wnd, sel,
            offset-ActiveMenuBar->ActiveSelection*4, 0);
        GetText(wnd)[offset1] = ' ';
        if (mwnd == NULL && wnd == inFocus) {
            char *st = ActiveMenu
                [ActiveMenuBar->ActiveSelection].StatusText;
            if (st != NULL)
                SendMessage(GetParent(wnd), ADDSTATUS,
                    (PARAM)st, 0);
        }
        free(sel);
    }
}

/* ------------ KEYBOARD Message ------------- */
/* is the key one of the globally valid accel keys? */
static void KeyboardMsg(WINDOW wnd, PARAM p1)
{
    MENU *mnu;
	int sel;
    if (mwnd == NULL)    {
        /* ----- search for menu bar shortcut keys ---- */
        int c = tolower((int)p1);
        int a = AltConvert((int)p1);
        int j;
        for (j = 0; j < mctr; j++)    {
            if ((inFocus == wnd && menu[j].sc == c) ||
                    (a && menu[j].sc == a))    {
				SendMessage(wnd, SETFOCUS, TRUE, 0);
                SendMessage(wnd, MB_SELECTION, j, 0);
                return;
            }
        }
    }
    /* -------- search for accelerator keys -------- */
    mnu = ActiveMenu;
    while (mnu->Title != (void *)-1)    {
        struct PopDown *pd = mnu->Selections;
        if (mnu->PrepMenu)
            (*(mnu->PrepMenu))(GetDocFocus(), mnu);
        sel = 0;
        while (pd->SelectionTitle != NULL)    {
            if (pd->Accelerator == (int) p1)    {
                if (pd->Attrib & INACTIVE)
                    beep();
                else    {
                    if (pd->Attrib & TOGGLE)
                        pd->Attrib ^= CHECKED;
                    /* wnd->mnu->Selection is only for highlighting  */
                    /* inside visible popdowns. however, we set the  */
                    /* CurrentMenuSelection for applicat.c, as extra */
                    /* parameter for the ID_WINDOW message... - 0.7c */
                    /* alternative would be using several ID_WINDOWx */
                    /* as actually *only* ID_WINDOW uses C.M.S. yet! */
                    CurrentMenuSelection = sel;
                    SendMessage(GetDocFocus(),
                        SETFOCUS, TRUE, 0);
                    PostMessage(GetParent(wnd),
                        COMMAND, pd->ActionId, 0);
                }
                return;
            }
            pd++;	/* search through all items of the popdown */
            sel++;
        }
        mnu++;	/* search through all possible popdowns */
    }
    switch ((int)p1)    {
        case F1: /* help inside menubar - possible context sensitive */
            if (ActiveMenu == NULL || ActiveMenuBar == NULL)
				break;
			sel = ActiveMenuBar->ActiveSelection;
			if (sel == -1)	{
	    		BaseWndProc(MENUBAR, wnd, KEYBOARD, F1, 0);
				return;
			}
			mnu = ActiveMenu+sel;
			if (mwnd == NULL ||
					mnu->Selections[0].SelectionTitle == NULL) {
               	DisplayHelp(wnd,mnu->Title);
            	return;
			}
            break;
        case DN:	/* suggested by Fox: down arrow to open sub-menu (0.7b) */
        case '\r':
            if (mwnd == NULL &&
                    ActiveMenuBar->ActiveSelection != -1)
                SendMessage(wnd, MB_SELECTION,
                    ActiveMenuBar->ActiveSelection, 0);
            break;
        case F10: /* F10, as ALT, toggles menu bar activation */
            if (wnd != inFocus && mwnd == NULL)    {
                SendMessage(wnd, SETFOCUS, TRUE, 0);
			    if ( ActiveMenuBar->ActiveSelection == -1)
			        ActiveMenuBar->ActiveSelection = 0;
			    SendMessage(wnd, PAINT, 0, 0);
                break;
            }
            /* ------- fall through ------- */
        case ESC:
            if (inFocus == wnd && mwnd == NULL)    {
                ActiveMenuBar->ActiveSelection = -1;
                SendMessage(GetDocFocus(),SETFOCUS,TRUE,0);
                SendMessage(wnd, PAINT, 0, 0);
            }
            break;
#ifdef HOOKKEYB
        case FWD: /* right arrow */
#else
	case RARROW: /* formerly called FWD */
#endif
            ActiveMenuBar->ActiveSelection++;
            if (ActiveMenuBar->ActiveSelection == mctr)
                ActiveMenuBar->ActiveSelection = 0;
            if (mwnd != NULL)
                SendMessage(wnd, MB_SELECTION,
                    ActiveMenuBar->ActiveSelection, 0);
            else 
                SendMessage(wnd, PAINT, 0, 0);
            break;
#ifndef HOOKKEYB:
	case LARROW: /* hope that makes sense */
#endif
        case BS:
            if (ActiveMenuBar->ActiveSelection == 0 ||
					ActiveMenuBar->ActiveSelection == -1)
                ActiveMenuBar->ActiveSelection = mctr;
            --ActiveMenuBar->ActiveSelection;
            if (mwnd != NULL)
                SendMessage(wnd, MB_SELECTION,
                    ActiveMenuBar->ActiveSelection, 0);
            else 
                SendMessage(wnd, PAINT, 0, 0);
            break;
        default:
            break;
    }
}

/* --------------- LEFT_BUTTON Message ---------- */
static void LeftButtonMsg(WINDOW wnd, PARAM p1)
{
    int i;
    int mx = (int) p1 - GetLeft(wnd);
    /* --- compute the selection that the left button hit --- */
    for (i = 0; i < mctr; i++)
        if (mx >= menu[i].x1-4*i &&
                mx <= menu[i].x2-4*i-5)
            break;
    if (i < mctr)
        if (i != ActiveMenuBar->ActiveSelection || mwnd == NULL)
            SendMessage(wnd, MB_SELECTION, i, 0);
}

/* -------------- MB_SELECTION Message -------------- */
static void SelectionMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
    int wd, mx, my;
    MENU *mnu;

	if (!p2)	{
	    ActiveMenuBar->ActiveSelection = -1;
    	SendMessage(wnd, PAINT, 0, 0);
	}
    Selecting = TRUE;
    mnu = ActiveMenu+(int)p1;
    if (mnu->PrepMenu != NULL)
        (*(mnu->PrepMenu))(GetDocFocus(), mnu);
    wd = MenuWidth(mnu->Selections);
    if (p2)    {
		int brd = GetRight(wnd);
        mx = GetLeft(mwnd) + WindowWidth(mwnd) - 1;
		if (mx + wd > brd)
			mx = brd - wd;
        my = GetTop(mwnd) + mwnd->selection;
    }
    else    {
        int offset = menu[(int)p1].x1 - 4 * (int)p1;
        if (mwnd != NULL)
            SendMessage(mwnd, CLOSE_WINDOW, 0, 0);
        ActiveMenuBar->ActiveSelection = (int) p1;
        if (offset > WindowWidth(wnd)-wd)
            offset = WindowWidth(wnd)-wd;
        mx = GetLeft(wnd)+offset;
        my = GetTop(wnd)+1;
    }
    mwnd = CreateWindow(POPDOWNMENU, NULL,
                mx, my,
                MenuHeight(mnu->Selections),
                wd,
                NULL,
                wnd,
                NULL,
                SHADOW);
	if (!p2)	{
	    Selecting = FALSE;
    	SendMessage(wnd, PAINT, 0, 0);
	    Selecting = TRUE;
	}
    if (mnu->Selections[0].SelectionTitle != NULL)    {
        SendMessage(mwnd, BUILD_SELECTIONS, (PARAM) mnu, 0);
		SendMessage(mwnd, SETFOCUS, TRUE, 0);
		SendMessage(mwnd, SHOW_WINDOW, 0, 0);
    }
    Selecting = FALSE;
}

/* --------- COMMAND Message ---------- */
static void CommandMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
	if (p1 == ID_HELP)	{
    	BaseWndProc(MENUBAR, wnd, COMMAND, p1, p2);
		return;
	}
    if (isCascadedCommand(ActiveMenuBar, (int)p1))    {
        /* find the cascaded menu based on command id in p1 */
        MENU *mnu = ActiveMenu+mctr;
        while (mnu->Title != (void *)-1)    {
            if (mnu->CascadeId == (int) p1)    {
                if (casc < MAXCASCADES)    {
                    Cascaders[casc++] = mwnd;
                    SendMessage(wnd, MB_SELECTION,
                        (PARAM)(mnu-ActiveMenu), TRUE);
                }
                break;
            }
            mnu++;
        }
    }
    else     {
        if (mwnd != NULL)
            SendMessage(mwnd, CLOSE_WINDOW, 0, 0);
        SendMessage(GetDocFocus(), SETFOCUS, TRUE, 0);
        PostMessage(GetParent(wnd), COMMAND, p1, p2);
    }
}

/* --------------- CLOSE_POPDOWN Message --------------- */
static void ClosePopdownMsg(WINDOW wnd)
{
    if (casc > 0)
        SendMessage(Cascaders[--casc], CLOSE_WINDOW, 0, 0);
    else     {
        mwnd = NULL;
        ActiveMenuBar->ActiveSelection = -1;
        if (!Selecting)	{
            SendMessage(GetDocFocus(), SETFOCUS, TRUE, 0);
	        SendMessage(wnd, PAINT, 0, 0);
		}
    }
}

/* ---------------- CLOSE_WINDOW Message --------------- */
static void CloseWindowMsg(WINDOW wnd)
{
    if (GetText(wnd) != NULL)    {
        free(GetText(wnd));
        GetText(wnd) = NULL;
    }
    mctr = 0;
    ActiveMenuBar->ActiveSelection = -1;
    ActiveMenu = NULL;
    ActiveMenuBar = NULL;
}

/* --- Window processing module for MENUBAR window class --- */
int MenuBarProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int rtn;

    switch (msg)    {
        case CREATE_WINDOW:
            reset_menubar(wnd);
            break;
        case SETFOCUS:
			return SetFocusMsg(wnd, p1);
        case BUILDMENU:
            BuildMenuMsg(wnd, p1);
            break;
        case PAINT:    
            if (!isVisible(wnd) || GetText(wnd) == NULL)
                break;
            PaintMsg(wnd);
            return FALSE;
        case BORDER:
		    if (mwnd == NULL)
				SendMessage(wnd, PAINT, 0, 0);
            return TRUE;
        case KEYBOARD:
            KeyboardMsg(wnd, p1);
            return TRUE;
        case LEFT_BUTTON:
            LeftButtonMsg(wnd, p1);
            return TRUE;
        case MB_SELECTION:
            SelectionMsg(wnd, p1, p2);
            break;
        case COMMAND:
            CommandMsg(wnd, p1, p2);
            return TRUE;
        case INSIDE_WINDOW:
            return InsideRect(p1, p2, WindowRect(wnd));
        case CLOSE_POPDOWN:
            ClosePopdownMsg(wnd);
            return TRUE;
        case CLOSE_WINDOW:
            rtn = BaseWndProc(MENUBAR, wnd, msg, p1, p2);
            CloseWindowMsg(wnd);
            return rtn;
        default:
            break;
    }
    return BaseWndProc(MENUBAR, wnd, msg, p1, p2);
}

/* ------------- reset the MENUBAR -------------- */
static void reset_menubar(WINDOW wnd)
{
    GetText(wnd) = DFrealloc(GetText(wnd), SCREENWIDTH+5);
    memset(GetText(wnd), ' ', SCREENWIDTH);
    *(GetText(wnd)+WindowWidth(wnd)) = '\0';
}

static WINDOW GetDocFocus(void)
{
	WINDOW wnd = ApplicationWindow;
	if (wnd != NULL)	{
		wnd = LastWindow(wnd);
		while (wnd != NULL && (GetClass(wnd) == MENUBAR ||
							GetClass(wnd) == STATUSBAR))
			wnd = PrevWindow(wnd);
		if (wnd != NULL)
			while (wnd->childfocus != NULL)
				wnd = wnd->childfocus;
	}
	return wnd ? wnd : ApplicationWindow;
}


