/*
 *      File mguimain.cpp
 *      Creates the main shell of the application
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "mguipp.h"

#include "mguitest.hpp"

#define N_VM_LED        18
#define N_GREEN_LED     13
#define VM_UPDATE_TIME 100L

class CaVuMeter:public CmDrawArea
{
    int maxl;
    int maxr;
    int daw;
    int dah;
    int ledw;
    int ledh;
    int hoff;
    int voff;
    CmColor *bgc;
    CmColor *darkbgc;
    struct
    {
	int l_status, r_status;
	CmColor *offcolor, *oncolor;
    }
    vmleds[N_VM_LED];
      public:
      CmTimeout * ptm;
    CmMenu *pmenu;

      private:
    void initLeds(void);

      public:
    void setValues(int xl, int xr)
    {
	maxl = xl;
	maxr = xr;
    };
    void update(void);
    void onExpiredTimeout(void);

    void onExpose(MEvent *);
    void onResize(DRAWAREA_PREF * pref);
    void onInput(MEvent * pe);
    void ItemStartVMeter(int);
    void ItemStopVMeter(int);

      CaVuMeter(CmContainer * p, int w, int h);
     ~CaVuMeter(void);
};

class CaMainShell : public CmShell {
    CmMenu *pmenu;
    CmMenuItem *item_change_text;
    CmMenuItem *item_change_title;
    CmMenuItem *item_unsens;
public:
    CaMainShell(char *title=0, int flags=0);
    void onWMClose(void);
    void ItemExit(int);
    void ItemChangeWindowTitle(int);
    void ItemDrawAreaDemo(int);
    void ItemStatusBarDemo(int);
    void ItemEditFieldDemo(int);
    void ItemRadioToggleDemo(int);
    void ItemSListDemo(int);
    void ItemMSListDemo(int);
    void ItemEditListDemo(int);
    void ItemClippedSListDemo(int);
    void ItemSkippedSListDemo(int);
    void ItemEditWindowDemo(int);
    void ItemSFormDemo(int);
    void ItemClippedFormDemo(int);
    void ItemPagedFormDemo(int);
    void ItemTabbedFormDemo(int);
    void ItemObjManageDemo(int);
    void ItemFontViewDemo(int);
    void ItemSelect(int);
    void ItemUnsensitive(int);
    void ItemChangeTitleText(int);
    void ItemChangeText(int);
    void ItemCheck(int);
    void ItemTableFormDemo(int);
    void openPopupMenu(MEvent *pe);
};

/*
 *  This is the application class. It must be defined to
 *  provide the pure virtual method start().
 */
class CaMGUItest : public CmAppl {
public:
	void start(int, char **);
};

/*
 *  The application instance. Must be defined once to ensure
 *  the CmAppl constructor runs.
 */
CaMGUItest appInstance;

/**************************************************
* Access point to the MGUI application
**************************************************/

void CaMGUItest::
start(int, char **)
{
/*
   *  Log parameter initialization:
   *
   *      log level:              3
   *      n. of stored lines:     10
   *      n. of visble lines:     5
   *      row len:                80
   *      visible row len:        60
   *      log file:               "mguitest.log" (NULL if not needed)
   *
   * To show/hide log window press        <Ctrl + Shift + F1>
   * To change run time log level press   <Ctrl + Shift + F2>
 */
    logInit(3, 10, 5, 80, 60, "mguipp.log");
    enableCustomizing();
    new CaMainShell("MGUI Library Test");
/*
   * Now the program passes control to MGUI library
   * and gains control only through callbacks
 */
    mainLoop();
}

void CaMainShell::onWMClose(void)
{
    if (MMessageDialog("Closing", "Really Quit ?", "Yes", "No", NULL) == 0)
    {
	delete this;
	CmAppl::end(0);
    }
}

/**************************************************
* This function starts the VuMeter timeout causing
* the animation to begin.
**************************************************/
void CaVuMeter::ItemStartVMeter(int)
{
    ptm->start(VM_UPDATE_TIME);
}

/**************************************************
* This callback stops the VuMeter timeout and animation
**************************************************/
void CaVuMeter::ItemStopVMeter(int)
{
    ptm->stop();
}

/**************************************************
* This function shows the checking status of
* a menu item
**************************************************/

