hostile-takeover/game/formmgr.cpp
2014-07-06 17:47:28 -07:00

1067 lines
23 KiB
C++

#include "ht.h"
namespace wi {
FormMgr *CreateFormMgr(DibBitmap *pbm)
{
FormMgr *pfrmm = new FormMgr();
Assert(pfrmm != NULL, "out of memory!");
if (pfrmm == NULL)
return NULL;
if (!pfrmm->Init(pbm, true)) {
delete pfrmm;
return NULL;
}
return pfrmm;
}
FormMgr::FormMgr()
{
m_wf = 0;
m_fFreeDib = false;
m_pupd = NULL;
m_idfCapture = 0;
m_cCaptureDowns = 0;
m_cfrm = 0;
m_dxScrollAccumulate = 0;
m_dyScrollAccumulate = 0;
memset(m_apfrm, 0, sizeof(m_apfrm));
}
FormMgr::~FormMgr()
{
if (m_fFreeDib)
delete m_pbm;
while (m_cfrm != 0) {
Form *pfrm = m_apfrm[0];
RemoveForm(pfrm);
delete pfrm;
}
delete m_pupd;
}
bool FormMgr::Init(DibBitmap *pbm, bool fFreeDib)
{
m_pbm = pbm;
m_fFreeDib = fFreeDib;
m_pupd = new UpdateMap();
Assert(m_pupd != NULL, "out of memory!");
if (m_pupd == NULL)
return false;
Size siz;
pbm->GetSize(&siz);
if (!m_pupd->Init(&siz))
return false;
return true;
}
void FormMgr::AddForm(Form *pfrm)
{
Assert(m_cfrm < kcFormsMax);
if (m_cfrm >= kcFormsMax)
return;
// Newly added forms go on top, unless there aren't topmost
// and topmost forms are already present, in which case the
// added form is placed immediately underneath the bottomost
// topmost form.
int i;
if (pfrm->GetFlags() & kfFrmTopMost) {
i = m_cfrm;
} else {
for (i = 0; i < m_cfrm; i++) {
if (m_apfrm[i]->GetFlags() & kfFrmTopMost)
break;
}
if (i != m_cfrm)
memmove(&m_apfrm[i + 1], &m_apfrm[i], sizeof(pfrm) * (m_cfrm - i));
}
m_apfrm[i] = pfrm;
m_cfrm++;
pfrm->InvalidateRect(NULL);
}
void FormMgr::RemoveForm(Form *pfrm)
{
for (int n = 0; n < m_cfrm; n++) {
if (m_apfrm[n] == pfrm) {
if (n < kcFormsMax - 1)
memmove(&m_apfrm[n], &m_apfrm[n + 1], sizeof(Form *) * (kcFormsMax - 1 - n));
m_cfrm--;
break;
}
}
pfrm->InvalidateRect(NULL);
}
bool FormMgr::EcomSuppressed()
{
for (int n = 0; n < m_cfrm; n++) {
if (m_apfrm[n]!= NULL) {
if (m_apfrm[n]->GetFlags() & kfFrmNoEcom)
return true;
}
}
return false;
}
bool FormMgr::CookKeyEvent(Event *pevt)
{
// Hack for now: assume the topmost form wants key input
// Maybe this'll be enough focus "knowledge".
for (int n = m_cfrm - 1; n >= 0; n--) {
if (m_apfrm[n]->OnKeyTest(pevt)) {
pevt->idf = m_apfrm[n]->GetId();
return true;
}
}
return false;
}
Form *FormMgr::FindPen2Form()
{
// Find a form that demands pen2
for (int i = 0; i < m_cfrm; i++) {
Form *pfrm = m_apfrm[i];
if (pfrm->m_wf & kfFrmDemandPen2) {
return pfrm;
}
}
return NULL;
}
void FormMgr::BreakCapture()
{
Form *pfrm = GetFormCapture();
if (pfrm == NULL) {
return;
}
pfrm->BreakCapture();
m_idfCapture = 0;
m_cCaptureDowns = 0;
}
bool FormMgr::CookPenEvent(Event *pevt)
{
// Send the input to the capturing form. Auto-release if pen up,
// auto-capture if pen down.
if (m_idfCapture != 0) {
pevt->idf = m_idfCapture;
if (pevt->eType == penDownEvent || pevt->eType == penDownEvent2) {
m_cCaptureDowns++;
}
if (pevt->eType == penUpEvent || pevt->eType == penUpEvent2) {
m_cCaptureDowns--;
}
if (m_cCaptureDowns <= 0) {
m_cCaptureDowns = 0;
m_idfCapture = 0;
}
return true;
} else {
// Ask the topmost form first if it wants this pen input.
// All coords are global.
word idf = 0;
for (int n = m_cfrm - 1; n >= 0; n--) {
Form *pfrm = m_apfrm[n];
if (pfrm->OnHitTest(pevt)) {
idf = pfrm->GetId();
break;
}
// If the top-most form is modal, don't pass the event on
// to any lower forms.
if (pfrm->GetFlags() & kfFrmDoModal) {
pevt->idf = 0;
return true;
}
}
pevt->idf = idf;
if (pevt->eType == penDownEvent || pevt->eType == penDownEvent2) {
m_cCaptureDowns = 1;
m_idfCapture = idf;
}
return pevt->idf != 0;
}
}
Form *FormMgr::GetFocus()
{
// Hack for now: assume the topmost form wants key input
// Maybe this'll be enough focus "knowledge".
for (int n = 0; n < m_cfrm; n++) {
if (m_apfrm[n]->OnKeyTest(NULL)) {
return m_apfrm[n];
}
}
return NULL;
}
bool FormMgr::CookEvent(Event *pevt)
{
pevt->idf = 0;
switch (pevt->eType) {
case penHoverEvent:
case penUpEvent:
case penDownEvent:
case penMoveEvent:
case penUpEvent2:
case penDownEvent2:
case penMoveEvent2:
case penHoldEvent:
{
// If no capture, process as usual
if (m_idfCapture == 0) {
return CookPenEvent(pevt);
}
// Mouse is captured. If captureed but there is a new form on top,
// gracefully release the capture (can happen if form shows while
// mouse is captured)
if (!CookPenEvent(pevt))
return false;
for (int n = m_cfrm - 1; n >= 0; n--) {
Form *pfrm = m_apfrm[n];
if ((pfrm->GetFlags() & (kfFrmDoModal | kfFrmVisible)) == (kfFrmDoModal | kfFrmVisible)) {
if (pfrm->m_idf != pevt->idf) {
pevt->dw = 1;
pevt->eType = penUpEvent;
m_idfCapture = 0;
m_cCaptureDowns = 0;
}
}
break;
}
return true;
}
break;
case keyDownEvent:
return CookKeyEvent(pevt);
}
return false;
}
Form *FormMgr::GetFormPtr(word idf)
{
for (int n = m_cfrm - 1; n >= 0; n--) {
if (m_apfrm[n]->GetId() == idf)
return m_apfrm[n];
}
return NULL;
}
Form *FormMgr::LoadForm(IniReader *pini, word idf, Form *pfrm)
{
if (pfrm == NULL)
return NULL;
if (!pfrm->Init(this, pini, idf)) {
delete pfrm;
return NULL;
}
return pfrm;
}
void FormMgr::CalcOpaqueRect(Form *pfrmStop, Rect *prcOpaqueStart, Rect *prcResult)
{
// Start from the top and expand the opaque rect when a portion lies safely inside
// the new rectangle.
if (prcOpaqueStart == NULL) {
prcResult->SetEmpty();
} else {
*prcResult = *prcOpaqueStart;
}
for (int n = m_cfrm - 1; n >= 0; n--) {
Form *pfrm = m_apfrm[n];
if (pfrm == pfrmStop)
break;
word wf = pfrm->GetFlags();
if (wf & kfFrmTranslucent)
continue;
if (!(wf & kfFrmVisible))
continue;
prcResult->Add(prcResult, &pfrm->m_rc);
}
}
void FormMgr::Paint(bool fScrolled, Rect *prcOpaqueStart)
{
// If scrolling, we want to scroll from a valid back buffer.
// After invalidation has been calculated for this frame, merge in
// damaged invalid; this'll make the back buffer valid after redraw.
if (fScrolled)
m_pupd->StartMergeDamagedInvalid();
// Give some time to sound servicing
HostSoundServiceProc();
// Invalidate UpdateMap for this frame
int j;
Rect rcOpaque;
for (j = 0; j < m_cfrm; j++) {
if (!(m_apfrm[j]->m_wf & kfFrmVisible))
continue;
CalcOpaqueRect(m_apfrm[j], prcOpaqueStart, &rcOpaque);
if (!rcOpaque.RectIn(&m_apfrm[j]->m_rc))
m_apfrm[j]->OnUpdateMapInvalidate(m_pupd, &rcOpaque);
}
// Give some time to sound servicing
HostSoundServiceProc();
// The merge is a two part process
if (fScrolled)
m_pupd->EndMergeDamagedInvalid();
// If nothing invalid, nothing to paint
if (!m_pupd->IsInvalid())
return;
// Now paint against the update map
for (j = 0; j < m_cfrm; j++) {
if (!(m_apfrm[j]->m_wf & kfFrmVisible))
continue;
if (m_pupd->IsRectInvalid(&m_apfrm[j]->m_rc)) {
CalcOpaqueRect(m_apfrm[j], prcOpaqueStart, &rcOpaque);
if (!rcOpaque.RectIn(&m_apfrm[j]->m_rc)) {
m_apfrm[j]->OnPaintBackground(m_pbm, m_pupd);
HostSoundServiceProc();
m_apfrm[j]->OnPaint(m_pbm);
HostSoundServiceProc();
m_apfrm[j]->OnPaintControls(m_pbm, m_pupd);
HostSoundServiceProc();
}
}
}
}
void FormMgr::Scroll(int dx, int dy)
{
// If nothing to scroll, return
if (dx == 0 && dy == 0)
return;
// Force the forms to invalidate the updatemap appropriately before the map
// is scrolled
int i;
for (i = 0; i < m_cfrm; i++)
m_apfrm[i]->ScrollInvalidate(m_pupd);
// Now scroll the update map
bool fScrolled = m_pupd->Scroll(dx, dy);
// Give forms a chance to respond to the scroll
for (i = 0; i < m_cfrm; i++)
m_apfrm[i]->OnScroll(dx, dy);
// Now invalidate again after the map has been scrolled
for (i = 0; i < m_cfrm; i++)
m_apfrm[i]->ScrollInvalidate(m_pupd);
// Accumulate
if (fScrolled) {
m_dxScrollAccumulate += dx;
m_dyScrollAccumulate += dy;
}
}
bool FormMgr::ScrollBits()
{
if (m_dxScrollAccumulate == 0 && m_dyScrollAccumulate == 0)
return false;
Size sizSrc;
m_pbm->GetSize(&sizSrc);
Rect rcSrc;
rcSrc.left = 0;
if (m_dxScrollAccumulate < 0) {
rcSrc.left = -m_dxScrollAccumulate;
m_dxScrollAccumulate = 0;
}
rcSrc.top = 0;
if (m_dyScrollAccumulate < 0) {
rcSrc.top = -m_dyScrollAccumulate;
m_dyScrollAccumulate = 0;
}
rcSrc.right = sizSrc.cx;
if (m_dxScrollAccumulate > 0)
rcSrc.right -= m_dxScrollAccumulate;
rcSrc.bottom = sizSrc.cy;
if (m_dyScrollAccumulate > 0)
rcSrc.bottom -= m_dyScrollAccumulate;
// Hack: if the front dib is a rotated dib, it is a 3 dib architecture and
// we can scroll the middle (rotated) dib rather than the back dib, giving
// us a speedup.
#if !defined(WIN)
DibBitmap *pbmFront = gpdisp->GetFrontDib();
if (pbmFront->GetFlags() & kfDibWantScrolls) {
#if defined(PALM)
// Change this over to DibBitmap::Scroll in the future
RotatedFrontDib *pbmScroll = (RotatedFrontDib *)pbmFront;
pbmScroll->ScrollBlt(&rcSrc, m_dxScrollAccumulate, m_dyScrollAccumulate);
#else
pbmFront->Scroll(&rcSrc, m_dxScrollAccumulate, m_dyScrollAccumulate);
#endif
m_dxScrollAccumulate = 0;
m_dyScrollAccumulate = 0;
return true;
}
#endif
// Perform blt of the back buffer
m_pbm->Blt(m_pbm, &rcSrc, m_dxScrollAccumulate, m_dyScrollAccumulate);
m_dxScrollAccumulate = 0;
m_dyScrollAccumulate = 0;
return true;
}
bool FormMgr::HasCapture()
{
return m_idfCapture != 0;
}
Form *FormMgr::GetFormCapture()
{
if (m_idfCapture == 0) {
return NULL;
}
for (int n = m_cfrm - 1; n >= 0; n--) {
if (m_apfrm[n]->m_idf == m_idfCapture) {
return m_apfrm[n];
}
}
return NULL;
}
Form *FormMgr::GetModalForm()
{
for (int n = m_cfrm - 1; n >= 0; n--) {
if ((m_apfrm[n]->m_wf & (kfFrmDoModal | kfFrmVisible)) == (kfFrmDoModal | kfFrmVisible))
return m_apfrm[n];
}
return NULL;
}
void FormMgr::InvalidateRect(Rect *prc)
{
Rect rcDib;
if (prc == NULL) {
Size siz;
m_pbm->GetSize(&siz);
rcDib.Set(0, 0, siz.cx, siz.cy);
prc = &rcDib;
}
if (m_pupd != NULL)
m_pupd->InvalidateRect(prc);
}
DibBitmap *FormMgr::GetDib()
{
return m_pbm;
}
bool FormMgr::BltTo(DibBitmap *pbmDst, int yTop, bool fScrolled)
{
// If nothing invalid, nothing to do
if (!m_pupd->IsInvalid())
return false;
// If we've scrolled, we've already assured the back dib is valid, so just
// copy it Unless the front dib is handling scrolls in which case do the
// normal blt tiles thing.
if (!(gpdisp->GetFrontDib()->GetFlags() & kfDibWantScrolls)) {
if (fScrolled && !(m_wf & kfFrmmNoScroll)) {
Size sizSrc;
m_pbm->GetSize(&sizSrc);
Rect rcSrc;
rcSrc.Set(0, 0, sizSrc.cx, sizSrc.cy);
pbmDst->Blt(m_pbm, &rcSrc, 0, yTop);
return true;
}
}
// Copy to pbmDst only tiles that are invalid
pbmDst->BltTiles(m_pbm, m_pupd, yTop);
return true;
}
void FormMgr::FrameStart()
{
for (int n = 0; n < m_cfrm; n++) {
m_apfrm[n]->FrameStart();
}
}
void FormMgr::FrameComplete()
{
for (int n = 0; n < m_cfrm; n++) {
m_apfrm[n]->FrameComplete();
}
}
//
// MultiFormMgr
//
MultiFormMgr::MultiFormMgr()
{
m_cfrmm = 0;
memset(m_apfrmm, 0, sizeof(m_apfrmm));
memset(&m_evtPen1Down, 0, sizeof(m_evtPen1Down));
}
MultiFormMgr::~MultiFormMgr()
{
while (m_cfrmm != 0) {
FormMgr *pfrmm = m_apfrmm[0];
RemoveFormMgr(pfrmm);
delete pfrmm;
}
}
void MultiFormMgr::AddFormMgr(FormMgr *pfrmm, Rect *prc)
{
Assert(m_cfrmm < kcFormMgrMax);
m_apfrmm[m_cfrmm] = pfrmm;
m_arcFormMgr[m_cfrmm] = *prc;
m_cfrmm++;
}
void MultiFormMgr::RemoveFormMgr(FormMgr *pfrmm)
{
for (int n = 0; n < m_cfrmm; n++) {
if (m_apfrmm[n] == pfrmm) {
if (n < kcFormMgrMax - 1) {
memmove(&m_apfrmm[n], &m_apfrmm[n + 1], sizeof(FormMgr *) * (kcFormMgrMax - 1 - n));
memmove(&m_arcFormMgr[n], &m_arcFormMgr[n + 1], sizeof(Rect) * (kcFormMgrMax - 1 - n));
}
m_cfrmm--;
break;
}
}
}
void MultiFormMgr::AddForm(Form *pfrm)
{
FormMgr::AddForm(pfrm);
InvalidateRect(NULL);
}
void MultiFormMgr::RemoveForm(Form *pfrm)
{
FormMgr::RemoveForm(pfrm);
InvalidateRect(NULL);
}
bool MultiFormMgr::EcomSuppressed()
{
return FormMgr::EcomSuppressed();
}
Form *MultiFormMgr::GetFocus()
{
Form *pfrm = FormMgr::GetFocus();
if (pfrm == NULL) {
if (m_cfrmm > 0)
return m_apfrmm[m_cfrmm - 1]->GetFocus();
}
return NULL;
}
bool MultiFormMgr::StealCapturePen2(Event *pevt, FormMgr *pfrmmChildCapture)
{
// If this is penDownEvent, remember the pre-cooked event for later
// use.
if (pevt->eType == penDownEvent) {
m_evtPen1Down = *pevt;
return false;
}
// The playfield allows multi-touch select. Sometimes
// finger 1 goes down on a control surrounding the playfield accidentially,
// and finger 2 goes down on the playfield. In this case, finger 1
// needs to be "uncaptured", and both finger 1 and finger 2 down events
// routed to the playfield.
if (pevt->eType != penDownEvent2) {
return false;
}
// Find a form that demands pen 2
Form *pfrmWantsPen2 = FindPen2Form();
if (pfrmWantsPen2 == NULL) {
for (int i = 0; i < m_cfrmm; i++) {
pfrmWantsPen2 = m_apfrmm[i]->FindPen2Form();
if (pfrmWantsPen2 != NULL) {
break;
}
}
}
if (pfrmWantsPen2 == NULL) {
return false;
}
// If this form already has capture, then no need to "steal" capture;
// process normally.
FormMgr *pfrmmWantsPen2 = pfrmWantsPen2->GetFormMgr();
if (pfrmmWantsPen2->HasCapture()) {
return false;
}
// Any modal forms up? Don't steal pen2
if (GetModalForm() != NULL) {
return false;
}
for (int i = 0; i < m_cfrmm; i++) {
if (m_apfrmm[i]->GetModalForm() != NULL) {
return false;
}
}
// Pen1 is captured in a different form manager. Steal that, then
// send pen1down and pen2down to the new form.
if (pfrmmChildCapture != NULL) {
pfrmmChildCapture->BreakCapture();
} else {
if (HasCapture()) {
BreakCapture();
}
}
// Force capture to this form specifically. This will by-pass
// hit testing.
pfrmmWantsPen2->m_idfCapture = pfrmWantsPen2->GetId();
// Find the formmgr's rect
Rect rc;
rc.SetEmpty();
for (int i = 0; i < m_cfrmm; i++) {
if (m_apfrmm[i] == pfrmmWantsPen2) {
rc = m_arcFormMgr[i];
break;
}
}
// These events need to be processed in the right order.
// To do this, they will get posted after being cooked.
Event evtT;
evtT = m_evtPen1Down;
evtT.x -= rc.left;
evtT.y -= rc.top;
pfrmmWantsPen2->CookEvent(&evtT);
gevm.PostEvent(&evtT, false);
evtT = *pevt;
evtT.x -= rc.left;
evtT.y -= rc.top;
pfrmmWantsPen2->CookEvent(&evtT);
gevm.PostEvent(&evtT, false);
// This current event is going to be processed so change it
// to something nop-ish.
pevt->eType = nullEvent;
pevt->idf = 0;
return true;
}
bool MultiFormMgr::CookEvent(Event *pevt)
{
// If no child has capture, give the multi form manager a crack at it
FormMgr *pfrmmChildCapture = NULL;
int n;
for (n = 0; n < m_cfrmm; n++) {
if (m_apfrmm[n]->HasCapture()) {
pfrmmChildCapture = m_apfrmm[n];
break;
}
}
// Treat Pen2 specially.
if (StealCapturePen2(pevt, pfrmmChildCapture)) {
return false;
}
// If no child has it captured and if the multiformmgr wants it, give it
if (pfrmmChildCapture == NULL) {
if (FormMgr::CookEvent(pevt))
return true;
} else {
// Child has it captured. If the multiformmgr has a modal form, force
// the child to release capture. Can happen if a form shows while
// a child has the capture.
if (GetModalForm() != NULL) {
pevt->dw = 1;
pevt->eType = penUpEvent;
// Set this to zero so it doesn't get out of whack when two
// fingers are down. It won't underflow since that is checked.
pfrmmChildCapture->m_cCaptureDowns = 0;
}
}
// Try to handle intelligently
switch (pevt->eType) {
case penHoverEvent:
case penDownEvent:
case penUpEvent:
case penMoveEvent:
case penDownEvent2:
case penUpEvent2:
case penMoveEvent2:
case penHoldEvent:
// Send to the form with the capture
for (n = 0; n < m_cfrmm; n++) {
if (m_apfrmm[n]->HasCapture() ||
m_apfrmm[n]->GetModalForm() != NULL) {
pevt->x -= m_arcFormMgr[n].left;
pevt->y -= m_arcFormMgr[n].top;
if (m_apfrmm[n]->CookEvent(pevt))
return true;
// else allow it to choose not to handle an event even if it
// had capture IE when it releases capture/ends modal and wants
// to pass on that event.
Assert(!m_apfrmm[n]->HasCapture() &&
m_apfrmm[n]->GetModalForm() == NULL);
// restore the pevt
pevt->x += m_arcFormMgr[n].left;
pevt->y += m_arcFormMgr[n].top;
}
}
// Send to the form the event is inside
for (n = 0; n < m_cfrmm; n++) {
if (m_arcFormMgr[n].PtIn(pevt->x, pevt->y)) {
pevt->x -= m_arcFormMgr[n].left;
pevt->y -= m_arcFormMgr[n].top;
return m_apfrmm[n]->CookEvent(pevt);
}
}
break;
case keyDownEvent:
if (FormMgr::CookKeyEvent(pevt))
return true;
// fall through
default:
// Send to the formmgr added last
if (m_cfrmm > 0) {
return m_apfrmm[m_cfrmm - 1]->CookEvent(pevt);
}
}
return false;
}
Form *MultiFormMgr::GetFormPtr(word idf)
{
Form *pfrm = FormMgr::GetFormPtr(idf);
if (pfrm != NULL)
return pfrm;
for (int n = 0; n < m_cfrmm; n++) {
pfrm = m_apfrmm[n]->GetFormPtr(idf);
if (pfrm != NULL)
return pfrm;
}
return NULL;
}
void MultiFormMgr::CalcOpaqueRect(Form *pfrmStop, Rect *prcOpaqueStart, Rect *prcResult)
{
// If this form is part of the multi form mgr, then it's simple
if (pfrmStop == NULL || pfrmStop->GetFormMgr() == this) {
FormMgr::CalcOpaqueRect(pfrmStop, prcOpaqueStart, prcResult);
return;
}
// Otherwise it's whatever is in the multi form mgr plus the child that
// this form is within
FormMgr::CalcOpaqueRect(NULL, prcOpaqueStart, prcResult);
pfrmStop->GetFormMgr()->CalcOpaqueRect(pfrmStop, prcResult, prcResult);
}
bool MultiFormMgr::GetFormMgrRect(FormMgr *pfrmm, Rect *prc)
{
if (pfrmm == this) {
Size sizDib;
m_pbm->GetSize(&sizDib);
prc->left = 0;
prc->top = 0;
prc->right = sizDib.cx;
prc->bottom = sizDib.cy;
return true;
}
for (int n = 0; n < m_cfrmm; n++) {
if (m_apfrmm[n] == pfrmm) {
*prc = m_arcFormMgr[n];
return true;
}
}
Assert();
return false;
}
bool MultiFormMgr::Paint(bool fForceBackBufferValid)
{
// Calc opaque rect
Rect rcOpaque;
rcOpaque.SetEmpty();
CalcOpaqueRect(NULL, NULL, &rcOpaque);
bool fAnyScroll = false;
int n;
for (n = 0; n < m_cfrmm; n++) {
// Paint children form mgr bits
bool fScrolled = m_apfrmm[n]->ScrollBits();
if (fForceBackBufferValid)
fScrolled = true;
if (fScrolled)
fAnyScroll = true;
// Paint / calc updatemaps for children
Rect rcT;
rcT.Intersect(&m_arcFormMgr[n], &rcOpaque);
rcT.Offset(-m_arcFormMgr[n].left, -m_arcFormMgr[n].top);
m_apfrmm[n]->Paint(fScrolled, &rcT);
}
// There is a form, need the children updatemap data to be reflected in the
// parent updatemap
for (n = 0; n < m_cfrmm; n++) {
FormMgr *pfrmm = m_apfrmm[n];
UpdateMap *pupd = pfrmm->GetUpdateMap();
Rect rcT;
bool fFirst = true;
while (pupd->EnumUpdateRects(fFirst, NULL, &rcT)) {
fFirst = false;
rcT.Offset(m_arcFormMgr[n].left, m_arcFormMgr[n].top);
m_pupd->InvalidateRect(&rcT);
}
}
// Now that the multi-form mgr has all the invalid state reflected,
// it is ok to paint
rcOpaque.SetEmpty();
FormMgr::Paint(false, &rcOpaque);
return fAnyScroll;
}
bool MultiFormMgr::BltTo(DibBitmap *pbmDst, int yTop, bool fScrolled)
{
bool fBlt = false;
for (int n = 0; n < m_cfrmm; n++) {
fBlt |= m_apfrmm[n]->BltTo(pbmDst, m_arcFormMgr[n].top, fScrolled);
}
return fBlt;
}
void MultiFormMgr::DrawFrame(bool fForceBackBufferValid, bool fPaint)
{
// Tell forms about to draw frame
for (int n = 0; n < m_cfrmm; n++)
m_apfrmm[n]->FrameStart();
// Notify display we're starting a frame
gpdisp->FrameStart();
// Draw forms
bool fAnyScroll = fPaint ? Paint(fForceBackBufferValid) : false;
// Copy to front buffer
BltTo(gpdisp->GetFrontDib(), 0, fAnyScroll);
// Notify the display we've completed a frame. Pass in update maps
// so selective invalidation can occur
UpdateMap *apupd[kcFormMgrMax];
Rect arc[kcFormMgrMax];
for (int n = 0; n < m_cfrmm; n++) {
apupd[n] = m_apfrmm[n]->GetUpdateMap();
arc[n] = m_arcFormMgr[n];
}
gpdisp->FrameComplete(m_cfrmm, apupd, arc, fAnyScroll);
// Save avi frame
#if defined(WIN) && !defined(CE)
if (gpavir != NULL)
gpavir->AddFrame(gpdisp->GetFrontDib());
#endif
// Draw update rects
#ifdef DRAW_UPDATERECTS
if (gfDrawUpdateRects) {
for (int n = 0; n < m_cfrmm; n++)
DrawUpdateRects(m_apfrmm[n]->GetUpdateMap(), m_arcFormMgr[n].top);
}
#endif
// Validate updatemaps
m_pupd->Validate();
for (int n = 0; n < m_cfrmm; n++) {
m_apfrmm[n]->GetUpdateMap()->Validate();
}
// Tell forms the frame is complete.
for (int n = 0; n < m_cfrmm; n++) {
m_apfrmm[n]->FrameComplete();
}
}
#ifdef DRAW_UPDATERECTS
void MultiFormMgr::DrawUpdateRects(UpdateMap *pupd, int yTop)
{
// Draw update rects
if (gfDrawUpdateRects) {
for (int n = 0; n < m_cfrmm; n++) {
bool fFirst = true;
Rect rc;
while (pupd->EnumUpdateRects(fFirst, NULL, &rc)) {
fFirst = false;
rc.Offset(0, yTop);
gpdisp->DrawFrameInclusive(&rc);
}
}
}
}
#endif
void MultiFormMgr::InvalidateRect(Rect *prc)
{
// Distribute to this form mgr
Rect rcDib;
if (prc == NULL) {
Size siz;
m_pbm->GetSize(&siz);
rcDib.Set(0, 0, siz.cx, siz.cy);
prc = &rcDib;
}
// Distribute to children form mgr's
for (int n = 0; n < m_cfrmm; n++) {
Rect rcT;
if (rcT.Intersect(prc, &m_arcFormMgr[n])) {
rcT.Offset(-m_arcFormMgr[n].left, -m_arcFormMgr[n].top);
m_apfrmm[n]->InvalidateRect(&rcT);
}
}
}
void MultiFormMgr::CheckSetRedrawDirty()
{
for (int n = 0; n < m_cfrmm; n++) {
if (m_apfrmm[n]->GetUpdateMap()->IsInvalid()) {
gevm.SetRedrawFlags(kfRedrawDirty);
return;
}
}
}
bool MultiFormMgr::IsInvalid()
{
for (int n = 0; n < m_cfrmm; n++) {
if (m_apfrmm[n]->GetUpdateMap()->IsInvalid()) {
return true;
}
}
return false;
}
#ifdef STATS_DISPLAY
int MultiFormMgr::GetUpdateRectCount()
{
int c = 0;
for (int n = 0; n < m_cfrmm; n++) {
UpdateMap *pupd = m_apfrmm[n]->GetUpdateMap();
Size sizMap;
pupd->GetMapSize(&sizMap);
int cb = sizMap.cx * sizMap.cy;
bool *pfInvalid = pupd->GetInvalidMap();
while (cb-- != 0) {
if (*pfInvalid++)
c++;
}
}
return c;
}
#endif
} // namespace wi