#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