void CaMainShell::ItemCheck(int status)
{
    char str[128];

    sprintf(str, "The selected menu item\nhas been %s",
	    (status ? "Activated" : "Deactivated"));
    MMessageDialog("", str, "Ok", 0);
}

/**************************************************
* This function changes a menu item text
**************************************************/

void CaMainShell::ItemChangeText(int)
{
    char text[32];

    strcpy(text, item_change_text->getText());
    if (MInputPrompt("Item Text", "Type in the new text", text, 31, "Exec", "Cancel"))
	item_change_text->setText(text);
}

/**************************************************
* This function changes a menu title text
**************************************************/

void CaMainShell::ItemChangeTitleText(int)
{
    char text[32];
    CmMenu *pd = item_change_title->getMenu();
    const char *oldtext = pd->getText();

    strcpy(text, (oldtext != NULL ? oldtext : "NULL"));
    if (MInputPrompt("Title Text", "Type in the new text", text, 31, "Exec", "Cancel"))
	pd->setText(text);
}

/**************************************************
* This function is called when the user
* selects a menu item
**************************************************/

void CaMainShell::ItemSelect(int status)
{
    if (status)
	MMessageDialog("", "Radio item selected", "Ok", 0);
}

/**************************************************
* This function is called when the user
* selects the menu item that makes itself unsensitive
**************************************************/

void CaMainShell::ItemUnsensitive(int)
{
    item_unsens->setUnsensitive();
}

/************************************************
* Program termination function (MenuItem action).
************************************************/

void CaMainShell::ItemExit(int)
{
    delete seagreen; 
    delete cadetblue; 
    delete darkblue; 
 
    delete this;
    CmAppl::end(0); 
}

/***********************************************
* This function changes the mainshell's title
* using the utility function MInputPrompt()
************************************************/

void CaMainShell::ItemChangeWindowTitle(int)
{
    char title[128];

    getText(title);
    if (MInputPrompt("INPUT", "New title:", title, 40, "Exec", "Cancel"))
	setText(title);
}

/**************************************************
* These functions opens secondary windows
**************************************************/

void CaMainShell::ItemDrawAreaDemo(int)
{
    DrawAreaDemo();
}

void CaMainShell::ItemStatusBarDemo(int)
{
    StatusBarDemo();
}

void CaMainShell::ItemEditFieldDemo(int)
{
    EditFieldDemo();
}

void CaMainShell::ItemRadioToggleDemo(int)
{
    RadioToggleDemo();
}

void CaMainShell::ItemSListDemo(int)
{
    SListDemo();
}

void CaMainShell::ItemMSListDemo(int)
{
    MSelListDemo();
}

void CaMainShell::ItemEditListDemo(int)
{
    EditListDemo();
}

void CaMainShell::ItemClippedSListDemo(int)
{
    ClippedListDemo();
}

void CaMainShell::ItemSkippedSListDemo(int)
{
    SkippedListDemo();
}

void CaMainShell::ItemEditWindowDemo(int)
{
    EditWindowDemo();
}

void CaMainShell::ItemSFormDemo(int)
{
    SFormDemo();
}

void CaMainShell::ItemClippedFormDemo(int)
{
    ClippedFormDemo();
}

void CaMainShell::ItemTableFormDemo(int)
{
    TableFormDemo();
}

void CaMainShell::ItemPagedFormDemo(int)
{
    PagedFormDemo();
}

void CaMainShell::ItemTabbedFormDemo(int)
{
    TabbedFormDemo();
}

void CaMainShell::ItemObjManageDemo(int)
{
    ObjManageDemo();
}

void CaMainShell::ItemFontViewDemo(int)
{
    FontViewDemo();
}

/*
 *  Common CmColor Instances
 */
CmSharedColor *black, *white, *darkblue, *cadetblue, *seagreen;

CaVuMeter::CaVuMeter(CmContainer * p, int w, int h)
:
CmDrawArea(p, w, h)
{
    initLeds();
    setResize(True, False);
    setPopupHelp("Click the right mouse\nbutton to start/stop", 500, 5000);

    ptm = new CmTimeout(this, (VOID_CB)(&CaVuMeter::onExpiredTimeout), VM_UPDATE_TIME, 1);
    setExposeCallback(this, (EVENT_CB)&CaVuMeter::onExpose);
    setInputCallback(this, (EVENT_CB)&CaVuMeter::onInput);
    setResizeCallback(this, (DRAWAREAPREF_CB)&CaVuMeter::onResize);
}

