hostile-takeover/game/misccontrols.cpp
2016-08-31 23:55:30 -04:00

2148 lines
49 KiB
C++

#include "ht.h"
namespace wi {
// Button control
TBitmap *ButtonControl::s_ptbmLeftUp;
TBitmap *ButtonControl::s_ptbmMidUp;
TBitmap *ButtonControl::s_ptbmRightUp;
TBitmap *ButtonControl::s_ptbmLeftDown;
TBitmap *ButtonControl::s_ptbmMidDown;
TBitmap *ButtonControl::s_ptbmRightDown;
bool ButtonControl::InitClass()
{
s_ptbmLeftUp = GetSharedTBitmap("buttonleftup.tbm");
s_ptbmMidUp = GetSharedTBitmap("buttonmidup.tbm");
s_ptbmRightUp = GetSharedTBitmap("buttonrightup.tbm");
s_ptbmLeftDown = GetSharedTBitmap("buttonleftdown.tbm");
s_ptbmMidDown = GetSharedTBitmap("buttonmiddown.tbm");
s_ptbmRightDown = GetSharedTBitmap("buttonrightdown.tbm");
return true;
}
void ButtonControl::ExitClass()
{
}
ButtonControl::ButtonControl()
{
m_nfnt = 0;
m_ptbmUp = NULL;
m_ptbmDown = NULL;
m_szLabel = NULL;
}
ButtonControl::~ButtonControl()
{
if (m_szLabel != NULL)
gmmgr.FreePtr(m_szLabel);
}
bool ButtonControl::Init(Form *pfrm, word idc, int x, int y, int cx, int cy,
char *pszLabel, int nfnt, char *szFnUp, char *szFnDown, char *szFnDisabled)
{
if (!Control::Init(pfrm, idc, x, y, cx, cy))
return false;
return Init(pszLabel, nfnt, szFnUp, szFnDown, szFnDisabled, false);
}
bool ButtonControl::Init(char *pszLabel, int nfnt, char *szFnUp, char *szFnDown, char *szFnDisabled, bool fCenter)
{
m_nfnt = nfnt;
m_szLabel = (char *)gmmgr.AllocPtr(strlen(pszLabel) + 1);
gmmgr.WritePtr(m_szLabel, 0, pszLabel, strlen(pszLabel) + 1);
if (szFnUp != NULL)
m_ptbmUp = GetSharedTBitmap(szFnUp);
if (szFnDown != NULL)
m_ptbmDown = GetSharedTBitmap(szFnDown);
if (szFnDisabled != NULL)
m_ptbmDisabled = GetSharedTBitmap(szFnDisabled);
if (m_ptbmUp != NULL && m_ptbmDown != NULL) {
Size siz1 = { 0, 0 };
m_ptbmUp->GetSize(&siz1);
Size siz2 = { 0, 0 };
m_ptbmDown->GetSize(&siz2);
int cx = _max(siz1.cx, siz2.cx);
int cy = _max(siz1.cy, siz2.cy);
if (cx != 0 && cy != 0) {
m_rc.right = m_rc.left + _max(siz1.cx, siz2.cx);
m_rc.bottom = m_rc.top + _max(siz1.cy, siz2.cy);
}
} else {
#if 0 // old-style filled-rect buttons
Font *pfnt = gapfnt[m_nfnt];
int cx = pfnt->GetTextExtent(m_szLabel);
int cy = pfnt->GetHeight();
if (m_rc.right == m_rc.left)
m_rc.right = m_rc.left + cx + PcFromFc(3 + 3);
if (m_rc.bottom == m_rc.top)
m_rc.bottom = m_rc.top + cy + PcFromFc(1 + 2);
#else
Size sizMid;
s_ptbmMidUp->GetSize(&sizMid);
// Always set the height correctly so that updatemap is correct
// and TBitmap::Fill() doesn't need to clip.
m_rc.bottom = m_rc.top + sizMid.cy;
// Default width
if (m_rc.right == m_rc.left)
m_rc.right = m_rc.left + (sizMid.cx * 3);
#endif
}
if (fCenter)
m_rc.Offset(-m_rc.Width() / 2, 0);
return true;
}
bool ButtonControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
{
// Base initialization
if (!Control::Init(pfrm, pini, pfind))
return false;
// idc (x y cx cy) "label" nfnt center buttonup.tbm buttondown.tbm
char szFnUp[kcbFilename];
char szFnDown[kcbFilename];
char szFnDisabled[kcbFilename];
char szT[32];
char szLabel[64];
int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) \"%s\" %d %s %s %s %s",
szLabel, &m_nfnt, szT, szFnUp, szFnDown, szFnDisabled);
switch (cArgs) {
case 2:
return Init(szLabel, m_nfnt, NULL, NULL, NULL, false);
case 3:
return Init(szLabel, m_nfnt, NULL, NULL, NULL, strcmp(szT, "center") == 0);
case 5:
return Init(szLabel, m_nfnt, szFnUp, szFnDown, NULL, strcmp(szT, "center") == 0);
case 6:
return Init(szLabel, m_nfnt, szFnUp, szFnDown, szFnDisabled, strcmp(szT, "center") == 0);
}
return false;
}
void ButtonControl::OnPaint(DibBitmap *pbm)
{
Rect rcForm;
m_pfrm->GetRect(&rcForm);
bool fSelected = m_pfrm->IsControlInside(this);
if (m_ptbmDown != NULL && m_ptbmUp != NULL) {
// Draw up / down image (if present)
TBitmap *ptbm;
if (m_wf & kfCtlDisabled)
ptbm = m_ptbmDisabled;
else
ptbm = fSelected ? m_ptbmDown : m_ptbmUp;
ptbm->BltTo(pbm, m_rc.left + rcForm.left, m_rc.top + rcForm.top, m_wf & kfCtlUseSide1Colors ? kside1 : ksideNeutral);
// Center the text (if present)
if (m_szLabel[0] != 0) {
Font *pfnt = gapfnt[m_nfnt];
int cx = pfnt->GetTextExtent(m_szLabel);
int cy = pfnt->GetHeight();
int x = m_rc.left + (m_rc.Width() - cx + 1) / 2;
int y = m_rc.top + (m_rc.Height() - cy + gcxyBorder) / 2;
if (fSelected) {
x++;
y++;
}
gapfnt[m_nfnt]->DrawText(pbm, m_szLabel, x + rcForm.left, y + rcForm.top);
}
} else {
#if 0 // old-style filled-rect buttons
Rect rcT = m_rc;
rcT.Offset(rcForm.left, rcForm.top);
int cxyBorder2x = gcxyBorder * 2;
pbm->Fill(rcT.left + gcxyBorder, rcT.top + gcxyBorder, rcT.Width() - cxyBorder2x, rcT.Height() - cxyBorder2x, GetColor(fSelected ? kiclrButtonFillHighlight : kiclrButtonFill));
int iclr = GetColor(fSelected ? kiclrWhite : kiclrButtonBorder);
pbm->Fill(rcT.left + gcxyBorder, rcT.top, rcT.Width() - cxyBorder2x, gcxyBorder, iclr);
pbm->Fill(rcT.left, rcT.top + gcxyBorder, gcxyBorder, rcT.Height() - cxyBorder2x, iclr);
pbm->Fill(rcT.right - gcxyBorder, rcT.top + gcxyBorder, gcxyBorder, rcT.Height() - cxyBorder2x, iclr);
pbm->Fill(rcT.left + gcxyBorder, rcT.bottom - gcxyBorder, rcT.Width() - cxyBorder2x, gcxyBorder, iclr);
if (m_szLabel[0] != 0) {
Font *pfnt = gapfnt[m_nfnt];
int cx = pfnt->GetTextExtent(m_szLabel);
int cy = pfnt->GetHeight();
int x = m_rc.left + (m_rc.Width() - cx + 1) / 2;
int y = m_rc.top + (m_rc.Height() - cy + gcxyBorder) / 2;
gapfnt[m_nfnt]->DrawText(pbm, m_szLabel, x + rcForm.left, y + rcForm.top);
}
#else
Rect rcT = m_rc;
rcT.Offset(rcForm.left, rcForm.top);
TBitmap *ptbmLeft, *ptbmMid, *ptbmRight;
if (!fSelected) {
ptbmLeft = s_ptbmLeftUp;
ptbmMid = s_ptbmMidUp;
ptbmRight = s_ptbmRightUp;
} else {
ptbmLeft = s_ptbmLeftDown;
ptbmMid = s_ptbmMidDown;
ptbmRight = s_ptbmRightDown;
}
Size sizLeft, sizMid, sizRight;
ptbmLeft->GetSize(&sizLeft);
ptbmMid->GetSize(&sizMid);
ptbmRight->GetSize(&sizRight);
int x = rcT.left;
int xRight = rcT.right - sizRight.cx;
ptbmLeft->BltTo(pbm, x, rcT.top);
x += sizLeft.cx;
#if 0
while (x < xRight) {
ptbmMid->BltTo(pbm, x, rcT.top);
x += sizMid.cx;
}
#else
ptbmMid->FillTo(0, pbm, x, rcT.top, xRight - x, rcT.Height());
x = xRight;
#endif
ptbmRight->BltTo(pbm, xRight, rcT.top);
if (m_szLabel[0] != 0) {
Font *pfnt = gapfnt[m_nfnt];
int cx = pfnt->GetTextExtent(m_szLabel);
int cy = pfnt->GetHeight();
int x = m_rc.left + (m_rc.Width() - cx + 1) / 2;
int y = m_rc.top + ((sizMid.cy - gapfnt[m_nfnt]->GetHeight() + 1) / 2);
gapfnt[m_nfnt]->DrawText(pbm, m_szLabel, x + rcForm.left, y + rcForm.top);
}
#endif
}
}
void ButtonControl::OnSelect(int nSelect)
{
if (m_wf & kfCtlDisabled)
return;
switch (nSelect) {
case knSelMoveInside:
case knSelDownInside:
gsndm.PlaySfx(ksfxGuiButtonTap);
Invalidate();
break;
case knSelUpInside:
case knSelMoveOutside:
Invalidate();
break;
}
Control::OnSelect(nSelect);
}
void ButtonControl::SetText(char *psz)
{
if (strcmp(m_szLabel, psz) == 0)
return;
if (m_szLabel != NULL)
gmmgr.FreePtr(m_szLabel);
m_szLabel = (char *)gmmgr.AllocPtr(strlen(psz) + 1);
gmmgr.WritePtr(m_szLabel, 0, psz, strlen(psz) + 1);
Invalidate();
}
#if 0 // not used now
// Preset button control
bool PresetButtonControl::Init(char *pszLabel, int nfnt, char *szFnUp, char *szFnDown, bool fCenter)
{
if (szFnUp != NULL)
m_ptbmUp = GetSharedTBitmap(szFnUp);
if (szFnDown != NULL)
m_ptbmDown = GetSharedTBitmap(szFnDown);
if (m_ptbmUp != NULL) {
Size siz1 = { 0, 0 };
m_ptbmUp->GetSize(&siz1);
if (siz1.cx != 0 && siz1.cy != 0) {
m_rc.right = m_rc.left + siz1.cx + 2;
m_rc.bottom = m_rc.top + siz1.cy + 2;
}
}
if (fCenter)
m_rc.Offset(-m_rc.Width() / 2, 0);
return true;
}
void PresetButtonControl::OnPaint(DibBitmap *pbm)
{
bool fSet = (m_wf & kfCtlSet) != 0;
Rect rcForm;
m_pfrm->GetRect(&rcForm);
bool fSelected = m_pfrm->IsControlInside(this);
Rect rcT = m_rc;
rcT.Offset(rcForm.left, rcForm.top);
if (fSelected) {
pbm->Fill(rcT.left + 1, rcT.top + 1, rcT.Width() - 2, rcT.Height() - 2, GetColor(kiclrButtonFillHighlight));
}
TBitmap *ptbm;
if (fSet)
ptbm = m_ptbmDown;
else
ptbm = m_ptbmUp;
if (ptbm != NULL)
ptbm->BltTo(pbm, m_rc.left + rcForm.left + 1, m_rc.top + rcForm.top + 1);
}
#endif
// CheckBox Control
TBitmap *CheckBoxControl::s_ptbmOnUp;
TBitmap *CheckBoxControl::s_ptbmOnDown;
TBitmap *CheckBoxControl::s_ptbmOffUp;
TBitmap *CheckBoxControl::s_ptbmOffDown;
CheckBoxControl::CheckBoxControl()
{
m_fChecked = false;
m_ifnt = 0;
m_szLabel[0] = 0;
}
CheckBoxControl::~CheckBoxControl()
{
}
bool CheckBoxControl::Init(Form *pfrm, word idc, int x, int y, char *pszLabel, int ifnt, bool fChecked)
{
if (!Control::Init(pfrm, idc, x, y, 0, 0))
return false;
return Init(pszLabel, ifnt, fChecked);
}
bool CheckBoxControl::InitClass()
{
s_ptbmOnUp = GetSharedTBitmap("checkboxonup.tbm");
s_ptbmOnDown = GetSharedTBitmap("checkboxondown.tbm");
s_ptbmOffUp = GetSharedTBitmap("checkboxoffup.tbm");
s_ptbmOffDown = GetSharedTBitmap("checkboxoffdown.tbm");
return true;
}
void CheckBoxControl::ExitClass()
{
}
bool CheckBoxControl::Init(char *pszLabel, int ifnt, bool fChecked)
{
m_fChecked = fChecked;
m_ifnt = ifnt;
SetText(pszLabel);
return true;
}
bool CheckBoxControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
{
// Base initialization
if (!Control::Init(pfrm, pini, pfind))
return false;
// idc (x y cx cy) "label" ifnt [checked]
char szChecked[64];
szChecked[0] = 0;
int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) \"%s\" %d %s",
m_szLabel, &m_ifnt, szChecked);
if (cArgs == 2)
return Init(m_szLabel, m_ifnt, false);
if (cArgs != 3)
return false;
return Init(m_szLabel, m_ifnt, strcmp(szChecked, "checked") == 0);
}
void CheckBoxControl::OnPaint(DibBitmap *pbm)
{
Rect rcForm;
m_pfrm->GetRect(&rcForm);
// Draw up / down image (if present)
TBitmap *ptbm;
bool fPenDownInside = m_pfrm->IsControlInside(this);
if (fPenDownInside) {
ptbm = m_fChecked ? s_ptbmOnDown : s_ptbmOffDown;
} else {
ptbm = m_fChecked ? s_ptbmOnUp : s_ptbmOffUp;
}
// Center the text horizontally and draw to the right of the checkbox
Font *pfnt = gapfnt[m_ifnt];
int cy = pfnt->GetHeight();
Size siz;
ptbm->GetSize(&siz);
ptbm->BltTo(pbm, m_rc.left + rcForm.left, m_rc.top + (cy - siz.cy) / 2 + rcForm.top - 1);
gapfnt[m_ifnt]->DrawText(pbm, m_szLabel, m_rc.left + rcForm.left + siz.cx, m_rc.top + rcForm.top);
}
void CheckBoxControl::OnSelect(int nSelect)
{
switch (nSelect) {
case knSelMoveInside:
case knSelDownInside:
gsndm.PlaySfx(ksfxGuiCheckBoxTap);
Invalidate();
break;
case knSelUpInside:
m_fChecked ^= 1;
Invalidate();
break;
case knSelMoveOutside:
Invalidate();
break;
}
Control::OnSelect(nSelect);
}
void CheckBoxControl::SetText(char *psz)
{
strncpyz(m_szLabel, psz, sizeof(m_szLabel));
Size siz;
s_ptbmOnUp->GetSize(&siz);
Font *pfnt = gapfnt[m_ifnt];
int cx = pfnt->GetTextExtent(m_szLabel);
m_rc.right = m_rc.left + siz.cx + pfnt->GetTextExtent(m_szLabel);
m_rc.bottom = m_rc.top + pfnt->GetHeight();
Invalidate();
}
bool CheckBoxControl::IsChecked()
{
return m_fChecked;
}
void CheckBoxControl::SetChecked(bool fChecked)
{
m_fChecked = fChecked;
Invalidate();
}
// Label Control
LabelControl::LabelControl()
{
m_nfnt = 0;
m_szLabel = NULL;
}
LabelControl::~LabelControl()
{
if (m_szLabel != NULL)
gmmgr.FreePtr(m_szLabel);
}
bool LabelControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
{
// Base initialization
if (!Control::Init(pfrm, pini, pfind))
return false;
// idc (x y cx cy) "label" nfnt
char szLabel[256];
char szFlag1[32], szFlag2[32];
szFlag1[0] = 0;
szFlag2[0] = 0;
int nfnt;
int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) \"%s\" %d %s %s",
szLabel, &nfnt, szFlag1, szFlag2);
if (cArgs < 2 || cArgs > 4)
return false;
return Init(nfnt, szLabel, szFlag1, szFlag2);
}
bool LabelControl::Init(int nfnt, char *pszLabel, char *pszFlag1, char *pszFlag2)
{
m_nfnt = nfnt;
char *pszT = (char *)gpbScratch + (gcbScratch / 2);
ExpandVars(pszLabel, pszT, kcbLabelTextMax);
m_szLabel = (char *)gmmgr.AllocPtr(strlen(pszT) + 1);
gmmgr.WritePtr(m_szLabel, 0, pszT, strlen(pszT) + 1);
if (strcmp(pszFlag1, "center") == 0)
m_wf |= kfLblCenterText;
else if (strcmp(pszFlag1, "multiline") == 0)
m_wf |= kfLblMultiLine;
else if (strcmp(pszFlag1, "right") == 0)
m_wf |= kfLblRightText;
if (strcmp(pszFlag2, "center") == 0)
m_wf |= kfLblCenterText;
else if (strcmp(pszFlag2, "multiline") == 0)
m_wf |= kfLblMultiLine;
else if (strcmp(pszFlag2, "right") == 0)
m_wf |= kfLblRightText;
else if (strcmp(pszFlag2, "clipvert") == 0)
m_wf |= kfLblClipVertical;
CalcRect();
return true;
}
void LabelControl::OnPaint(DibBitmap *pbm)
{
if (m_szLabel == NULL)
return;
Rect rcForm;
m_pfrm->GetRect(&rcForm);
Font *pfnt = gapfnt[m_nfnt];
if (m_wf & kfLblMultiLine) {
Font *pfnt = gapfnt[m_nfnt];
int cyClip = -1;
if (m_wf & kfLblClipVertical) {
cyClip = m_rc.Height();
}
pfnt->DrawText(pbm, m_szLabel, m_rc.left + rcForm.left,
m_rc.top + rcForm.top, m_rc.Width(), cyClip,
(m_wf & kfLblEllipsis) != 0);
} else {
DrawFancyText(pbm, pfnt, m_szLabel, m_rc.left + rcForm.left,
m_rc.top + rcForm.top);
}
}
int LabelControl::OnHitTest(Event *pevt)
{
if (m_wf & kfLblHitTest) {
return Control::OnHitTest(pevt);
}
// Assuming that passing Label selected events through to Forms does
// more harm than good (e.g., Forms have to case out labels) we'll
// just nip this in the bud.
return -1;
}
void LabelControl::SetText(const char *psz)
{
if (psz == NULL) {
if (m_szLabel != NULL) {
gmmgr.FreePtr(m_szLabel);
m_szLabel = NULL;
}
} else {
if (strcmp(psz, m_szLabel) == 0)
return;
if (m_szLabel != NULL)
gmmgr.FreePtr(m_szLabel);
// Use the tail end of the scratch buffer because ExpandVars
// (potentially) calls StringTable::GetString which reads from the
// database, decompressing to the front of the scratch buffer as part
// of the process.
char *pszT = (char *)gpbScratch + (gcbScratch / 2);
ExpandVars((char *)psz, pszT, kcbLabelTextMax);
m_szLabel = (char *)gmmgr.AllocPtr(strlen(pszT) + 1);
gmmgr.WritePtr(m_szLabel, 0, pszT, strlen(pszT) + 1);
}
CalcRect();
}
void LabelControl::CalcRect()
{
Rect rcNew = m_rc;
int cx, cy;
if (m_szLabel == NULL) {
cx = cy = 0;
} else {
Font *pfnt = gapfnt[m_nfnt];
if (m_wf & kfLblMultiLine) {
cx = m_rc.Width();
if (m_wf & kfLblClipVertical) {
cy = m_rc.Height();
} else {
cy = pfnt->CalcMultilineHeight(m_szLabel, m_rc.Width());
}
} else {
cx = GetFancyTextExtent(pfnt, m_szLabel);
cy = pfnt->GetHeight();
}
}
if (m_wf & kfLblCenterText) {
rcNew.left = (m_rc.left + m_rc.Width() / 2) - cx / 2;
} else if (m_wf & kfLblRightText) {
rcNew.left = m_rc.right - cx;
} else {
rcNew.left = m_rc.left;
}
rcNew.right = rcNew.left + cx;
rcNew.bottom = rcNew.top + cy;
SetRect(&rcNew, false);
}
// FancyText supports special character sequences to control the formatting
// of the text as it is displayed. Callers should also note that the default
// font has a number of special symbol characters in it.
//
// Special characters
// ------------------
// @ - Galaxite icon
// \ - Power (Reactor) icon
//
// Control sequences
// -----------------
// @S. - subsequent characters' side colors are mapped to the local player's side
// @@ - escaped '@'. Use to output an '@'
int FancyTextCore(DibBitmap *pbm, Font *pfntDefault, char *psz, int x, int y, int cch, bool fGetTextExtent) secControl;
int DrawFancyText(DibBitmap *pbm, Font *pfntDefault, char *psz, int x, int y, int cch)
{
return FancyTextCore(pbm, pfntDefault, psz, x, y, cch, false);
}
int GetFancyTextExtent(Font *pfntDefault, char *psz, int cch)
{
return FancyTextCore(NULL, pfntDefault, psz, 0, 0, cch, true);
}
int FancyTextCore(DibBitmap *pbm, Font *pfntDefault, char *psz, int x, int y, int cch, bool fGetExtent)
{
dword *mpscaiclr = NULL;
Font *pfnt = pfntDefault;
if (cch == 0)
cch = (int)strlen(psz);
int cx = 0;
int cchT = cch;
char *pchT = psz;
while (cchT > 0) {
char ch = *pchT++;
cchT--;
if (ch == '@') {
if (pchT != psz + 1) {
if (fGetExtent) {
cx += pfnt->GetTextExtent(psz, (int)(pchT - psz - 1));
} else {
cx += pfnt->DrawText(pbm, psz, x, y, (int)(pchT - psz - 1), mpscaiclr);
x += cx;
}
psz = pchT;
}
switch (*pchT) {
// "@@" outputs a single @
case '@':
cchT--;
psz = pchT++;
continue;
// "@S." turns on mapping to local player's side color
case 'S':
mpscaiclr = TBitmap::s_ampscaiclrSide[gpplrLocal->GetSide()];
cchT--;
pchT++;
break;
}
Assert(*pchT == '.');
// Skip past '.'
cchT--;
pchT++;
psz = pchT;
}
}
if (pchT != psz) {
if (fGetExtent)
cx += pfnt->GetTextExtent(psz, (int)(pchT - psz));
else
cx += pfnt->DrawText(pbm, psz, x, y, (int)(pchT - psz), mpscaiclr);
}
return cx;
}
// Bitmap Control
BitmapControl::BitmapControl()
{
m_phtbm = NULL;
}
BitmapControl::~BitmapControl()
{
delete m_phtbm;
}
bool BitmapControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
{
// Base initialization
if (!Control::Init(pfrm, pini, pfind))
return false;
// idc (x y cx cy) bitmap.tbm
char szBitmap[kcbFilename];
int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) %s", szBitmap);
if (cArgs == 0) {
m_rc.right = m_rc.left;
m_rc.bottom = m_rc.top;
return true;
}
if (cArgs != 1)
return false;
// Distinguish between RawBitmaps and TBitmaps
// UNDONE: this should probably be handled by an HtBitmap::Init.
int cch = (int)strlen(szBitmap);
if (szBitmap[cch - 3] == 'r') {
Assert(szBitmap[cch - 4] == '.' && szBitmap[cch - 2] == 'b' && szBitmap[cch - 1] == 'm');
m_phtbm = new RawBitmap();
} else {
m_phtbm = new TBitmap();
}
if (m_phtbm == NULL)
return false;
if (!m_phtbm->Init(szBitmap))
return false;
Size siz = { 0, 0 };
m_phtbm->GetSize(&siz);
m_rc.right = m_rc.left + siz.cx;
m_rc.bottom = m_rc.top + siz.cy;
return true;
}
void BitmapControl::OnPaint(DibBitmap *pbm)
{
Rect rcForm;
m_pfrm->GetRect(&rcForm);
// Draw image
if (m_phtbm != NULL)
// HACK: this "& ~1" is to force word alignment on CE as required by RawBitmap::BltTo
// Character portrats have some side color in them that must be mapped to blue
m_phtbm->BltTo(pbm, (m_rc.left + rcForm.left) & ~1, m_rc.top + rcForm.top, kside1);
}
void BitmapControl::SetBitmap(HtBitmap *phtbm)
{
if (m_phtbm != NULL)
delete m_phtbm;
m_phtbm = phtbm;
if (m_phtbm != NULL) {
Size siz;
m_phtbm->GetSize(&siz);
if (m_rc.Width() < siz.cx)
m_rc.right = m_rc.left + siz.cx;
if (m_rc.Height() < siz.cy)
m_rc.bottom = m_rc.top + siz.cy;
}
Invalidate();
}
// Edit Control
EditControl::EditControl()
{
m_nfnt = 0;
m_szText[0] = 0;
}
bool EditControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
{
// Base initialization
if (!Control::Init(pfrm, pini, pfind))
return false;
// idc (x y cx cy) "Text" nfnt
int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) \"%s\" %d",
m_szText, &m_nfnt);
if (cArgs != 2)
return false;
if (m_rc.Height() == 0)
m_rc.bottom = m_rc.top + gapfnt[m_nfnt]->GetHeight();
return true;
}
void EditControl::OnPaint(DibBitmap *pbm)
{
Font *pfnt = gapfnt[m_nfnt];
Rect rcForm;
m_pfrm->GetRect(&rcForm);
int x = m_rc.left + rcForm.left;
int y = m_rc.top + rcForm.top;
pbm->Fill(x, y + m_rc.Height() - 1, m_rc.Width(), 1, GetColor(kiclrButtonBorder));
pfnt->DrawText(pbm, m_szText, x + 1, y);
}
void EditControl::SetText(const char *psz)
{
strncpyz(m_szText, psz, sizeof(m_szText));
Invalidate();
}
void EditControl::GetText(char *psz, int cb)
{
strncpyz(psz, m_szText, cb);
}
//
// List Control
//
#define kcxyListBorder gcxyBorder
#define kcxListIntMargin PcFromFc(2)
#define kcyListLineSpace gcxyBorder
TBitmap *ListControl::s_ptbmScrollUpUp;
TBitmap *ListControl::s_ptbmScrollUpDown;
TBitmap *ListControl::s_ptbmScrollDownUp;
TBitmap *ListControl::s_ptbmScrollDownDown;
bool ListControl::InitClass()
{
s_ptbmScrollUpUp = GetSharedTBitmap("scrollupup.tbm");
s_ptbmScrollUpDown = GetSharedTBitmap("scrollupdown.tbm");
s_ptbmScrollDownUp = GetSharedTBitmap("scrolldownup.tbm");
s_ptbmScrollDownDown = GetSharedTBitmap("scrolldowndown.tbm");
return true;
}
void ListControl::ExitClass()
{
}
ListControl::ListControl()
{
m_nfnt = 0;
m_pliFirst = NULL;
m_pliLast = NULL;
m_cyItem = 0;
m_iliTop = 0;
m_cli = 0;
memset(m_awfTab, 0, sizeof(m_awfTab));
memset(m_axTab, 0xff, sizeof(m_axTab));
m_axTab[0] = 0;
m_fDrag = false;
m_fTimerAdded = false;
m_iclrScrollPos = kiclrMediumGray;
m_fPenDown = false;
}
ListControl::~ListControl()
{
// Do this to free any allocated ListItem structures
Clear();
#if 0 // Don't delete these because the form will do it
if (m_pbtnUp != NULL)
delete m_pbtnUp;
if (m_pbtnDown != NULL)
delete m_pbtnDown;
#endif
if (m_fTimerAdded) {
gtimm.RemoveTimer(this);
}
}
bool ListControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
{
// Base initialization
if (!Control::Init(pfrm, pini, pfind))
return false;
// idc (x y cx cy) "Text" nfnt
int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) %d",
&m_nfnt);
if (cArgs != 1)
return false;
Font *pfnt = gapfnt[m_nfnt];
m_cyItem = pfnt->GetHeight() + kcyListLineSpace - pfnt->GetLineOverlap();
m_cxEllipsis = pfnt->GetTextExtent("...");
#if defined(IPHONE) || defined(__IPHONEOS__) || defined(__ANDROID__)
m_wf |= kfLstcScrollPosition;
#endif
return true;
}
void ListControl::GetSubRects(Rect *prcInterior, Rect *prcUpArrow,
Rect *prcDownArrow, Rect *prcScrollPosition)
{
Rect rc = m_rc;
if (m_wf & kfLstcBorder) {
rc.Inflate(-gcxyBorder * 2, -gcxyBorder * 2);
}
if (prcScrollPosition != NULL) {
prcScrollPosition->SetEmpty();
}
if (prcUpArrow != NULL) {
prcUpArrow->SetEmpty();
}
if (prcDownArrow != NULL) {
prcDownArrow->SetEmpty();
}
// If all the elements can be displayed without scrolling than the interior
// area can fill the whole ListControl
Size sizArrow;
s_ptbmScrollUpUp->GetSize(&sizArrow);
if (rc.Height() / m_cyItem >= m_cli) {
if (m_wf & kfLstcKeepInteriorPositioning) {
*prcInterior = rc;
prcInterior->Inflate(0, -sizArrow.cy);
} else {
*prcInterior = rc;
}
return;
}
if (m_wf & kfLstcScrollPosition) {
// Maintain interior positioning (with arrows) so that form
// layout doesn't change if this bit is on.
prcInterior->Set(rc.left, rc.top + sizArrow.cy,
rc.right - gcxyBorder * 3, rc.bottom - sizArrow.cy);
if (prcScrollPosition != NULL) {
prcScrollPosition->left = rc.right - gcxyBorder * 2;
prcScrollPosition->top = prcInterior->top;
prcScrollPosition->right = rc.right;
prcScrollPosition->bottom = prcInterior->bottom -
prcInterior->Height() % m_cyItem;
}
} else {
int xArrow = rc.left + ((rc.Width() - sizArrow.cx) / 2);
if (prcUpArrow != NULL) {
prcUpArrow->Set(xArrow - sizArrow.cx, rc.top,
xArrow + sizArrow.cx * 2, rc.top + sizArrow.cy);
}
if (prcDownArrow != NULL) {
prcDownArrow->Set(xArrow - sizArrow.cx, rc.bottom - sizArrow.cy,
xArrow + sizArrow.cx * 2, rc.bottom);
}
if (prcInterior != NULL) {
prcInterior->Set(rc.left, rc.top + sizArrow.cy, rc.right,
rc.bottom - sizArrow.cy);
}
}
}
void ListControl::OnPaint(DibBitmap *pbm)
{
Rect rcForm;
m_pfrm->GetRect(&rcForm);
Rect rc;
Rect rcScrollPos;
GetSubRects(&rc, NULL, NULL, &rcScrollPos);
rc.Offset(rcForm.left, rcForm.top);
rcScrollPos.Offset(rcForm.left, rcForm.top);
Size sizArrow;
s_ptbmScrollUpUp->GetSize(&sizArrow);
int cliDraw = GetVisibleItemCount();
int cx = rc.Width();
int x = rc.left;
int y = rc.top;
int xArrow = rc.left + ((cx - sizArrow.cx) / 2);
// Walk through the list to the item that should be displayed first
ListItem *pli;
int ili = 0;
for (pli = m_pliFirst; ili < m_iliTop; pli = pli->pliNext, ili++);
int cliDrawn = 0;
for (; pli != NULL && cliDraw > 0;
pli = pli->pliNext, y += m_cyItem, cliDraw--) {
DrawItem(pbm, pli, x, y, cx, m_cyItem);
cliDrawn++;
}
bool fNeedsScrollUp = NeedsScrollUpArrow();
bool fNeedsScrollDown = NeedsScrollDownArrow();
if (m_wf & kfLstcScrollPosition) {
if (fNeedsScrollUp || fNeedsScrollDown) {
pbm->Shadow(rcScrollPos.left, rcScrollPos.top, rcScrollPos.Width(),
rcScrollPos.Height());
pbm->Shadow(rcScrollPos.left, rcScrollPos.top, rcScrollPos.Width(),
rcScrollPos.Height());
int y1 = rcScrollPos.Height() * m_iliTop / m_cli;
int y2 = rcScrollPos.Height() * (m_iliTop + cliDrawn) / m_cli;
pbm->Fill(rcScrollPos.left, rcScrollPos.top + y1,
rcScrollPos.Width(), y2 - y1,
#if 0
m_fPenDown ? GetColor(kiclrWhite) :
GetColor(m_iclrScrollPos));
#else
GetColor(m_iclrScrollPos));
#endif
}
} else {
if (fNeedsScrollUp) {
s_ptbmScrollUpUp->BltTo(pbm, xArrow, rc.top - sizArrow.cy);
}
if (fNeedsScrollDown) {
s_ptbmScrollDownUp->BltTo(pbm, xArrow, rc.bottom);
}
}
if (m_wf & kfLstcBorder) {
Rect rcT;
rcT = m_rc;
rcT.Offset(rcForm.left, rcForm.top);
DrawBorder(pbm, &rcT, 1, GetColor(kiclrListBorder));
}
}
bool ListControl::NeedsScrollUpArrow()
{
return m_iliTop != 0;
}
bool ListControl::NeedsScrollDownArrow()
{
int ciliVisible = GetVisibleItemCount();
return m_cli > m_iliTop + ciliVisible;
}
void ListControl::OnControlSelected(word idc)
{
int iliVisible = GetVisibleItemCount();
if (idc == kidcScrollUpButton) {
m_iliTop -= iliVisible;
} else {
m_iliTop += iliVisible;
}
Invalidate();
}
void ListControl::OnControlNotify(word idc, int nNotify)
{
}
bool ListControl::OnControlHeld(word idc)
{
return false;
}
void ListControl::SetTabStops(int x0, int x1, int x2, int x3)
{
m_axTab[0] = x0;
m_axTab[1] = x1;
m_axTab[2] = x2;
m_axTab[3] = x3;
}
void ListControl::SetTabFlags(word wf0, word wf1, word wf2, word wf3)
{
m_awfTab[0] = wf0;
m_awfTab[1] = wf1;
m_awfTab[2] = wf2;
m_awfTab[3] = wf3;
}
void ListControl::DrawItem(DibBitmap *pbm, ListItem *pli, int x, int y,
int cx, int cy)
{
if (pli->fSelected) {
pbm->Fill(x, y, cx, cy, GetColor(kiclrButtonFillHighlight));
}
char *pszTabNext = pli->szText - 1;
int iTab = 0;
do {
char *pszAfterTab = pszTabNext + 1;
pszTabNext = strchr(pszAfterTab, '\t');
// Figure out what to draw - up to the next tab
char szT[80];
char *pszDraw;
if (pszTabNext == NULL) {
pszDraw = pszAfterTab;
} else {
strncpyz(szT, pszAfterTab,
_min((int)sizeof(szT), (int)(pszTabNext - pszAfterTab + 1)));
pszDraw = szT;
}
// Figure out the width of this tab column
int cxT;
if (iTab < ARRAYSIZE(m_axTab) - 1 && m_axTab[iTab + 1] >= 0) {
cxT = m_axTab[iTab + 1] - m_axTab[iTab];
} else {
cxT = cx - m_axTab[iTab];
}
// Draw
DrawText(pbm, pszDraw, x + m_axTab[iTab], y, cxT, cy,
m_awfTab[iTab]);
iTab++;
} while (pszTabNext != NULL && iTab < ARRAYSIZE(m_axTab));
}
void ListControl::DrawText(DibBitmap *pbm, char *psz, int x, int y,
int cx, int cy, word wf)
{
Font *pfnt = gapfnt[m_nfnt];
int dx = 0;
if (wf & kfLstTabCenter) {
int cxT = pfnt->GetTextExtent(psz);
if (cxT > cx) {
Rect rc;
GetSubRects(&rc);
int xT = x + (cx - cxT);
if (xT <= rc.left) {
dx = rc.left - x;
} else {
dx = xT - x;
wf &= ~kfLstTabEllipsis;
}
} else {
dx = (cx - cxT) / 2;
wf &= ~kfLstTabEllipsis;
}
}
if (wf & kfLstTabCenterOn) {
int cxT = pfnt->GetTextExtent(psz);
dx = -cxT / 2;
}
if (wf & kfLstTabRight) {
int cxT = pfnt->GetTextExtent(psz);
dx = -cxT;
}
x += dx;
cx -= dx;
// Make room before the next tab stop for ellipsis
if (wf & kfLstTabEllipsis) {
pfnt->DrawTextWithEllipsis(pbm, psz, (int)strlen(psz), x,
y + kcyListLineSpace, cx);
} else {
pfnt->DrawText(pbm, psz, x, y + kcyListLineSpace, cx, cy, false);
}
}
void ListControl::OnPenEvent(Event *pevt)
{
switch (pevt->eType) {
case penDownEvent:
m_fDrag = false;
m_yDrag = pevt->y;
m_iliTopDrag = m_iliTop;
m_fPenDown = true;
Invalidate();
break;
case penMoveEvent:
if (!m_fDrag) {
if (abs(m_yDrag - pevt->y) >= m_cyItem / 2) {
m_fDrag = true;
}
}
break;
case penUpEvent:
m_fPenDown = false;
Invalidate();
if (m_fDrag) {
if (m_flics.Init(1, 1.0f, 0.12f, 33, false)) {
m_yDragUp = pevt->y;
gtimm.AddTimer(this, 10);
m_fTimerAdded = true;
}
m_fDrag = false;
return;
}
break;
default:
m_fDrag = false;
break;
}
if (!m_fDrag) {
m_flics.Clear();
OnPenEvent2(pevt);
return;
}
DragScroll(pevt->y);
}
void ListControl::DragScroll(int y)
{
int dy = m_yDrag - y;
int ciScroll = (abs(dy) + m_cyItem / 2) / m_cyItem;
if (ciScroll == 0) {
return;
}
if (dy < 0) {
ciScroll = -ciScroll;
}
int iliTopNew = m_iliTopDrag + ciScroll;
if (iliTopNew < 0) {
iliTopNew = 0;
}
int cVisible = GetVisibleItemCount();
if (iliTopNew > m_cli - cVisible) {
iliTopNew = m_cli - cVisible;
if (iliTopNew < 0) {
iliTopNew = 0;
}
}
if (iliTopNew == m_iliTop) {
return;
}
m_iliTop = iliTopNew;
Invalidate();
//Trace("DragScroll: m_yDrag=%d y=%d dy=%d", m_yDrag, y, dy);
}
void ListControl::OnTimer(long tCurrent)
{
if (!m_flics.HasMagnitude()) {
gtimm.RemoveTimer(this);
m_fTimerAdded = false;
return;
}
// GetPosition returns a delta.
Point pt;
m_flics.GetPosition(&pt);
DragScroll(pt.y + m_yDragUp);
}
void ListControl::OnPenEvent2(Event *pevt)
{
// UNDONE: track pen inside/outside of arrows and arrow pressed state
Rect rcForm;
m_pfrm->GetRect(&rcForm);
// Finger hittesting works as follows:
// 1. Out of the desired hit test rects, find the "closest" one
// 2. See if the event is within the "finger rect", an expansion of the
// regular rect.
Rect rcInterior, rcUpArrow, rcDownArrow;
GetSubRects(&rcInterior, &rcUpArrow, &rcDownArrow);
rcInterior.Offset(rcForm.left, rcForm.top);
rcUpArrow.Offset(rcForm.left, rcForm.top);
rcDownArrow.Offset(rcForm.left, rcForm.top);
// Find the closest selectable item
int iliBottom = (m_iliTop + (rcInterior.Height() / m_cyItem)) - 1;
if (iliBottom >= m_cli)
iliBottom = m_cli - 1;
int nDistClosest = 99999;
int iClosest = -1;
for (int ili = m_iliTop; ili <= iliBottom; ili++) {
Rect rc;
rc.left = rcInterior.left;
rc.top = rcInterior.top + (ili - m_iliTop) * m_cyItem;
rc.right = rcInterior.right;
rc.bottom = rc.top + m_cyItem;
int nDist = rc.GetDistance(pevt->x, pevt->y);
if (nDist < nDistClosest) {
iClosest = ili - m_iliTop;
nDistClosest = nDist;
}
}
int nDistHit = 0;
if (pevt->ff & kfEvtFinger) {
nDistHit = 20; // Need a constant here
}
// Compare this best distance against the up and down arrows.
if (!(m_wf & kfLstcScrollPosition)) {
int nDistUp = rcUpArrow.GetDistance(pevt->x, pevt->y);
int nDistDown = rcDownArrow.GetDistance(pevt->x, pevt->y);
if (nDistUp < nDistDown) {
if (nDistUp < nDistClosest) {
if (nDistUp <= nDistHit) {
if (pevt->eType == penUpEvent && NeedsScrollUpArrow()) {
gsndm.PlaySfx(ksfxGuiScrollingListSelectItem);
OnControlSelected(kidcScrollUpButton);
}
}
return;
}
} else {
if (nDistDown < nDistClosest) {
if (nDistDown <= nDistHit) {
if (pevt->eType == penUpEvent && NeedsScrollDownArrow()) {
gsndm.PlaySfx(ksfxGuiScrollingListSelectItem);
OnControlSelected(kidcScrollDownButton);
}
}
return;
}
}
}
if (pevt->eType != penDownEvent) {
return;
}
// Pin selection to min/max entry
Assert(iClosest >= 0 && iClosest <= iliBottom - m_iliTop);
if (iClosest < 0)
return;
int iliSel;
bool fOnItem;
if (nDistClosest <= nDistHit) {
iliSel = m_iliTop + iClosest;
fOnItem = true;
} else {
iliSel = -1;
fOnItem = false;
}
int ili = 0;
bool fChange = false;
for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext, ili++) {
if (pli->fSelected != (ili == iliSel)) {
pli->fSelected = (ili == iliSel);
fChange = true;
}
}
if (fChange) {
m_pceh->OnControlNotify(m_idc, knNotifySelectionChange);
gsndm.PlaySfx(ksfxGuiScrollingListSelectItem);
Invalidate();
} else {
if (fOnItem && pevt->eType == penDownEvent)
m_pceh->OnControlNotify(m_idc, knNotifySelectionTap);
}
}
void ListControl::Clear()
{
ListItem *pli = m_pliFirst;
while (pli != NULL) {
ListItem *pliDel = pli;
pli = pli->pliNext;
delete pliDel;
}
m_pliFirst = NULL;
m_pliLast = NULL;
m_cli = 0;
m_iliTop = 0;
Invalidate();
}
Font *ListControl::GetFont()
{
return gapfnt[m_nfnt];
}
int ListControl::GetCount()
{
return m_cli;
}
void ListControl::OnSelect(int nSelect)
{
Control::OnSelect(nSelect);
}
int ListControl::GetVisibleItemCount()
{
Rect rc;
GetSubRects(&rc);
return rc.Height() / m_cyItem;
}
bool ListControl::Add(ListItem *pli)
{
pli->fSelected = false;
pli->pliNext = NULL;
if (m_pliLast != NULL)
m_pliLast->pliNext = pli;
else
m_pliFirst = pli;
m_pliLast = pli;
m_cli++;
Invalidate();
return true;
}
bool ListControl::Add(const char *psz, void *pvData)
{
ListItem *pli = new ListItem;
if (pli == NULL)
return false;
strncpyz(pli->szText, psz, sizeof(pli->szText));
pli->pvData = pvData;
return Add(pli);
}
void ListControl::Select(int iliSelect, bool fOnly, bool fMakeCenter)
{
ListItem *pli = m_pliFirst;
bool fChange = false;
// Single selection only
if (fOnly) {
int ili = 0;
for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext, ili++) {
if (iliSelect != ili) {
if (pli->fSelected)
fChange = true;
pli->fSelected = false;
} else {
if (!pli->fSelected)
fChange = true;
pli->fSelected = true;
}
}
}
int ili = 0;
pli = m_pliFirst;
for (; pli != NULL && ili < iliSelect; pli = pli->pliNext, ili++)
;
if (ili == iliSelect && pli != NULL) {
pli->fSelected = true;
int ciliVisible = GetVisibleItemCount();
int iliTop;
if (fMakeCenter) {
// Almost center
iliTop = iliSelect - ciliVisible * 2 / 5;
if (iliTop < 0) {
iliTop = 0;
}
} else {
// Make sure the selected item is visible.
int ciliVisible = GetVisibleItemCount();
iliTop = iliSelect - (iliSelect % ciliVisible);
}
// Make sure a full list is shown if selected item is near the end.
if (iliSelect > m_cli - ciliVisible) {
iliTop = m_cli - ciliVisible;
if (iliTop < 0) {
iliTop = 0;
}
}
// If the selected item can be visible and the list be at
// the beginning, do that because it feels better.
if (iliSelect < ciliVisible) {
iliTop = 0;
}
m_iliTop = iliTop;
OnSelect(knSelUpInside);
m_pceh->OnControlNotify(m_idc, knNotifySelectionChange);
}
if (fChange)
Invalidate();
}
#if 0
// Enumerate the list Items
ListItem *ListControl::EnumItemPtr(Enum *penm)
{
if (penm->m_pvNext == (void *)kEnmFirst)
penm->m_pvNext = (void *)m_pliFirst;
else if (penm->m_pvNext != NULL)
penm->m_pvNext = (void *)((ListItem *)penm->m_pvNext)->pliNext;
return (ListItem *)penm->m_pvNext;
}
#endif
int ListControl::GetSelectedItemIndex()
{
int i = 0;
for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext) {
if (pli->fSelected)
return i;
else
i++;
}
return -1;
}
// Return first selected item's data pointer
void *ListControl::GetSelectedItemData()
{
for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext)
if (pli->fSelected)
return pli->pvData;
return NULL;
}
// Return first selected item's ListItem pointer
ListItem *ListControl::GetSelectedItem()
{
for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext)
if (pli->fSelected)
return pli;
return NULL;
}
// set same item's data
void ListControl::SetSelectedItemData(void *pvData)
{
for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext)
if (pli->fSelected)
pli->pvData = pvData;
}
// Return first selected item's text
bool ListControl::GetSelectedItemText(char *psz, int cb)
{
for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext) {
if (pli->fSelected) {
strncpyz(psz, pli->szText, cb);
return true;
}
}
return false;
}
// Set first selected item's text
bool ListControl::SetSelectedItemText(const char *psz)
{
for (ListItem *pli = m_pliFirst; pli != NULL; pli = pli->pliNext) {
if (pli->fSelected) {
strncpyz(pli->szText, psz, sizeof(pli->szText));
Invalidate();
return true;
}
}
return false;
}
//
// SilkButtonControl
//
void SilkButtonControl::OnSelect(int nSelect)
{
switch (nSelect) {
case knSelMoveInside:
case knSelDownInside:
gsndm.PlaySfx(ksfxGuiButtonTap);
break;
}
Control::OnSelect(nSelect);
}
//
// SliderControl
//
#define kcxyTrack gcxyBorder
#define kcxySlider PcFromFc(4)
SliderControl::SliderControl()
{
m_nMin = m_nMax = m_nValue = 0;
}
void SliderControl::OnPaint(DibBitmap *pbm)
{
int cx = m_rc.Width();
int cy = m_rc.Height();
Rect rc;
m_pfrm->GetRect(&rc);
rc.left += m_rc.left;
rc.top += m_rc.top;
rc.right = rc.left + cx;
rc.bottom = rc.top + cy;
// Draw track
pbm->Fill(rc.left, rc.top + (cy / 2), cx, kcxyTrack, GetColor(kiclrButtonBorder));
// Draw slider
int x;
if (m_nMax - m_nMin <= 0)
x = 0;
else
x = (short)(((m_nValue - m_nMin) * (long)(cx - kcxySlider)) / (m_nMax - m_nMin));
pbm->Fill(rc.left + x, rc.top, kcxySlider, cy, GetColor(kiclrButtonFill));
}
void SliderControl::OnSelect(int nSelect)
{
if (nSelect == knSelDownInside)
gsndm.PlaySfx(ksfxGuiButtonTap);
}
void SliderControl::OnPenEvent(Event *pevt)
{
if (pevt->eType == penDownEvent || pevt->eType == penMoveEvent) {
Rect rcForm;
m_pfrm->GetRect(&rcForm);
int x = pevt->x + (kcxySlider / 2) - (rcForm.left + m_rc.left);
m_nValue = m_nMin + ((x * (m_nMax - m_nMin)) / m_rc.Width());
if (m_nValue < m_nMin)
m_nValue = m_nMin;
else if (m_nValue > m_nMax)
m_nValue = m_nMax;
Invalidate();
m_pceh->OnControlSelected(m_idc);
}
}
void SliderControl::SetRange(int nMin, int nMax)
{
Assert(nMax >= nMin);
m_nMin = nMin;
m_nMax = nMax;
if (m_nValue < m_nMin)
m_nValue = m_nMin;
if (m_nValue > m_nMax)
m_nValue = m_nMax;
Invalidate();
}
void SliderControl::SetValue(int n)
{
if (m_nValue < m_nMin)
m_nValue = m_nMin;
else if (m_nValue > m_nMax)
m_nValue = m_nMax;
else
m_nValue = n;
Invalidate();
}
int SliderControl::GetValue()
{
return m_nValue;
}
//
// Pip Meter control
//
TBitmap *PipMeterControl::s_ptbmPip;
bool PipMeterControl::InitClass()
{
s_ptbmPip = GetSharedTBitmap("pip.tbm");
return true;
}
void PipMeterControl::ExitClass()
{
}
PipMeterControl::PipMeterControl()
{
m_ptbmPip = NULL;
m_nValue = 0;
}
PipMeterControl::~PipMeterControl()
{
}
bool PipMeterControl::Init(Form *pfrm, word idc, int x, int y, int cx, int cy,
char *szPip)
{
if (!Control::Init(pfrm, idc, x, y, cx, cy))
return false;
return Init(szPip);
}
bool PipMeterControl::Init(char *szPip)
{
if (szPip != NULL)
m_ptbmPip = GetSharedTBitmap(szPip);
else
m_ptbmPip = s_ptbmPip;
return true;
}
bool PipMeterControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
{
// Base initialization
if (!Control::Init(pfrm, pini, pfind))
return false;
// idc (x y cx cy) [pip.tbm]
char szPip[kcbFilename];
int cArgs = pini->GetPropertyValue(pfind, "%*d (%*d %*d %*d %*d) %s", szPip);
return Init(cArgs == 1 ? szPip : NULL);
}
void PipMeterControl::OnPaint(DibBitmap *pbm)
{
Rect rcForm;
m_pfrm->GetRect(&rcForm);
Rect rcT = m_rc;
rcT.Offset(rcForm.left, rcForm.top);
Size sizPip;
s_ptbmPip->GetSize(&sizPip);
int cxPips = (rcT.Width() * m_nValue) / 100;
int cPips = (cxPips + (sizPip.cx / 2)) / sizPip.cx;
int x = rcT.left;
for (int i = 0; i < cPips; i++) {
m_ptbmPip->BltTo(pbm, x, rcT.top);
x += sizPip.cx;
}
}
void PipMeterControl::SetValue(int nValue)
{
// Don't cause unnecessary invalidation
if (nValue == m_nValue)
return;
// for the bar to fit in the rect, nValue must be less than or equal to 100
Assert(nValue <= 100, "value out of PipMeter range");
m_nValue = nValue;
Invalidate();
}
//
// Damage Meter control
//
TBitmap *DamageMeterControl::s_ptbmInfantry;
TBitmap *DamageMeterControl::s_ptbmVehicle;
TBitmap *DamageMeterControl::s_ptbmStructure;
bool DamageMeterControl::InitClass()
{
s_ptbmInfantry = GetSharedTBitmap("damage_infantry.tbm");
s_ptbmVehicle = GetSharedTBitmap("damage_vehicle.tbm");
s_ptbmStructure = GetSharedTBitmap("damage_structure.tbm");
return true;
}
void DamageMeterControl::ExitClass()
{
}
DamageMeterControl::DamageMeterControl()
{
m_puntc = NULL;
}
DamageMeterControl::~DamageMeterControl()
{
}
#define kfxDamagePerSecMax itofx(16)
void DrawPips(DibBitmap *pbm, TBitmap *ptbm, int *px, int y, int nDamage, int ctFiringRate) secControl;
void DrawPips(DibBitmap *pbm, TBitmap *ptbm, int *px, int y, int nDamage, int ctFiringRate)
{
if (nDamage == 0)
return;
fix fxDamagePerSec = (fix)mulfx(itofx(nDamage), divfx(itofx(100), itofx(ctFiringRate)));
int cPips = fxtoi(addfx((fix)divfx(8 * (long)fxDamagePerSec, kfxDamagePerSecMax), itofx(1) / itofx(2)));
if (cPips > 8)
cPips = 8;
Size sizPip;
ptbm->GetSize(&sizPip);
for (int i = 0; i < cPips; i++) {
ptbm->BltTo(pbm, *px, y);
*px += sizPip.cx + 1;
}
}
void DamageMeterControl::OnPaint(DibBitmap *pbm)
{
if (m_puntc == NULL)
return;
Rect rcForm;
m_pfrm->GetRect(&rcForm);
Rect rcT = m_rc;
rcT.Offset(rcForm.left, rcForm.top);
int x = rcT.left;
DrawPips(pbm, s_ptbmInfantry, &x, rcT.top, m_puntc->nInfantryDamage, m_puntc->ctFiringRate);
DrawPips(pbm, s_ptbmVehicle, &x, rcT.top, m_puntc->nVehicleDamage, m_puntc->ctFiringRate);
DrawPips(pbm, s_ptbmStructure, &x, rcT.top, m_puntc->nStructureDamage, m_puntc->ctFiringRate);
}
void DamageMeterControl::SetUnitConsts(UnitConsts *puntc)
{
// Don't cause unnecessary invalidation
if (m_puntc == puntc)
return;
m_puntc = puntc;
Invalidate();
}
RadioButtonBarControl::RadioButtonBarControl()
{
m_isel = 0;
m_ifnt = kifntButton;
m_cLabels = 0;
memset(m_apszLabels, 0, sizeof(m_apszLabels));
}
RadioButtonBarControl::~RadioButtonBarControl()
{
for (int i = 0; i < m_cLabels; i++) {
delete[] m_apszLabels[i];
}
}
bool RadioButtonBarControl::Init(Form *pfrm, IniReader *pini, FindProp *pfind)
{
// Base initialization
if (!Control::Init(pfrm, pini, pfind)) {
return false;
}
// idc (x y cx cy) "label" ifnt [isel]
char szLabel[256];
int ifnt;
int isel;
int cArgs = pini->GetPropertyValue(pfind,
"%*d (%*d %*d %*d %*d) \"%s\" %d %d",
szLabel, &ifnt, &isel);
if (cArgs == 2) {
return Init(szLabel, ifnt, 0);
}
if (cArgs == 3) {
return Init(szLabel, ifnt, isel);
}
return false;
}
bool RadioButtonBarControl::Init(const char *pszLabel, int ifnt, int isel)
{
// Parse pszLabel into string pieces
const char *pszNext = pszLabel;
while (true) {
const char *pszT = strchr(pszNext, '|');
if (pszT == NULL) {
AddLabel(pszNext, (int)strlen(pszNext));
break;
}
AddLabel(pszNext, (int)(pszT - pszNext));
pszNext = pszT + 1;
}
m_isel = 0;
if (isel >= 0 && isel < m_cLabels) {
m_isel = isel;
}
m_ifnt = ifnt;
// Figure out positioning
// If cx == 0, then center horizontally
if (m_cLabels == 0) {
return true;
}
Rect rcInner, rcOuter;
GetCellRects(0, &rcInner, &rcOuter);
for (int i = 1; i < m_cLabels; i++) {
Rect rcInnerT, rcOuterT;
GetCellRects(i, &rcInnerT, &rcOuterT);
rcOuter.Union(&rcOuterT);
}
if (m_rc.Width() == 0) {
Rect rcForm;
m_pfrm->GetRect(&rcForm);
m_rc.left = (rcForm.Width() - rcOuter.Width()) / 2;
// m_rc.top stays the same
m_rc.right = m_rc.left + rcOuter.Width();
m_rc.bottom = m_rc.top + rcOuter.Height();
}
return true;
}
void RadioButtonBarControl::AddLabel(const char *psz, int cch)
{
if (m_cLabels >= ARRAYSIZE(m_apszLabels)) {
return;
}
char *pszT = new char[cch + 1];
if (pszT == NULL) {
return;
}
strncpyz(pszT, psz, cch + 1);
m_apszLabels[m_cLabels] = pszT;
m_cLabels++;
}
void RadioButtonBarControl::GetCellRects(int icell, Rect *prcInner,
Rect *prcOuter)
{
// First step through the cells up to icell, since a starting x has
// to be calculated.
int xStart = m_rc.left;
for (int i = 0; i < icell; i++) {
Size sizT;
GetOuterCellSize(i, &sizT);
xStart += sizT.cx - gcxyBorder;
}
Size sizT;
GetOuterCellSize(icell, &sizT);
prcOuter->Set(xStart, m_rc.top, xStart + sizT.cx, m_rc.top + sizT.cy);
*prcInner = *prcOuter;
prcInner->Inflate(-gcxyBorder, -gcxyBorder);
}
void RadioButtonBarControl::GetOuterCellSize(int icell, Size *psiz)
{
#define kcxLabelGap (gcxyBorder * 4)
#define kcyLabelGap (gcxyBorder * 1)
// Pad around start and end, and border
Font *pfnt = gapfnt[m_ifnt];
psiz->cx = pfnt->GetTextExtent(m_apszLabels[icell]);
psiz->cx += kcxLabelGap * 2 + gcxyBorder * 2;
psiz->cy = pfnt->GetHeight();
psiz->cy += kcyLabelGap * 2 + gcxyBorder * 2;
}
void RadioButtonBarControl::OnPaint(DibBitmap *pbm)
{
Rect rcForm;
m_pfrm->GetRect(&rcForm);
Font *pfnt = gapfnt[m_ifnt];
for (int icell = 0; icell < m_cLabels; icell++) {
Rect rcInner, rcOuter;
GetCellRects(icell, &rcInner, &rcOuter);
int xText = rcForm.left + rcInner.left + kcxLabelGap;
int yText = rcForm.top + rcInner.top + kcyLabelGap;
if (icell < m_cLabels - 1) {
int cyDescender = gcxyBorder * 2; // hack
pbm->Fill(rcInner.right + rcForm.left, yText,
gcxyBorder, pfnt->GetHeight() - cyDescender,
GetColor(kiclrWhite));
}
if (icell == m_isel) {
pbm->Fill(xText, yText + pfnt->GetHeight(),
pfnt->GetTextExtent(m_apszLabels[icell]), 1,
GetColor(kiclrWhite));
}
pfnt->DrawText(pbm, (char *)m_apszLabels[icell], xText, yText);
}
}
void RadioButtonBarControl::OnPenEvent(Event *pevt)
{
// Change selection on down
if (pevt->eType != penDownEvent) {
return;
}
// Figure out which cell the down was in
int isel = -1;
Rect rcForm;
m_pfrm->GetRect(&rcForm);
for (int i = 0; i < m_cLabels; i++) {
Rect rcInner, rcOuter;
GetCellRects(i, &rcInner, &rcOuter);
int x = pevt->x;
int y = rcForm.top + rcInner.top;
if (rcOuter.PtIn(x, y)) {
isel = i;
break;
}
}
// Off the ends?
Rect rcInner, rcOuter;
GetCellRects(0, &rcInner, &rcOuter);
if (pevt->x < rcOuter.left + rcForm.left) {
isel = 0;
}
if (m_cLabels > 0) {
GetCellRects(m_cLabels - 1, &rcInner, &rcOuter);
if (pevt->x >= rcOuter.right + rcForm.left) {
isel = m_cLabels - 1;
}
}
// Any change?
if (isel == m_isel) {
return;
}
// Notify of change, invalidate to redraw
m_isel = isel;
m_pceh->OnControlNotify(m_idc, knNotifySelectionChange);
gsndm.PlaySfx(ksfxGuiButtonTap);
Invalidate();
}
int RadioButtonBarControl::GetSelectionIndex()
{
return m_isel;
}
void RadioButtonBarControl::SetSelectionIndex(int isel)
{
if (isel < 0 || isel >= m_cLabels) {
return;
}
if (isel == m_isel) {
return;
}
m_isel = isel;
Invalidate();
}
} // namespace wi