app.cc
//*************************************************************************
// MODULE : App - "application" body code. *
// AUTHOR : Ron Chernich *
// PURPOSE: Open a graphics screen, instanciate all the base controls *
// types (which will be empty) and provide a messaging system *
// on which an application can be built. *
// HISTORY: *
// 22-JAN-93 First (MSC/C++ 7.00) version *
// 28-JAN-93 Window class added and App amended accordingly *
// 11-APR-93 Mouse activation code added *
// 27-OCT-93 Mouse pointer should be hidden during Window open/close *
//*************************************************************************
#include "app.hh"
/////////////
// Timer object which should be private to App, but I can't make that work!
//
// NOTE: Because declaring a static class data member does "define" it,
// actual allocation of storage must take place elsewhere.. this is where.
//
Timer Clock;
UINT32 nTicks;
////////////////////////// App Class Routines /////////////////////////////
//
// Constructor initialises the graphic subsystem and (if successful)
// creates the window title bar, then color fills the desk-top space.
//
App::App (char *st)
{
if (!GfxInit())
bGfxOpen = FALSE;
else {
bGfxOpen = TRUE;
pMain = new AppWindow;
if (st)
pMain->Title(st);
}
}
/////////////////////
// Destructor performs DOS text mode restoration (if in graphics mode)
// and destroys the system menu and associated gadgets..
//
App::~App ()
{
if (bGfxOpen) {
delete pMain;
GfxClose();
}
}
//////////////////
// This is the message buffer stuffer. It is run at every reasonable
// opportunity to extract control events (ALT-key presses and eventually,
// mouse left-button presses) from the ring buffer, see if they correspond
// to any valid control acellerators, and if so, create a message in the
// FIFO message queue about the event for later dispatch.
//
void App::AppRun (void)
{
char ch;
INT16 n = 0;
point HotSpot;
if (Mickey.GetEvent(HotSpot))
n = pMain->Scan(HotSpot);
else
if (KbdIn.KeyHit(_ALT)) {
ch = KbdIn.KeyGet(_ALT);
n = pMain->Scan(ch);
}
if (n) {
msgstruct m = { RC_Userapp, RC_Click, (UINT16)n, 0L };
if (n == ID_HELP)
m.message = RC_HelpKey;
if (n == ID_CLOSE)
m.message = RC_Close;
if (n == ID_REDRAW)
m.message = RC_Paint;
pm = (msgstruct*)DblAppend((void*)&m, (UINT16)sizeof(msgstruct));
}
}
////////////////
// The application calls here to see if there are any messages for it..
// We'll take the opprotunity to run the message extractor, the remove
// the first message targeted for the client, copy the relevant data into
// the passed var references, and return a result.
// RETURNS: TRUE .. message data loaded into parameters referenced
// FALSE .. nothing for you yet.
//
BOOL App::AppGetMsg (UINT16 &msg, UINT16 &wParam, UINT32 &lParam)
{
AppRun();
pm = (msgstruct*)DblGetHead();
while (pm) {
if (pm->dest == RC_Userapp) {
msg = pm->message;
wParam = pm->wParam;
lParam = pm->lParam;
DblDelete();
return TRUE;
}
pm = (msgstruct*)DblGetNext();
}
return FALSE;
}
/////////////////////////// Window Class Routines //////////////////////////
//
// Window killer - close all windows.
//
AppWindow::~AppWindow (void)
{
pw = (PWIN)DblGetTail();
while (pw) {
if (pw->pi) {
GfxPutImage(pw->r.ul.x, pw->r.ul.y, pw->pi);
GfxFreeImage(pw->pi);
}
if (pw->Sys)
delete pw->Sys;
pw->Pb.BtnKill(0);
pw->Rb.BtnKill(0);
pw->Cb.BtnKill(0);
DblDelete();
pw = (PWIN)DblGetPrev();
}
}
///////////////
// delete the topmost (latest) window
//
void AppWindow::Close (void)
{
if (pw = (PWIN)DblGetTail()) {
if (pw->pi) {
Mickey.HidePointer();
GfxPutImage(pw->r.ul.x, pw->r.ul.y, pw->pi);
GfxFreeImage(pw->pi);
pw->pi = NULL;
Mickey.ShowPointer();
}
if (pw->Sys)
delete pw->Sys;
DblDelete();
pw = (PWIN)DblGetTail();
}
}
///////////
// Get state of a check box by ID
//
BOOL AppWindow::CBstate (UINT16 id, BOOL &bState)
{
pw = (PWIN)DblGetTail();
while (pw) {
if (pw->Cb.State(id, bState))
return TRUE;
if (pw->mode == RC_Modal)
break;
pw = (PWIN)DblGetPrev();
}
return FALSE;
}
/////////////
// Change the title text of the current window.
//
void AppWindow::Title (char *st)
{
pw = (PWIN)DblGetTail();
if ((pw->mode == RC_Modeless) && pw->Sys)
pw->Sys->SysTitle(st, pw->r);
}
//////////////////
// See if the passed Hot Key character is associated with any control in
// the current (tail-end) window. If not, while there are more windows
// and the current one is not type MODAL, check out the previous one..
// RETURNS: ID of control activated or ZERO for unregistered key
//
UINT16 AppWindow::Scan (char ch)
{
UINT16 n, k;
pw = (PWIN)DblGetTail();
while (pw) {
n = (k = pw->Pb.Scan(ch)) ? k :
(k = pw->Rb.Scan(ch)) ? k :
(k = pw->Cb.Scan(ch)) ? k : pw->Sys->Scan(ch);
if (n)
return n;
if (pw->mode == RC_Modal)
break;
pw = (PWIN)DblGetPrev();
}
return (UINT16)0;
}
//////////////////
// Very similar to the above, but uses a point..
// RETURNS: ID of control activated or ZERO for unregistered key
//
UINT16 AppWindow::Scan (point& Pt)
{
UINT16 n, k;
pw = (PWIN)DblGetTail();
while (pw) {
n = (k = pw->Pb.Scan(Pt)) ? k :
(k = pw->Rb.Scan(Pt)) ? k :
(k = pw->Cb.Scan(Pt)) ? k : pw->Sys->Scan(Pt);
if (n)
return n;
if (pw->mode == RC_Modal)
break;
pw = (PWIN)DblGetPrev();
}
return (UINT16)0;
}
////////////////
// create a window control gadget.
// RETURNS: TRUE .. control created
// FALSE .. invalid type, out of memory, something bad..
//
BOOL AppWindow::Create (UINT16 ctrl, UINT16 id, char *st,
INT16 p1, INT16 p2, INT16 p3, INT16 p4)
{
pw = (PWIN)DblGetTail();
switch (ctrl) {
case RC_PushBtn:
return pw->Pb.Create(id, st, p1, p2, p3, p4);
case RC_RadioBtn:
return pw->Rb.Create(id, st, p1, p2, (BOOL)p3);
case RC_CheckBox:
return pw->Cb.Create(id, st, p1, p2, (BOOL)p3);
}
return FALSE;
}
/////////////////
// Kill off a control..
//
BOOL AppWindow::Destroy (UINT16 ctrl, UINT16 id)
{
switch (ctrl) {
case RC_PushBtn: return pw->Pb.Destroy(id);
case RC_RadioBtn: return pw->Rb.Destroy(id);
case RC_CheckBox: return pw->Cb.Destroy(id);
}
return FALSE;
}
/////////////////
// Open a new window in mode using the passed corners. Mode and params:
// RC_Modal - No system menu/close gadget
// Border/Interior colors are n2/n1
// If is not NULL, window gets title bar + title
// Area occupied by window will be saved and restored if possible.
// RC_Modeless - Window gets Title bar, close gadget and optional title
// Window has no border, interior color = n2
// Occupied area not saved. User responsible for re-draw.
// RETURNS: TRUE .. window opened and "running"
// FALSE .. illegal mode.
//
BOOL AppWindow::Open (UINT16 md, char *st, INT16 x1, INT16 y1,
INT16 x2, INT16 y2, UINT16 n1, UINT16 n2)
{
WIN w;
BOOL bCreated = FALSE;
w.mode = md;
w.r = rect(x1, y1, x2, y2);
Mickey.HidePointer();
if (w.mode == RC_Modeless) {
w.pi = NULL;
w.Sys = new SysMenu;
GfxRect(x1, y1+SYS_Height+1, x2, y2, GFX_Fill, n1);
GfxRect(x1, y1, x2, y2, GFX_Frame, _Black);
bCreated = TRUE;
}
if (w.mode == RC_Modal) {
w.Sys = NULL;
w.pi = GfxGetImage(x1, y1, x2, y2);
GfxRect(x1, y1, x2, y2, GFX_Fill, n1);
GfxRect(x1+4, y1+4, x2-4, y2-4, GFX_Fill, n2);
GfxRect(x1, y1, x2, y2, GFX_Frame, _Black);
if (st) {
GfxRect(x1+5, y1+5, x2-5, y1+SYS_Height+4, GFX_Fill, n1);
GfxSetClip(x1+5, y1+5, x2-5, y1+SYS_Height+4);
INT16 y = y1 + 5 + ((SYS_Height - GfxTextHeight()) >> 1);
INT16 x = x1 + ((x2 - x1 - GfxTextExtent(st)) >> 1);
GfxTextColor(_BrightWhite);
GfxText(x, y, st, GFX_Transparent);
GfxClrClip();
}
bCreated = TRUE;
}
Mickey.ShowPointer();
if (bCreated)
DblAppend((void*)&w, (UINT16)sizeof(WIN));
return bCreated;
}
////////////////////
// For circumstances where all control gadgets need to be repainted
// (like we were just exposed in an XWindow environment, or the display
// has become trashed somehow), repaint all defined controls..
//
void AppWindow::Refresh (void)
{
Mickey.HidePointer();
pw = (PWIN)DblGetTail();
while (pw) {
pw->Pb.Refresh();
pw->Rb.Refresh();
pw->Cb.Refresh();
pw = (PWIN)DblGetNext();
}
Mickey.ShowPointer();
}
/********************************** eof **********************************/