CaVuMeter::~CaVuMeter(void)
{
    delete ptm;
}

/**************************************************
* This function initializes the V-Meter led parameters
**************************************************/
void CaVuMeter::
initLeds(void)
{
    int i;

    hoff = 4;
    voff = 10;
    ledw = 14;
    ledh = 6;
    daw = N_VM_LED * ledw + (N_VM_LED + 1) * hoff;
    dah = 2 * ledh + 3 * voff;
    bgc = darkblue;
    darkbgc = new CmSharedColor(bgc, 90);

    for (i = 0; i < N_GREEN_LED; i++)
    {
	vmleds[i].l_status = 0;
	vmleds[i].r_status = 0;
	vmleds[i].offcolor = new CmSharedColor(0x48, 0x60, 0x50);
	vmleds[i].oncolor = new CmSharedColor(0x40, 0xe0, 0x40);
    }

    vmleds[i].l_status = 0;
    vmleds[i].r_status = 0;
    vmleds[i].offcolor = new CmSharedColor(0x65, 0x58, 0x48);
    vmleds[i].oncolor = new CmSharedColor(0xe0, 0xa0, 0x40);

    for (i++; i < N_VM_LED; i++)
    {
	vmleds[i].l_status = 0;
	vmleds[i].r_status = 0;
	vmleds[i].offcolor = new CmSharedColor(0x60, 0x48, 0x50);
	vmleds[i].oncolor = new CmSharedColor(0xf0, 0x00, 0x40);
    }
}

/**************************************************
* This function calculates random values for the
* VuMeter's left and right levels, then queries the
* VuMeter to update itself.
**************************************************/
void CaVuMeter::
onExpiredTimeout(void)
{
    int xl, xr;

    xl = (int) ((float) rand() / RAND_MAX * N_VM_LED);
    xr = xl + (int) ((float) rand() / RAND_MAX * 5 - 2);

    setValues(xl, xr);
    update();
}

/**************************************************
* This function updates the VuMeter led lights
**************************************************/
void CaVuMeter::
update(void)
{
    int i, x, y, w, h;

    w = ledw;
    h = ledh;
    for (i = 0; i < N_VM_LED; i++)
    {
	x = (i + 1) * hoff + i * w;
	y = voff;
	if (i <= maxl && !vmleds[i].l_status)
	    drawFilledBox(x, y, w, h, vmleds[i].oncolor);
	else if (i > maxl && vmleds[i].l_status)
	    drawFilledBox(x, y, w, h, vmleds[i].offcolor);
	vmleds[i].l_status = (i <= maxl);
	y += voff + h;
	if (i <= maxr && !vmleds[i].r_status)
	    drawFilledBox(x, y, w, h, vmleds[i].oncolor);
	else if (i > maxr && vmleds[i].r_status)
	    drawFilledBox(x, y, w, h, vmleds[i].offcolor);
	vmleds[i].r_status = (i <= maxr);
    }
}
/**************************************************
* This function draws the VuMeter when a portion of
* its area becomes visible.
**************************************************/
void CaVuMeter::
onExpose(MEvent *)
{
    int i, x, y, w, h;

    drawFilledBox(0, 0, getWidth(), getHeight(), bgc);
    w = ledw;
    h = ledh;
    for (i = 0; i < N_VM_LED; i++)
    {
	x = (i + 1) * hoff + i * w;
	y = voff;
	drawBox(x - 1, y - 1, w + 2, h + 2, darkbgc);
	drawFilledBox(x, y, w, h,
	     (vmleds[i].l_status ? vmleds[i].oncolor : vmleds[i].offcolor));
	y += voff + h;
	drawBox(x - 1, y - 1, w + 2, h + 2, darkbgc);
	drawFilledBox(x, y, w, h,
	     (vmleds[i].r_status ? vmleds[i].oncolor : vmleds[i].offcolor));
    }
}

/**************************************************
*  Function that recomputes the VuMeter dimensions
*  as the Object is resized.
**************************************************/
void CaVuMeter::
onResize(DRAWAREA_PREF * pref)
{
    ledw = (pref->new_w - (N_VM_LED + 1) * hoff) / N_VM_LED;
    ledh = (pref->new_h - 3 * voff) / 2;
}

