mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
1067 lines
23 KiB
C++
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
|