mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-23 06:57:23 +00:00
Android’s system back button will be processed as vchrBack which will select kidcCancel on most forms. This causes the user to feel that the game is more integrated with the OS.
1126 lines
22 KiB
C++
1126 lines
22 KiB
C++
#include "ht.h"
|
|
|
|
namespace wi {
|
|
|
|
//
|
|
// Form
|
|
//
|
|
|
|
Form::Form()
|
|
{
|
|
m_iclrBack = kiclrFormBackground;
|
|
m_pctlCapture = NULL;
|
|
m_pfrmm = NULL;
|
|
m_ptbm = NULL;
|
|
m_wf = 0;
|
|
m_idf = 0;
|
|
m_idcDefault = 0;
|
|
m_idcLast = 0;
|
|
m_nResult = 0;
|
|
m_cctl = 0;
|
|
memset(m_apctl, 0, sizeof(m_apctl));
|
|
m_rc.SetEmpty();
|
|
m_pUserData = NULL;
|
|
}
|
|
|
|
Form::~Form()
|
|
{
|
|
// Delete controls
|
|
|
|
for (int n = 0; n < m_cctl; n++)
|
|
delete m_apctl[n];
|
|
|
|
// Delete TBitmap
|
|
|
|
delete m_ptbm;
|
|
|
|
// Remove this form
|
|
|
|
if (m_pfrmm != NULL)
|
|
m_pfrmm->RemoveForm(this);
|
|
|
|
if (m_wf & kfFrmHasPalette)
|
|
gpakr.UnmapFile(&m_fmapPalette);
|
|
if (m_wf & kfFrmHasShadowMap)
|
|
gpakr.UnmapFile(&m_fmapShadowMap);
|
|
|
|
// Mark deleted for debugging purposes
|
|
|
|
m_wf |= kfFrmDeleted;
|
|
}
|
|
|
|
bool Form::Init(FormMgr *pfrmm, IniReader *pini, word idf)
|
|
{
|
|
// Initialize form properties
|
|
|
|
char szForm[10];
|
|
itoa(idf, szForm, 10);
|
|
if (!InitFromProperties(pfrmm, idf, pini, szForm))
|
|
return false;
|
|
|
|
// Step through and create controls
|
|
|
|
char szProp[32];
|
|
FindProp find;
|
|
while (pini->FindNextProperty(&find, szForm, szProp, sizeof(szProp))) {
|
|
// Instantiate and initialize control
|
|
|
|
Control *pctl = NULL;
|
|
if (strcmp(szProp, "BUTTON") == 0) {
|
|
pctl = new ButtonControl;
|
|
}
|
|
else if (strcmp(szProp, "RADIOBUTTONBAR") == 0) {
|
|
pctl = new RadioButtonBarControl;
|
|
}
|
|
else if (strcmp(szProp, "LABEL") == 0) {
|
|
pctl = new LabelControl;
|
|
}
|
|
else if (strcmp(szProp, "ECOMTEXT") == 0) {
|
|
pctl = new EcomTextControl;
|
|
}
|
|
else if (strcmp(szProp, "CHECKBOX") == 0) {
|
|
pctl = new CheckBoxControl;
|
|
}
|
|
else if (strcmp(szProp, "BITMAP") == 0) {
|
|
pctl = new BitmapControl;
|
|
}
|
|
#if 0 // not used now
|
|
else if (strcmp(szProp, "PRESETBUTTON") == 0) {
|
|
pctl = new PresetButtonControl;
|
|
}
|
|
#endif
|
|
else if (strcmp(szProp, "EDIT") == 0) {
|
|
pctl = new EditControl;
|
|
}
|
|
else if (strcmp(szProp, "LIST") == 0) {
|
|
pctl = new ListControl;
|
|
}
|
|
else if (strcmp(szProp, "ANIMLIST") == 0) {
|
|
pctl = new BuildListControl;
|
|
}
|
|
else if (strcmp(szProp, "GRAFFITISCROLL") == 0) {
|
|
pctl = new GraffitiScrollControl;
|
|
}
|
|
else if (strcmp(szProp, "SILKBUTTON") == 0) {
|
|
pctl = new SilkButtonControl;
|
|
}
|
|
else if (strcmp(szProp, "SLIDER") == 0) {
|
|
pctl = new SliderControl;
|
|
}
|
|
else if (strcmp(szProp, "MINIMAP") == 0) {
|
|
pctl = new MiniMapControl;
|
|
}
|
|
else if (strcmp(szProp, "PIPMETER") == 0) {
|
|
pctl = new PipMeterControl;
|
|
}
|
|
else if (strcmp(szProp, "DAMAGEMETER") == 0) {
|
|
pctl = new DamageMeterControl;
|
|
}
|
|
else if (strcmp(szProp, "HELP") == 0) {
|
|
pctl = new HelpControl;
|
|
}
|
|
else if (strcmp(szProp, "ALERT") == 0) {
|
|
pctl = new AlertControl;
|
|
}
|
|
else if (strcmp(szProp, "CREDITS") == 0) {
|
|
pctl = new CreditsControl;
|
|
}
|
|
else if (strcmp(szProp, "POWER") == 0) {
|
|
pctl = new PowerControl;
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
Assert(pctl != NULL, "out of memory!");
|
|
if (pctl == NULL)
|
|
return false;
|
|
if (!pctl->Init(this, pini, &find))
|
|
return false;
|
|
|
|
// Add it to the list for this form
|
|
|
|
Assert(m_cctl < kcControlsMax);
|
|
m_apctl[m_cctl++] = pctl;
|
|
}
|
|
|
|
// Add the form to the form mgr
|
|
|
|
m_pfrmm->AddForm(this);
|
|
|
|
// Show the form. This is a asynchronous event, which'll give time
|
|
// to the derived form to initialize.
|
|
|
|
Show(true);
|
|
return true;
|
|
}
|
|
|
|
bool Form::InitFromProperties(FormMgr *pfrmm, word idf, IniReader *pini, char *pszForm)
|
|
{
|
|
// FORM=(x y cx cy) idcDefault
|
|
|
|
int x, y, cx, cy;
|
|
char szBitmap[kcbFilename];
|
|
char szPalette[kcbFilename];
|
|
int idcDefault;
|
|
char szArgs[3][32];
|
|
int cArgs = pini->GetPropertyValue(pszForm, "FORM", "(%d %d %d %d) %d %s %s %s",
|
|
&x, &y, &cx, &cy, &idcDefault, szArgs[0], szArgs[1], szArgs[2]);
|
|
if (cArgs < 5)
|
|
return false;
|
|
m_idcDefault = (word)idcDefault;
|
|
|
|
// Scale form coordinates depending on the resolution of the device.
|
|
|
|
bool fCenter = false;
|
|
bool fScale = true;
|
|
bool fTopMost = false;
|
|
|
|
int csz = _min((int)ARRAYSIZE(szArgs), cArgs - 5);
|
|
for (int n = 0; n < csz; n++) {
|
|
if (strcmp(szArgs[n], "noscale") == 0) {
|
|
fScale = false;
|
|
continue;
|
|
}
|
|
if (strcmp(szArgs[n], "center") == 0) {
|
|
fCenter = true;
|
|
continue;
|
|
}
|
|
if (strcmp(szArgs[n], "topmost") == 0) {
|
|
fTopMost = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (fScale) {
|
|
m_wf |= kfFrmScaleCoords;
|
|
x = PcFromFc(x);
|
|
y = PcFromFc(y);
|
|
cx = PcFromFc(cx);
|
|
cy = PcFromFc(cy);
|
|
}
|
|
|
|
if (fCenter) {
|
|
DibBitmap *pbm = pfrmm->GetDib();
|
|
Size siz;
|
|
pbm->GetSize(&siz);
|
|
x = ((siz.cx - cx) / 2) & ~1;
|
|
y = (siz.cy - cy) / 2;
|
|
}
|
|
|
|
if (fTopMost)
|
|
m_wf |= kfFrmTopMost;
|
|
|
|
cArgs = pini->GetPropertyValue(pszForm, "FORMBITMAP", "%s", szBitmap);
|
|
if (cArgs == 0)
|
|
szBitmap[0] = 0;
|
|
cArgs = pini->GetPropertyValue(pszForm, "FORMPALETTE", "%s", szPalette);
|
|
if (cArgs == 0)
|
|
szPalette[0] = 0;
|
|
cArgs = pini->GetPropertyValue(pszForm, "FORMBACKCOLOR", "%d", &m_iclrBack);
|
|
if (cArgs == 0)
|
|
m_iclrBack = -1;
|
|
|
|
// Fix the size of the form to the bitmap width and height
|
|
|
|
m_ptbm = NULL;
|
|
m_rc.Set(x, y, x + cx, y + cy);
|
|
if (szBitmap[0] != 0) {
|
|
m_ptbm = LoadTBitmap(szBitmap);
|
|
if (m_ptbm == NULL)
|
|
return false;
|
|
Size siz;
|
|
m_ptbm->GetSize(&siz);
|
|
m_rc.Set(x, y, x + siz.cx, y + siz.cy);
|
|
}
|
|
m_pfrmm = pfrmm;
|
|
m_idf = idf;
|
|
|
|
// Load and set the form Palette, if any
|
|
|
|
if (szPalette[0] != 0) {
|
|
Palette *ppal = (Palette *)gpakr.MapFile(szPalette, &m_fmapPalette);
|
|
Assert(ppal != NULL);
|
|
if (ppal == NULL)
|
|
return false;
|
|
m_wf |= kfFrmHasPalette;
|
|
|
|
// Select palette
|
|
|
|
SetHslAdjustedPalette(ppal, gnHueOffset, gnSatMultiplier, gnLumOffset);
|
|
|
|
// Load and set a corresponding shadow map if it exists
|
|
|
|
strcat(szPalette, ".shadowmap");
|
|
gmpiclriclrShadow = (byte *)gpakr.MapFile(szPalette, &m_fmapShadowMap);
|
|
if (gmpiclriclrShadow != NULL)
|
|
m_wf |= kfFrmHasShadowMap;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Form::AddControl(Control *pctl)
|
|
{
|
|
// Add it to the list for this form
|
|
|
|
// bottommost
|
|
|
|
Assert(m_cctl < kcControlsMax);
|
|
m_apctl[m_cctl++] = pctl;
|
|
return true;
|
|
}
|
|
|
|
void Form::SetUserDataPtr(void *pUserData)
|
|
{
|
|
m_pUserData = pUserData;
|
|
}
|
|
|
|
void *Form::GetUserDataPtr()
|
|
{
|
|
return m_pUserData;
|
|
}
|
|
|
|
word Form::GetId()
|
|
{
|
|
return m_idf;
|
|
}
|
|
|
|
word Form::GetFlags()
|
|
{
|
|
return m_wf;
|
|
}
|
|
|
|
void Form::SetFlags(word wf)
|
|
{
|
|
m_wf = wf;
|
|
}
|
|
|
|
void Form::Show(bool fShow)
|
|
{
|
|
if (fShow) {
|
|
if (m_wf & kfFrmVisible)
|
|
return;
|
|
m_wf |= kfFrmVisible;
|
|
InvalidateRect(NULL);
|
|
} else {
|
|
if (!(m_wf & kfFrmVisible))
|
|
return;
|
|
InvalidateRect(NULL);
|
|
m_wf &= ~kfFrmVisible;
|
|
}
|
|
}
|
|
|
|
void Form::OnUpdateMapInvalidate(UpdateMap *pupd, Rect *prcOpaque)
|
|
{
|
|
for (int n = 0; n < m_cctl; n++) {
|
|
if (!(m_apctl[n]->m_wf & kfCtlVisible))
|
|
continue;
|
|
|
|
Rect rcT;
|
|
rcT = m_apctl[n]->m_rc;
|
|
rcT.Offset(m_rc.left, m_rc.top);
|
|
|
|
// Only mark the control invalid
|
|
|
|
if (!prcOpaque->RectIn(&rcT)) {
|
|
if (pupd->IsRectInvalidAndTrackDamage(&rcT))
|
|
m_apctl[n]->SetFlags(m_apctl[n]->GetFlags() | kfCtlRedraw);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Form::FrameStart()
|
|
{
|
|
}
|
|
|
|
void Form::FrameComplete()
|
|
{
|
|
}
|
|
|
|
void Form::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd)
|
|
{
|
|
// Draw invalid parts of the background
|
|
|
|
if (m_iclrBack == -1)
|
|
return;
|
|
FillHelper(pbm, pupd, &m_rc, GetColor(m_iclrBack));
|
|
}
|
|
|
|
void Form::OnPaint(DibBitmap *pbm)
|
|
{
|
|
if (m_ptbm != NULL)
|
|
m_ptbm->BltTo(pbm, m_rc.left, m_rc.top);
|
|
}
|
|
|
|
void Form::OnPaintControls(DibBitmap *pbm, UpdateMap *pupd)
|
|
{
|
|
HostSoundServiceProc();
|
|
int nServiceSfx = 0;
|
|
for (int n = 0; n < m_cctl; n++) {
|
|
if ((m_apctl[n]->m_wf & (kfCtlVisible | kfCtlRedraw)) != (kfCtlVisible | kfCtlRedraw))
|
|
continue;
|
|
nServiceSfx++;
|
|
if ((nServiceSfx & 3) == 0)
|
|
HostSoundServiceProc();
|
|
m_apctl[n]->OnPaint(pbm);
|
|
m_apctl[n]->SetFlags(m_apctl[n]->GetFlags() & ~kfCtlRedraw);
|
|
}
|
|
}
|
|
|
|
void Form::ScrollInvalidate(UpdateMap *pupd)
|
|
{
|
|
// Invalidate the children due to scrolling
|
|
|
|
for (int n = 0; n < m_cctl; n++)
|
|
m_apctl[n]->Invalidate();
|
|
}
|
|
|
|
// for override
|
|
void Form::OnScroll(int dx, int dy)
|
|
{
|
|
}
|
|
|
|
bool Form::EventProc(Event *pevt)
|
|
{
|
|
switch (pevt->eType) {
|
|
case penHoverEvent:
|
|
case penDownEvent:
|
|
case penMoveEvent:
|
|
case penUpEvent:
|
|
case penHoldEvent:
|
|
case penDownEvent2:
|
|
case penMoveEvent2:
|
|
case penUpEvent2:
|
|
return OnPenEvent(pevt);
|
|
}
|
|
if (pevt->eType == keyDownEvent) {
|
|
switch (pevt->chr) {
|
|
case vchrBack:
|
|
OnControlSelected(kidcCancel);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Control *Form::GetControlPtr(word idc)
|
|
{
|
|
for (int n = 0; n < m_cctl; n++) {
|
|
if (m_apctl[n]->GetId() == idc)
|
|
return m_apctl[n];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool Form::OnPenEvent(Event *pevt)
|
|
{
|
|
// Auto-handle control capturing. Auto-release, auto-capture.
|
|
// Notify the control of important states:
|
|
// - penDownEvent and captured
|
|
// - penMoveEvent from inside to outside of captured control
|
|
// - penMoveEvent from outside to inside of captured control
|
|
// - penUpEvent inside control releasing capture
|
|
// - penUpEvent outside control releasing capture
|
|
|
|
if (m_pctlCapture != NULL) {
|
|
// Send on pen event to the captured control
|
|
|
|
m_pctlCapture->OnPenEvent(pevt);
|
|
word wf = m_pctlCapture->OnHitTest(pevt) >= 0 ? kfFrmPenInside : 0;
|
|
|
|
// Handle the transition states
|
|
|
|
Control *pctl = m_pctlCapture;
|
|
switch (pevt->eType) {
|
|
case penUpEvent:
|
|
// Release capture. Are we inside or outside of the control?
|
|
|
|
m_idcLast = pctl->GetId();
|
|
m_wf &= ~kfFrmPenInside;
|
|
m_pctlCapture = NULL;
|
|
pctl->OnSelect(((wf & kfFrmPenInside) && pevt->dw == 0) ? knSelUpInside : knSelUpOutside);
|
|
break;
|
|
|
|
case penMoveEvent:
|
|
if ((m_wf & kfFrmPenInside) != wf) {
|
|
m_wf &= ~kfFrmPenInside;
|
|
m_wf |= wf;
|
|
pctl->OnSelect((wf & kfFrmPenInside) ? knSelMoveInside : knSelMoveOutside);
|
|
}
|
|
break;
|
|
|
|
// This does happen. For example if a modal form is invoked in response
|
|
// to a penHoldEvent it will take the penUpEvent. Also happens on
|
|
// Windows a lot when debugging if something happens to invoke the
|
|
// debugger (e.g., fault, breakpoint) while the mousebutton is down.
|
|
|
|
case penDownEvent:
|
|
//Assert("this shouldn't happen");
|
|
break;
|
|
|
|
case penHoldEvent:
|
|
pctl->OnSelect(knSelHoldInside);
|
|
break;
|
|
|
|
// These events don't affect capture once it has already been set.
|
|
case penDownEvent2:
|
|
case penUpEvent2:
|
|
case penMoveEvent2:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// If pen isn't captured and this is a pen down on a control,
|
|
// capture future pen events to the control and let it know it
|
|
// has been pressed.
|
|
|
|
if (pevt->eType != penDownEvent)
|
|
return false;
|
|
|
|
// Auto-capture if we've hit a control
|
|
|
|
Control *pctlHit = HitTestControls(pevt);
|
|
if (pctlHit != NULL) {
|
|
m_wf |= kfFrmPenInside;
|
|
m_pctlCapture = pctlHit;
|
|
m_pctlCapture->OnPenEvent(pevt);
|
|
m_pctlCapture->OnSelect(knSelDownInside);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Control *Form::HitTestControls(Event *pevt) {
|
|
// Finger input may register hits on more than one control at a time.
|
|
// In this case, use the "closest" control, measured by hit distance.
|
|
// Pen input will simply register on the first control registering a hit.
|
|
|
|
int nControlBest = -1;
|
|
int nDistanceBest = 9999;
|
|
for (int n = m_cctl - 1; n >= 0; n--) {
|
|
// Is it on this control?
|
|
|
|
Control *pctl = m_apctl[n];
|
|
int nDistance = pctl->OnHitTest(pevt);
|
|
if (nDistance < 0) {
|
|
continue;
|
|
}
|
|
|
|
// Remember closest
|
|
|
|
if (nDistance < nDistanceBest) {
|
|
nDistanceBest = nDistance;
|
|
nControlBest = n;
|
|
}
|
|
|
|
// No need to enumerate all controls if not finger input
|
|
|
|
if (!(pevt->ff & kfEvtFinger)) {
|
|
break;
|
|
}
|
|
}
|
|
if (nControlBest >= 0) {
|
|
return m_apctl[nControlBest];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void Form::BreakCapture()
|
|
{
|
|
if (m_pctlCapture != NULL) {
|
|
m_pctlCapture->OnBreakCapture();
|
|
m_pctlCapture = NULL;
|
|
m_wf &= ~kfFrmPenInside;
|
|
}
|
|
}
|
|
|
|
bool Form::OnHitTest(Event *pevt)
|
|
{
|
|
if (!(m_wf & kfFrmVisible))
|
|
return false;
|
|
|
|
|
|
// If this is a finger event, Check first if any control reports hittest.
|
|
// This allows hit testing outside the normal rect of the form.
|
|
|
|
bool fHit = false;
|
|
if (pevt->ff & kfEvtFinger) {
|
|
for (int n = 0; n < m_cctl; n++) {
|
|
Control *pctl = m_apctl[n];
|
|
if (pctl->OnHitTest(pevt) >= 0) {
|
|
fHit = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!fHit) {
|
|
fHit = m_rc.PtIn(pevt->x, pevt->y);
|
|
}
|
|
|
|
if (fHit || !(m_wf & kfFrmAutoTakedown))
|
|
return fHit;
|
|
|
|
// We're AutoTakedown and hit is outside form
|
|
// We can receive pen ups and holds here if the pen was already
|
|
// down and a trigger invokes than ecom.
|
|
|
|
if (pevt->eType == penDownEvent)
|
|
EndForm(kidcCancel);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Form::OnKeyTest(Event *pevt)
|
|
{
|
|
// If pevt == NULL we're just checking; no actual event
|
|
|
|
if (pevt == NULL) {
|
|
if (m_wf & kfFrmNoFocus)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
if (m_wf & kfFrmAutoTakedown) {
|
|
EndForm(kidcCancel);
|
|
return false;
|
|
}
|
|
|
|
if (m_wf & kfFrmNoFocus)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool Form::DoModal(int *pnResult, Sfx sfxShow, Sfx sfxHide)
|
|
{
|
|
#ifdef DEBUG
|
|
bool fFirstTimeThrough = true;
|
|
#endif
|
|
|
|
Show(true);
|
|
gsndm.PlaySfx(sfxShow);
|
|
m_wf |= kfFrmDoModal;
|
|
while (m_wf & kfFrmDoModal) {
|
|
// Only process if there is an event
|
|
|
|
Event evt;
|
|
if (!gevm.PeekEvent(&evt, -1))
|
|
continue;
|
|
|
|
// Leave appStopEvent and gameOverEvents in the queue so the whole
|
|
// call chain will see it and be able to respond appropriately.
|
|
|
|
switch (evt.eType) {
|
|
case appStopEvent:
|
|
case gameOverEvent:
|
|
case cancelModeEvent:
|
|
m_idcLast = m_idcDefault;
|
|
m_nResult = m_idcDefault;
|
|
m_wf &= ~kfFrmDoModal;
|
|
break;
|
|
}
|
|
|
|
// Autotakedown is handled inside PeekEvent/CookEvent/Form::OnHitTest
|
|
// and can take us out of modal mode. If so, drop out here so the
|
|
// event is left on the queue.
|
|
|
|
if (!(m_wf & kfFrmDoModal))
|
|
break;
|
|
|
|
#ifdef DEBUG
|
|
fFirstTimeThrough = false;
|
|
#endif
|
|
if (!gevm.GetEvent(&evt))
|
|
continue;
|
|
if (ggame.FilterEvent(&evt))
|
|
continue;
|
|
if (OnFilterEvent(&evt))
|
|
continue;
|
|
gevm.DispatchEvent(&evt);
|
|
}
|
|
|
|
if (pnResult != NULL)
|
|
*pnResult = m_nResult;
|
|
gsndm.PlaySfx(sfxHide);
|
|
Show(false);
|
|
return m_idcLast == kidcOk;
|
|
}
|
|
|
|
void Form::EndForm(int nResult)
|
|
{
|
|
m_nResult = nResult;
|
|
m_wf &= ~kfFrmDoModal;
|
|
}
|
|
|
|
void Form::GetRect(Rect *prc)
|
|
{
|
|
*prc = m_rc;
|
|
}
|
|
|
|
void Form::SetRect(Rect *prc)
|
|
{
|
|
InvalidateRect(NULL);
|
|
m_rc = *prc;
|
|
InvalidateRect(NULL);
|
|
}
|
|
|
|
bool Form::IsControlInside(Control *pctl)
|
|
{
|
|
if (m_pctlCapture != NULL && (m_wf & kfFrmPenInside))
|
|
return m_pctlCapture == pctl;
|
|
return false;
|
|
}
|
|
|
|
void Form::OnControlSelected(word idc)
|
|
{
|
|
EndForm(idc);
|
|
}
|
|
|
|
bool Form::OnControlHeld(word idc)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void Form::OnControlNotify(word idc, int nNotify)
|
|
{
|
|
}
|
|
|
|
void Form::InvalidateRect(Rect *prc)
|
|
{
|
|
// If invisible, return
|
|
|
|
if (!(m_wf & kfFrmVisible))
|
|
return;
|
|
if (prc == NULL)
|
|
prc = &m_rc;
|
|
|
|
// If not opaqued, then invalidate
|
|
|
|
Rect rcOpaque;
|
|
gpmfrmm->CalcOpaqueRect(this, NULL, &rcOpaque);
|
|
Rect rcT;
|
|
gpmfrmm->GetFormMgrRect(m_pfrmm, &rcT);
|
|
rcOpaque.Intersect(&rcOpaque, &rcT);
|
|
rcOpaque.Offset(-rcT.left, -rcT.top);
|
|
if (!rcOpaque.RectIn(prc))
|
|
m_pfrmm->InvalidateRect(prc);
|
|
}
|
|
|
|
void ShadowHelper(DibBitmap *pbm, UpdateMap *pupd, Rect *prc)
|
|
{
|
|
Rect rc;
|
|
if (prc == NULL) {
|
|
Size siz;
|
|
pbm->GetSize(&siz);
|
|
rc.Set(0, 0, siz.cx, siz.cy);
|
|
prc = &rc;
|
|
}
|
|
|
|
if (pupd == NULL) {
|
|
pbm->Shadow(prc->left, prc->top, prc->Width(), prc->Height());
|
|
} else {
|
|
Rect rcInvalid;
|
|
bool fFirst = true;
|
|
while (pupd->EnumUpdateRects(fFirst, prc, &rcInvalid)) {
|
|
fFirst = false;
|
|
pbm->Shadow(rcInvalid.left, rcInvalid.top, rcInvalid.Width(), rcInvalid.Height());
|
|
}
|
|
}
|
|
}
|
|
|
|
void FillHelper(DibBitmap *pbm, UpdateMap *pupd, Rect *prc, Color clr)
|
|
{
|
|
Rect rc;
|
|
if (prc == NULL) {
|
|
Size siz;
|
|
pbm->GetSize(&siz);
|
|
rc.Set(0, 0, siz.cx, siz.cy);
|
|
prc = &rc;
|
|
}
|
|
|
|
if (pupd == NULL) {
|
|
pbm->Fill(prc->left, prc->top, prc->Width(), prc->Height(), clr);
|
|
} else {
|
|
Rect rcInvalid;
|
|
bool fFirst = true;
|
|
while (pupd->EnumUpdateRects(fFirst, prc, &rcInvalid)) {
|
|
fFirst = false;
|
|
pbm->Fill(rcInvalid.left, rcInvalid.top, rcInvalid.Width(), rcInvalid.Height(), clr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BltHelper(DibBitmap *pbm, HtBitmap *phtbm, UpdateMap *pupd, int xDst, int yDst)
|
|
{
|
|
if (pupd == NULL) {
|
|
phtbm->BltTo(pbm, xDst, yDst);
|
|
} else {
|
|
Size siz;
|
|
phtbm->GetSize(&siz);
|
|
Rect rc;
|
|
rc.Set(xDst, yDst, xDst + siz.cx, yDst + siz.cy);
|
|
Rect rcInvalid;
|
|
bool fFirst = true;
|
|
while (pupd->EnumUpdateRects(fFirst, &rc, &rcInvalid)) {
|
|
fFirst = false;
|
|
Rect rcSrc;
|
|
rcSrc.left = rcInvalid.left - xDst;
|
|
rcSrc.top = rcInvalid.top - yDst;
|
|
rcSrc.right = rcSrc.left + rcInvalid.Width();
|
|
rcSrc.bottom = rcSrc.top + rcInvalid.Height();
|
|
phtbm->BltTo(pbm, rcInvalid.left, rcInvalid.top, &rcSrc);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// DialogForm
|
|
//
|
|
|
|
DialogForm::DialogForm()
|
|
{
|
|
m_clrTitle = GetColor(kiclrGreen);
|
|
m_iclrBorder = kiclrBlack;
|
|
m_fClearDib = false;
|
|
SetBackgroundColorIndex(kiclrShadow);
|
|
}
|
|
|
|
// The compiler generates this no matter what so by declaring it ourselves
|
|
// we can control which section it ends up in (not .text)
|
|
|
|
DialogForm::~DialogForm()
|
|
{
|
|
}
|
|
|
|
void DialogForm::OnPaintBackground(DibBitmap *pbm, UpdateMap *pupd)
|
|
{
|
|
// First clear dib if asked
|
|
|
|
if (m_fClearDib)
|
|
FillHelper(pbm, pupd, NULL, GetColor(m_iclrBackground));
|
|
|
|
// Draw invalid parts of the background
|
|
|
|
int cyTitle = PcFromFc(kcyTitle);
|
|
Rect rc = m_rc;
|
|
rc.left += gcxyBorder;
|
|
rc.right -= gcxyBorder;
|
|
rc.top += cyTitle;
|
|
rc.bottom -= gcxyBorder;
|
|
|
|
switch (m_iclrBackground) {
|
|
case kiclrShadow2x:
|
|
ShadowHelper(pbm, pupd, &rc);
|
|
// ...FALL THROUGH...
|
|
|
|
case kiclrShadow:
|
|
ShadowHelper(pbm, pupd, &rc);
|
|
break;
|
|
|
|
default:
|
|
FillHelper(pbm, pupd, &rc, GetColor(m_iclrBackground));
|
|
break;
|
|
}
|
|
|
|
// Draw form border
|
|
|
|
DrawBorder(pbm, &m_rc, gcxyBorder, GetColor(m_iclrBorder), pupd);
|
|
|
|
// Draw title background
|
|
|
|
rc = m_rc;
|
|
rc.left += gcxyBorder;
|
|
rc.right -= gcxyBorder;
|
|
rc.top += gcxyBorder;
|
|
rc.bottom = m_rc.top + cyTitle;
|
|
|
|
FillHelper(pbm, pupd, &rc, m_clrTitle);
|
|
}
|
|
|
|
void DialogForm::SetBackgroundColorIndex(int iclr)
|
|
{
|
|
m_iclrBackground = iclr;
|
|
if (m_iclrBackground == kiclrShadow || m_iclrBackground == kiclrShadow2x) {
|
|
m_wf |= kfFrmTranslucent;
|
|
} else {
|
|
m_wf &= ~kfFrmTranslucent;
|
|
}
|
|
}
|
|
|
|
void DialogForm::OnPaint(DibBitmap *pbm)
|
|
{
|
|
Form::OnPaint(pbm);
|
|
}
|
|
|
|
bool DialogForm::OnPenEvent(Event *pevt)
|
|
{
|
|
if (Form::OnPenEvent(pevt))
|
|
return true;
|
|
|
|
return FormDragger(this, pevt);
|
|
}
|
|
|
|
bool DialogForm::DoModal(int *pnResult, Sfx sfxShow, Sfx sfxHide)
|
|
{
|
|
bool fResult = Form::DoModal(pnResult, sfxShow, sfxHide);
|
|
if (m_fClearDib) {
|
|
m_pfrmm->InvalidateRect(NULL);
|
|
}
|
|
return fResult;
|
|
}
|
|
|
|
bool FormDragger(Form *pfrm, Event *pevt)
|
|
{
|
|
static bool s_fDragging = false;
|
|
static Rect s_rcInitial;
|
|
static int s_xDown, s_yDown;
|
|
|
|
if (pevt->eType == penDownEvent) {
|
|
|
|
Rect rcForm;
|
|
pfrm->GetRect(&rcForm);
|
|
|
|
// Is event in title? If so, capture and save drag start info
|
|
|
|
if (pevt->y - rcForm.top <= PcFromFc(kcyTitle) + gcxyBorder) {
|
|
s_fDragging = true;
|
|
s_rcInitial = rcForm;
|
|
s_xDown = pevt->x;
|
|
s_yDown = pevt->y;
|
|
return true;
|
|
}
|
|
return false;
|
|
} else if (pevt->eType == penMoveEvent) {
|
|
|
|
// Are we dragging? If so, reposition form at new location
|
|
|
|
if (s_fDragging) {
|
|
Rect rc;
|
|
rc.left = s_rcInitial.left + pevt->x - s_xDown;
|
|
rc.top = s_rcInitial.top + pevt->y - s_yDown;
|
|
rc.right = rc.left + s_rcInitial.Width();
|
|
rc.bottom = rc.top + s_rcInitial.Height();
|
|
pfrm->SetRect(&rc);
|
|
return true;
|
|
}
|
|
} else if (pevt->eType == penUpEvent) {
|
|
|
|
// Are we dragging? If so, release capture
|
|
|
|
if (s_fDragging) {
|
|
s_fDragging = false;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Control
|
|
//
|
|
|
|
Control::Control()
|
|
{
|
|
m_rc.SetEmpty();
|
|
m_wf = 0;
|
|
m_idc = 0;
|
|
m_pfrm = NULL;
|
|
m_pceh = NULL;
|
|
}
|
|
|
|
Control::~Control()
|
|
{
|
|
}
|
|
|
|
bool Control::Init(Form *pfrm, word idc, int x, int y, int cx, int cy)
|
|
{
|
|
m_idc = idc;
|
|
m_pfrm = pfrm;
|
|
m_pceh = pfrm;
|
|
m_wf |= kfCtlVisible;
|
|
|
|
m_rc.Set(x, y, x + cx, y + cy);
|
|
return true;
|
|
}
|
|
|
|
bool Control::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
|
|
{
|
|
// idc (x y cx cy)
|
|
|
|
int x, y, cx, cy;
|
|
int idc;
|
|
int cArgs = pini->GetPropertyValue(pfind, "%d (%d %d %d %d)", &idc,
|
|
&x, &y, &cx, &cy);
|
|
if (cArgs != 5)
|
|
return false;
|
|
|
|
// Scale form coordinates depending on the resolution of the device.
|
|
|
|
if (pfrm->GetFlags() & kfFrmScaleCoords) {
|
|
x = PcFromFc(x);
|
|
y = PcFromFc(y);
|
|
cx = PcFromFc(cx);
|
|
cy = PcFromFc(cy);
|
|
}
|
|
m_rc.Set(x, y, x + cx, y + cy);
|
|
return Init(pfrm, idc, x, y, cx, cy);
|
|
}
|
|
|
|
void Control::OnSelect(int nSelect)
|
|
{
|
|
if (m_wf & kfCtlDisabled)
|
|
return;
|
|
|
|
if (nSelect == knSelUpInside)
|
|
m_pceh->OnControlSelected(m_idc);
|
|
else if (nSelect == knSelHoldInside) {
|
|
if (m_pceh->OnControlHeld(m_idc)) {
|
|
// m_pctlCapture = NULL;
|
|
// m_wf &= ~kfFrmPenInside;
|
|
// OnSelect(knSelUpOutside);
|
|
}
|
|
}
|
|
}
|
|
|
|
int Control::OnHitTest(Event *pevt)
|
|
{
|
|
if (!(m_wf & kfCtlVisible))
|
|
return -1;
|
|
|
|
// Get the distance to a side of the control
|
|
|
|
Rect rcForm;
|
|
m_pfrm->GetRect(&rcForm);
|
|
int nDist = m_rc.GetDistance(pevt->x - rcForm.left, pevt->y - rcForm.top);
|
|
|
|
// Finger input hits on a larger rect and returns the distance to
|
|
// the inner rect. Non-finger input just hits against the inner rect.
|
|
|
|
if (pevt->ff & kfEvtFinger) {
|
|
Rect rcT;
|
|
GetFingerRect(&rcT);
|
|
if (rcT.PtIn(pevt->x - rcForm.left, pevt->y - rcForm.top)) {
|
|
return nDist;
|
|
}
|
|
} else {
|
|
if (nDist == 0) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// No hit
|
|
|
|
return -1;
|
|
}
|
|
|
|
word Control::GetId()
|
|
{
|
|
return m_idc;
|
|
}
|
|
|
|
void Control::GetRect(Rect *prc)
|
|
{
|
|
*prc = m_rc;
|
|
}
|
|
|
|
void Control::GetFingerRect(Rect *prc)
|
|
{
|
|
*prc = m_rc;
|
|
|
|
// Hack for now
|
|
prc->Inflate(20, 20);
|
|
}
|
|
|
|
void Control::OnBreakCapture()
|
|
{
|
|
Invalidate();
|
|
}
|
|
|
|
void Control::SetRect(Rect *prc, bool fCompareRect)
|
|
{
|
|
if (fCompareRect) {
|
|
if (prc->Equal(&m_rc))
|
|
return;
|
|
}
|
|
Invalidate();
|
|
m_rc = *prc;
|
|
Invalidate();
|
|
}
|
|
|
|
void Control::SetPosition(int x, int y)
|
|
{
|
|
if (m_rc.left == x && m_rc.top == y)
|
|
return;
|
|
|
|
Invalidate();
|
|
m_rc.Offset(x - m_rc.left, y - m_rc.top);
|
|
Invalidate();
|
|
}
|
|
|
|
void Control::Show(bool fShow)
|
|
{
|
|
if (fShow) {
|
|
if (m_wf & kfCtlVisible)
|
|
return;
|
|
m_wf |= kfCtlVisible;
|
|
Invalidate();
|
|
} else {
|
|
if (!(m_wf & kfCtlVisible))
|
|
return;
|
|
Invalidate();
|
|
m_wf &= ~kfCtlVisible;
|
|
}
|
|
}
|
|
|
|
void Control::Enable(bool fEnable)
|
|
{
|
|
if (fEnable) {
|
|
if (!(m_wf & kfCtlDisabled))
|
|
return;
|
|
m_wf &= ~kfCtlDisabled;
|
|
Invalidate();
|
|
} else {
|
|
if (m_wf & kfCtlDisabled)
|
|
return;
|
|
m_wf |= kfCtlDisabled;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void Control::OnPaint(DibBitmap *pbm)
|
|
{
|
|
}
|
|
|
|
void Control::OnPenEvent(Event *pevt)
|
|
{
|
|
}
|
|
|
|
void Control::Invalidate()
|
|
{
|
|
if (!(m_wf & kfCtlVisible))
|
|
return;
|
|
Rect rcForm;
|
|
m_pfrm->GetRect(&rcForm);
|
|
Rect rcCtl;
|
|
rcCtl = m_rc;
|
|
rcCtl.Offset(rcForm.left, rcForm.top);
|
|
m_pfrm->InvalidateRect(&rcCtl);
|
|
}
|
|
|
|
word Control::GetFlags()
|
|
{
|
|
return m_wf;
|
|
}
|
|
|
|
void Control::SetFlags(word wf)
|
|
{
|
|
word wfT = m_wf;
|
|
m_wf = wf;
|
|
if ((wfT ^ wf) & kfCtlSet)
|
|
Invalidate();
|
|
}
|
|
|
|
} // namespace wi
|