/**************************************************
*  This function opens the popup menu when the right
*  mouse button is pressed within the VuMeter.
*  X and Y positions for this popup are those in the
*  mouse event.
**************************************************/
void CaVuMeter::
onInput(MEvent * pe)
{
    if (pe->type == E_BUTTON_PRESS && pe->mouse.button == Button3)
	pmenu->popup(pe->mouse.x_root, pe->mouse.y_root);
}

/**************************************************
*  This function opens a popup menu when the right
*  mouse button is pressed within the Object.
*  X and Y positions for this popup are those in the
*  mouse event.
**************************************************/
void CaMainShell::
openPopupMenu(MEvent *pe)
{
    if (pe->mouse.button == Button3)
	pmenu->popup(pe->mouse.x_root, pe->mouse.y_root);
}

CaMainShell::CaMainShell(char *title, int flags)
    : CmShell(title, flags)
{
    CmMenuBar *menu;
    CmColForm *form;
    CmLabel *label;
    CmMenu *menupd;
    CmMenu *subpd;
    CmMenuItem *item;
    CmMenuRadioItem *ritem;
    CmSFile *sfile;
    CaVuMeter *vmeter;

/*
   *  Colors allocation
 */
    black = new CmSharedColor(0, 0, 0);
    white = new CmSharedColor(255, 255, 255);
    cadetblue = new CmSharedColor(0x60, 0x85, 0x95);
    seagreen = new CmSharedColor(0x70, 0xa5, 0xa0);
    darkblue = new CmSharedColor(cadetblue, (MGetNColors() > 16 ? 65 : 100));

    MSetMessageDialogRGB(120, 140, 130);
    MSetInputPromptRGB(150, 120, 120);

    setWMCloseCallback(this, (VOID_CB)&CaMainShell::onWMClose);
    menu = new CmMenuBar(this, HELV_MEDIUM);
    menu->setAcceleratorKey(F10);
	menu->setBackgroundImageFile("tile1.bmp", BI_TILED);
    menupd = new CmMenu(menu, "File");

//      MPullDownAddItem(menupd, "Color Browser", ColPickCB, 0L);
    new CmMenuItem(menupd, "Font browser...", this, (MENUITEM_CB)&CaMainShell::ItemFontViewDemo);
    new CmMenuItem(menupd, "Shell title...", this, (MENUITEM_CB)&CaMainShell::ItemChangeWindowTitle);
    new CmMenuSeparator(menupd);
    new CmMenuItem(menupd, "Quit", this, (MENUITEM_CB)&CaMainShell::ItemExit, C_Q, "Ctrl+Q");

    menupd = new CmMenu(menu, "Objects");
    item = new CmMenuItem(menupd, "Form");
    subpd = new CmMenu(item);
    new CmMenuItem(subpd, "Table Form", this, (MENUITEM_CB)&CaMainShell::ItemTableFormDemo);
    new CmMenuItem(subpd, "Scrolled Form", this, (MENUITEM_CB)&CaMainShell::ItemSFormDemo);
    new CmMenuItem(subpd, "Clipped Form", this, (MENUITEM_CB)&CaMainShell::ItemClippedFormDemo);
    new CmMenuItem(subpd, "Paged Form", this, (MENUITEM_CB)&CaMainShell::ItemPagedFormDemo);
    new CmMenuItem(subpd, "Tabbed Form", this, (MENUITEM_CB)&CaMainShell::ItemTabbedFormDemo);
    new CmMenuItem(subpd, "Object Management", this, (MENUITEM_CB)&CaMainShell::ItemObjManageDemo);
    item = new CmMenuItem(menupd, "List");
    subpd = new CmMenu(item);
    new CmMenuItem(subpd, "Scrolled List", this, (MENUITEM_CB)&CaMainShell::ItemSListDemo);
    new CmMenuItem(subpd, "MultiSel List", this, (MENUITEM_CB)&CaMainShell::ItemMSListDemo);
    new CmMenuItem(subpd, "EditSel List", this, (MENUITEM_CB)&CaMainShell::ItemEditListDemo);
    new CmMenuItem(subpd, "Clipped List", this, (MENUITEM_CB)&CaMainShell::ItemClippedSListDemo);
    new CmMenuItem(subpd, "Skipped List", this, (MENUITEM_CB)&CaMainShell::ItemSkippedSListDemo);
    new CmMenuSeparator(menupd);
    new CmMenuItem(menupd, "Edit Field", this, (MENUITEM_CB)&CaMainShell::ItemEditFieldDemo);
    new CmMenuItem(menupd, "Edit Window", this, (MENUITEM_CB)&CaMainShell::ItemEditWindowDemo);
    new CmMenuSeparator(menupd);
    new CmMenuItem(menupd, "Radio && Toggle", this, (MENUITEM_CB)&CaMainShell::ItemRadioToggleDemo);
    new CmMenuItem(menupd, "Draw Area", this, (MENUITEM_CB)&CaMainShell::ItemDrawAreaDemo);
    new CmMenuItem(menupd, "Status Bar", this, (MENUITEM_CB)&CaMainShell::ItemStatusBarDemo);

    menupd = new CmMenu(menu, "Pulldown");
    new CmMenuToggleItem(menupd, "Item 1", this, (MENUITEM_CB)&CaMainShell::ItemCheck);
    new CmMenuSeparator(menupd);

    item = new CmMenuItem(menupd, "Subpulldown");
    item_change_text = new CmMenuItem(menupd, "Change me", this, (MENUITEM_CB)&CaMainShell::ItemChangeText);
    item_change_title = new CmMenuItem(menupd, "Change title", this, (MENUITEM_CB)&CaMainShell::ItemChangeTitleText);
    item_unsens = new CmMenuItem(menupd, "Unsensitive", this, (MENUITEM_CB)&CaMainShell::ItemUnsensitive, C_U, "Ctrl+U");

    subpd = new CmMenu(item);
    new CmMenuToggleItem(subpd, "subItem 2-1", this, (MENUITEM_CB)&CaMainShell::ItemCheck);
    new CmMenuToggleItem(subpd, "subItem 2-2", this, (MENUITEM_CB)&CaMainShell::ItemCheck);
    item = new CmMenuItem(subpd, "Subpulldown");

    subpd = new CmMenu(item);
    new CmMenuRadioItem(subpd, "subItem 1-1", this, (MENUITEM_CB)&CaMainShell::ItemSelect);
    new CmMenuRadioItem(subpd, "subItem 1-2", this, (MENUITEM_CB)&CaMainShell::ItemSelect);
    ritem = new CmMenuRadioItem(subpd, "subItem 1-3", this, (MENUITEM_CB)&CaMainShell::ItemSelect);
    ritem->setCheckStatus(True);
    new CmMenuSeparator(subpd);
    new CmMenuRadioItem(subpd, "subItem 2-1", this, (MENUITEM_CB)&CaMainShell::ItemSelect);
    ritem = new CmMenuRadioItem(subpd, "subItem 2-2", this, (MENUITEM_CB)&CaMainShell::ItemSelect);
    ritem->setCheckStatus(True);
    new CmMenuItem(subpd, "Change me", this, (MENUITEM_CB)&CaMainShell::ItemChangeText);
    new CmMenuItem(subpd, "Change title", this, (MENUITEM_CB)&CaMainShell::ItemChangeTitleText);

    menu->enableOptions("menu", MO_ALL);

    label = new CmLabel(this, "Ctrl+Shift+MouseClick here to customize");
    label->enableOptions("label_1", MO_ALL);

    form = new CmColForm(this);
    form->setShadow(WS_ETCHED_IN, 2, 1);
    form->setColor(darkblue, black);

    vmeter = new CaVuMeter(form, 100, 40);

    vmeter->pmenu = new CmMenu(HELV_SMALL);

    vmeter->pmenu->setColor(seagreen, white);
    new CmMenuItem(vmeter->pmenu, "Start", vmeter, (MENUITEM_CB)&CaVuMeter::ItemStartVMeter);
    new CmMenuItem(vmeter->pmenu, "Stop", vmeter, (MENUITEM_CB)&CaVuMeter::ItemStopVMeter);

    label = new CmLabel(form, "Click here the right mouse\n\
button to open a popup menu", FIXED_MEDIUM);
    label->setAlignment(RIGHT_ALIGN);
    label->setColor(darkblue, white);
    label->setResize(False, False);
    label->setShadow(WS_SHADOW_IN, 1, 0);
    label->setEventCallback(this, (EVENT_CB)&CaMainShell::openPopupMenu, EM_BUTTON_PRESS);

    pmenu = menupd;

    sfile = new CmSFile(this, __FILE__, FIXED_MEDIUM, 16, 74);
    sfile->setColor(darkblue, white);
    realize();
}

