2025-04-27 07:49:33 -04:00

1237 lines
35 KiB
C++

#include "stdafx.h"
#include "Ctrl.h"
#include "SmEditLine.h"
#if ENABLE_MSGTABLE_API
/***************************************************************************\
*****************************************************************************
*
* class SmEditLine
*
*****************************************************************************
\***************************************************************************/
HFONT SmEditLine::s_hfntDefault = NULL;
//------------------------------------------------------------------------------
SmEditLine::SmEditLine()
{
if (s_hfntDefault == NULL) {
s_hfntDefault = UtilBuildFont(L"Tahoma", 85, FS_NORMAL);
}
m_szBuffer[0] = '\0';
m_idxchCaret = 0;
m_cchSize = 0;
m_hfnt = s_hfntDefault;
m_hpenCaret = NULL;
m_fFocus = FALSE;
m_pThread = GetThread();
RebuildCaret();
}
//------------------------------------------------------------------------------
SmEditLine::~SmEditLine()
{
::DeleteHandle(m_hactBlink);
::DeleteObject(m_hpenCaret);
}
//------------------------------------------------------------------------------
HRESULT
SmEditLine::PostBuild(DUser::Gadget::ConstructInfo * pci)
{
UNREFERENCED_PARAMETER(pci);
GetStub()->SetFilter( GMFI_PAINT | GMFI_INPUTKEYBOARD | GMFI_INPUTMOUSE | GMFI_CHANGESTATE, GMFI_ALL);
GetStub()->SetStyle( GS_KEYBOARDFOCUS | GS_ZEROORIGIN | GS_MOUSEFOCUS,
GS_KEYBOARDFOCUS | GS_ZEROORIGIN | GS_MOUSEFOCUS);
SetTextColor(RGB(0, 0, 0));
return S_OK;
}
//------------------------------------------------------------------------------
void CALLBACK
SmEditLine::BlinkActionProc(GMA_ACTIONINFO * pmai)
{
SmEditLine * pThis = (SmEditLine *) pmai->pvData;
if (pmai->fFinished) {
pThis->m_hactBlink = NULL;
return;
}
pThis->m_fCaretShown = !pThis->m_fCaretShown;
pThis->GetStub()->Invalidate();
}
//------------------------------------------------------------------------------
HRESULT
SmEditLine::ApiOnEvent(EventMsg * pmsg)
{
if (GET_EVENT_DEST(pmsg) == GMF_DIRECT) {
switch (pmsg->nMsg)
{
case GM_PAINT:
{
GMSG_PAINT * pmsgPaint = (GMSG_PAINT *) pmsg;
if (pmsgPaint->nCmd == GPAINT_RENDER) {
switch (pmsgPaint->nSurfaceType)
{
case GSURFACE_HDC:
{
GMSG_PAINTRENDERI * pmsgR = (GMSG_PAINTRENDERI *) pmsgPaint;
OnDraw(pmsgR->hdc, pmsgR);
}
break;
default:
Trace("WARNING: Unknown surface type\n");
}
return DU_S_PARTIAL;
}
}
break;
case GM_INPUT:
{
GMSG_INPUT * pmsgI = (GMSG_INPUT *) pmsg;
switch (pmsgI->nDevice)
{
case GINPUT_KEYBOARD:
{
GMSG_KEYBOARD * pmsgK = (GMSG_KEYBOARD *) pmsgI;
switch (pmsgK->nCode)
{
case GKEY_DOWN:
{
//
// GKEY_DOWN is the raw key-press event that contains any key that
// is pressed.
//
WCHAR chKey = pmsgK->ch;
switch (chKey)
{
case VK_LEFT:
if (m_idxchCaret > 0) {
m_idxchCaret--;
UpdateCaretPos();
}
break;
case VK_RIGHT:
if (m_idxchCaret < m_cchSize) {
m_idxchCaret++;
UpdateCaretPos();
}
break;
case VK_BACK:
DeleteChars(m_idxchCaret - 1, 1);
UpdateCaretPos();
break;
case VK_DELETE:
DeleteChars(m_idxchCaret, 1);
UpdateCaretPos();
break;
case VK_HOME:
m_idxchCaret = 0;
UpdateCaretPos();
break;
case VK_END:
m_idxchCaret = m_cchSize;
UpdateCaretPos();
break;
case VK_RETURN:
InsertChar('\r\n');
break;
}
}
break;
case GKEY_CHAR:
{
//
// GKEY_CHAR is a "processed" key-press event that gets
// generated from other messages. This will take into account
// the caps-lock state, etc., so it is useful to get
// characters.
//
WCHAR chKey = pmsgK->ch;
if (chKey >= ' ') {
InsertChar(chKey);
}
}
break;
}
}
break; // GINPUT_KEYBOARD
case GINPUT_MOUSE:
{
GMSG_MOUSE * pmsgM = (GMSG_MOUSE *) pmsg;
switch (pmsgM->nCode)
{
case GMOUSE_DOWN:
case GMOUSE_UP:
if (pmsgM->bButton == GBUTTON_LEFT) {
POINT ptOffsetPxl = pmsgM->ptClientPxl;
int idxchNew = ComputeMouseChar(ptOffsetPxl);
if (idxchNew != m_idxchCaret) {
m_idxchCaret = idxchNew;
UpdateCaretPos();
}
return DU_S_COMPLETE;
}
}
}
break; // GINPUT_MOUSE
}
}
break; // GM_INPUT
case GM_CHANGESTATE:
{
GMSG_CHANGESTATE * pmsgS = (GMSG_CHANGESTATE *) pmsg;
if (pmsgS->nCode == GSTATE_KEYBOARDFOCUS) {
UpdateFocus(pmsgS->nCmd);
}
}
break;
case GM_QUERY:
{
GMSG_QUERY * pmsgQ = (GMSG_QUERY *) pmsg;
switch (pmsgQ->nCode)
{
case GQUERY_DESCRIPTION:
{
GMSG_QUERYDESC * pmsgQD = (GMSG_QUERYDESC *) pmsg;
CopyString(pmsgQD->szName, m_szBuffer, _countof(pmsgQD->szName));
CopyString(pmsgQD->szType, L"SmEditLine", _countof(pmsgQD->szType));
return DU_S_COMPLETE;
}
#if 0
case GQUERY_DROPTARGET:
{
GMSG_QUERYDROPTARGET * pmsgQDT = (GMSG_QUERYDROPTARGET *) pmsg;
pmsgQDT->hgadDrop = GetHandle();
pmsgQDT->pdt = static_cast<IDropTarget *> (this);
pmsgQDT->pdt->AddRef();
return DU_S_COMPLETE;
}
#endif
}
}
break;
}
}
return SVisual::ApiOnEvent(pmsg);
}
//------------------------------------------------------------------------------
void
SmEditLine::UpdateFocus(UINT nCmd)
{
m_fFocus = (nCmd == GSC_SET);
if (m_fFocus) {
//
// This control is getting the focus.
//
//
// Create an action that can be used to blink the edit control. Each thread
// needs to do this since timers are per-thread and Gadget functions need
// to execute in the correct Context.
//
GMA_ACTION gma;
ZeroMemory(&gma, sizeof(gma));
gma.cbSize = sizeof(gma);
gma.cRepeat = (UINT) -1;
gma.flDelay = 0.0f;
gma.flPeriod = GetCaretBlinkTime() / 1000.0f;
gma.flDuration = 0.0f;
gma.pfnProc = BlinkActionProc;
gma.pvData = this;
m_hactBlink = CreateAction(&gma);
} else {
//
// Loosing focus, so change to NULL if currently point
// to this Edit control.
//
::DeleteHandle(m_hactBlink);
m_hactBlink = NULL;
}
GetStub()->Invalidate();
}
//------------------------------------------------------------------------------
void
SmEditLine::InsertChar(WCHAR chKey)
{
if (m_cchSize < m_cchMax) {
//
// Move the text after current position down one character
//
int cchMove = m_cchSize - m_idxchCaret + 1;
if (cchMove > 0) {
MoveMemory(&m_szBuffer[m_idxchCaret + 1], &m_szBuffer[m_idxchCaret], cchMove * sizeof(WCHAR));
}
//
// Insert the new character
//
m_szBuffer[m_idxchCaret] = chKey;
//
// Check if need to re-NULL-terminate
//
if (m_idxchCaret > m_cchSize) {
m_szBuffer[m_idxchCaret] = '\0';
}
//
// Update position
//
m_idxchCaret++;
m_cchSize++;
UpdateCaretPos();
}
}
//------------------------------------------------------------------------------
void
SmEditLine::DeleteChars(int idxchStart, int cchDel)
{
if ((idxchStart >= m_cchSize) || (idxchStart < 0)) {
// Nothing to delete
return;
}
// Make sure deleting within range
cchDel = min(cchDel, m_cchSize - idxchStart);
int idxchDest = idxchStart;
int idxchSrc = idxchStart + cchDel;
int chMove = m_cchSize - idxchSrc;
MoveMemory(&m_szBuffer[idxchDest], &m_szBuffer[idxchSrc], chMove * sizeof(WCHAR));
m_cchSize -= cchDel;
if (m_idxchCaret > idxchStart) {
m_idxchCaret = idxchStart;
}
}
//------------------------------------------------------------------------------
void
SmEditLine::OnDraw(HDC hdc, GMSG_PAINTRENDERI * pmsgR)
{
RECT rcDraw = *pmsgR->prcGadgetPxl;
if (m_cchSize != 0) {
HFONT hfntOld = NULL;
if (m_hfnt != NULL) {
hfntOld = (HFONT) ::SelectObject(hdc, m_hfnt);
}
COLORREF crOld = ::SetTextColor(hdc, m_crText);
int nOldMode = ::SetBkMode(hdc, TRANSPARENT);
//
// NOTE: We have currently disabled calling DrawText because the
// performance is TERRIBLE! This should be much better using Text+.
//
#if 1
OS()->ExtTextOut(hdc, rcDraw.left, rcDraw.top,
ETO_CLIPPED, &rcDraw, m_szBuffer, m_cchSize, NULL);
#else
OS()->DrawText(hdc, m_szBuffer, m_cchSize, &rcDraw,
DT_TOP | DT_LEFT | DT_WORDBREAK | DT_NOPREFIX);
#endif
::SetBkMode(hdc, nOldMode);
::SetTextColor(hdc, crOld);
if (m_hfnt != NULL) {
::SelectObject(hdc, hfntOld);
}
}
if (m_fFocus && m_fCaretShown) {
HPEN hpenOld = (HPEN) SelectObject(hdc, m_hpenCaret);
POINT rgpt[2];
rgpt[0].x = m_ptCaretPxl.x;
rgpt[0].y = m_ptCaretPxl.y + m_yCaretOffsetPxl;
rgpt[1].x = m_ptCaretPxl.x;
rgpt[1].y = m_ptCaretPxl.y + m_cyCaretPxl + m_yCaretOffsetPxl;
Polyline(hdc, rgpt, _countof(rgpt));
SelectObject(hdc, hpenOld);
}
}
//------------------------------------------------------------------------------
HRESULT
SmEditLine::ApiGetFont(EditLineGadget::GetFontMsg * pmsg)
{
pmsg->hfnt = m_hfnt;
return S_OK;
}
//------------------------------------------------------------------------------
HRESULT
SmEditLine::ApiSetFont(EditLineGadget::SetFontMsg * pmsg)
{
m_hfnt = pmsg->hfnt;
RebuildCaret();
UpdateCaretPos();
return S_OK;
}
//------------------------------------------------------------------------------
HRESULT
SmEditLine::ApiGetText(EditLineGadget::GetTextMsg * pmsg)
{
pmsg->pszText = m_szBuffer;
return S_OK;
}
//------------------------------------------------------------------------------
HRESULT
SmEditLine::ApiSetText(EditLineGadget::SetTextMsg * pmsg)
{
wcsncpy(m_szBuffer, pmsg->pszText, m_cchMax);
m_szBuffer[m_cchMax] = '\0';
m_cchSize = lstrlenW(m_szBuffer);
if (m_idxchCaret > m_cchSize) {
m_idxchCaret = m_cchSize;
}
UpdateCaretPos();
return S_OK;
}
//------------------------------------------------------------------------------
HRESULT
SmEditLine::ApiGetTextColor(EditLineGadget::GetTextColorMsg * pmsg)
{
pmsg->crText = m_crText;
return S_OK;
}
//------------------------------------------------------------------------------
HRESULT
SmEditLine::ApiSetTextColor(EditLineGadget::SetTextColorMsg * pmsg)
{
SetTextColor(pmsg->crText);
return S_OK;
}
//------------------------------------------------------------------------------
void
SmEditLine::SetTextColor(COLORREF crText)
{
m_crText = crText;
if (m_hpenCaret != NULL) {
DeleteObject(m_hpenCaret);
}
m_hpenCaret = CreatePen(PS_SOLID, 1, m_crText);
}
//------------------------------------------------------------------------------
void
SmEditLine::RebuildCaret()
{
HDC hdc = GetGdiCache()->GetTempDC();
TEXTMETRIC tm;
HFONT hfntOld = (HFONT) SelectObject(hdc, m_hfnt);
GetTextMetrics(hdc, &tm);
m_cyLinePxl = tm.tmHeight;
m_cyCaretPxl = tm.tmHeight;
m_yCaretOffsetPxl = tm.tmInternalLeading;
SelectObject(hdc, hfntOld);
GetGdiCache()->ReleaseTempDC(hdc);
}
//------------------------------------------------------------------------------
void
SmEditLine::UpdateCaretPos()
{
HDC hdc = GetGdiCache()->GetTempDC();
HFONT hfntOld = (HFONT) SelectObject(hdc, m_hfnt);
//
// TODO: This wrapping calculation is PURE EVIL and needs to be properly
// written using Text+.
//
SIZE sizeTextPxl;
if (OS()->GetTextExtentPoint32(hdc, m_szBuffer, m_idxchCaret, &sizeTextPxl)) {
int nLine = 0;
int idxStart = 0;
SIZE sizeGadgetPxl;
GetStub()->GetSize(&sizeGadgetPxl);
while(sizeTextPxl.cx > sizeGadgetPxl.cx) {
//
// Need to word wrap.
//
int idxMax;
if (OS()->GetTextExtentExPoint(hdc, &m_szBuffer[idxStart], m_idxchCaret, sizeGadgetPxl.cx, &idxMax, NULL, &sizeTextPxl)) {
idxMax += idxStart;
for (int idx = idxMax; idx >= idxStart; idx--) {
if (m_szBuffer[idx] == ' ') {
break;
}
}
if (idx == (idxStart - 1)) {
//
// There was no space in the word to break on, so just chop at the end
//
idx = idxMax;
}
nLine++;
idxStart = idx + 1;
OS()->GetTextExtentPoint32(hdc, &m_szBuffer[idxStart], m_idxchCaret - idxStart, &sizeTextPxl);
}
}
m_ptCaretPxl.x = sizeTextPxl.cx;
m_ptCaretPxl.y = nLine * m_cyLinePxl;
if (m_ptCaretPxl.x > 0) {
m_ptCaretPxl.x++;
}
}
SelectObject(hdc, hfntOld);
GetGdiCache()->ReleaseTempDC(hdc);
GetStub()->Invalidate();
}
//------------------------------------------------------------------------------
int
SmEditLine::ComputeMouseChar(POINT ptOffsetPxl)
{
//
// Check boundary conditions
//
if (ptOffsetPxl.x < 0) {
return 0;
}
SIZE sizePxl;
GetStub()->GetSize(&sizePxl);
if (ptOffsetPxl.x > sizePxl.cx) {
return m_cchSize;
}
//
// This implementation is very poor, but we don't care for now because this
// is a sample control and is only for single-line.
//
HDC hdc = GetGdiCache()->GetTempDC();
HFONT hfntOld = (HFONT) SelectObject(hdc, m_hfnt);
int ichFound = m_cchSize;
SIZE sizeTextPxl;
for (int cch = 0; cch < m_cchSize; cch++) {
OS()->GetTextExtentPoint32(hdc, m_szBuffer, cch, &sizeTextPxl);
if (sizeTextPxl.cx > ptOffsetPxl.x) {
// We are greater, so return previous character
AssertMsg(cch > 0, "Should have at least one character");
ichFound = cch - 1;
goto Cleanup;
}
}
Cleanup:
SelectObject(hdc, hfntOld);
GetGdiCache()->ReleaseTempDC(hdc);
return ichFound;
}
#if 0
//------------------------------------------------------------------------------
STDMETHODIMP
SmEditLine::DragEnter(IDataObject * pdoSrc, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
{
UNREFERENCED_PARAMETER(grfKeyState);
UNREFERENCED_PARAMETER(pt);
if (pdoSrc == NULL) {
return E_INVALIDARG;
}
m_dwLastDropEffect = DROPEFFECT_NONE;
if (HasText(pdoSrc)) {
m_dwLastDropEffect = DROPEFFECT_COPY;
}
*pdwEffect = m_dwLastDropEffect;
GetStub()->Invalidate();
return S_OK;
}
//------------------------------------------------------------------------------
STDMETHODIMP
SmEditLine::DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
{
UNREFERENCED_PARAMETER(grfKeyState);
UNREFERENCED_PARAMETER(pt);
*pdwEffect = m_dwLastDropEffect;
return S_OK;
}
//------------------------------------------------------------------------------
STDMETHODIMP
SmEditLine::DragLeave()
{
GetStub()->Invalidate();
return S_OK;
}
//------------------------------------------------------------------------------
STDMETHODIMP
SmEditLine::Drop(IDataObject * pdoSrc, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
{
UNREFERENCED_PARAMETER(grfKeyState);
UNREFERENCED_PARAMETER(pt);
Assert(pdoSrc != NULL);
FORMATETC etc;
etc.cfFormat = CF_TEXT;
etc.ptd = NULL;
etc.dwAspect = DVASPECT_CONTENT;
etc.lindex = -1;
etc.tymed = TYMED_HGLOBAL;
STGMEDIUM stg;
stg.tymed = TYMED_HGLOBAL;
stg.hGlobal = NULL;
*pdwEffect = DROPEFFECT_NONE;
HRESULT hr = pdoSrc->GetData(&etc, &stg);
if (hr == S_OK) {
USES_CONVERSION;
void * pvData = GlobalLock(stg.hGlobal);
if (pvData != NULL) {
SetText(A2W((LPCSTR) pvData));
GlobalUnlock(stg.hGlobal);
*pdwEffect = DROPEFFECT_COPY;
}
GetComManager()->ReleaseStgMedium(&stg);
}
return S_OK;
}
//------------------------------------------------------------------------------
BOOL
SmEditLine::HasText(IDataObject * pdoSrc)
{
Assert(pdoSrc != NULL);
FORMATETC etc;
etc.cfFormat = CF_TEXT;
etc.ptd = NULL;
etc.dwAspect = DVASPECT_CONTENT;
etc.lindex = -1;
etc.tymed = TYMED_HGLOBAL;
HRESULT hr = pdoSrc->QueryGetData(&etc);
return hr == S_OK; // S_OK means the data format is supported
}
#endif
/***************************************************************************\
*****************************************************************************
*
* class SmEditLineF
*
*****************************************************************************
\***************************************************************************/
//------------------------------------------------------------------------------
SmEditLineF::SmEditLineF()
{
m_szBuffer[0] = '\0';
m_idxchCaret = 0;
m_cchSize = 0;
m_pgpfnt = new Gdiplus::Font(L"Tahoma", 12.0f, Gdiplus::FontStyleRegular, Gdiplus::UnitPoint);
m_fOwnFont = TRUE;
m_fFocus = FALSE;
m_pThread = GetThread();
RebuildCaret();
}
//------------------------------------------------------------------------------
SmEditLineF::~SmEditLineF()
{
::DeleteHandle(m_hactBlink);
if (m_fOwnFont) {
delete m_pgpfnt;
}
}
//------------------------------------------------------------------------------
HRESULT
SmEditLineF::PostBuild(DUser::Gadget::ConstructInfo * pci)
{
UNREFERENCED_PARAMETER(pci);
GetStub()->SetFilter( GMFI_PAINT | GMFI_INPUTKEYBOARD | GMFI_INPUTMOUSE | GMFI_CHANGESTATE, GMFI_ALL);
GetStub()->SetStyle( GS_KEYBOARDFOCUS | GS_ZEROORIGIN | GS_MOUSEFOCUS,
GS_KEYBOARDFOCUS | GS_ZEROORIGIN | GS_MOUSEFOCUS);
m_pgpbrText = GetStdColorBrushF(SC_Black);
return S_OK;
}
//------------------------------------------------------------------------------
void CALLBACK
SmEditLineF::BlinkActionProc(GMA_ACTIONINFO * pmai)
{
SmEditLineF * pThis = (SmEditLineF *) pmai->pvData;
if (pmai->fFinished) {
pThis->m_hactBlink = NULL;
return;
}
pThis->m_fCaretShown = !pThis->m_fCaretShown;
pThis->GetStub()->Invalidate();
}
//------------------------------------------------------------------------------
HRESULT
SmEditLineF::ApiOnEvent(EventMsg * pmsg)
{
if (GET_EVENT_DEST(pmsg) == GMF_DIRECT) {
switch (pmsg->nMsg)
{
case GM_PAINT:
{
GMSG_PAINT * pmsgPaint = (GMSG_PAINT *) pmsg;
if (pmsgPaint->nCmd == GPAINT_RENDER) {
switch (pmsgPaint->nSurfaceType)
{
case GSURFACE_GPGRAPHICS:
{
GMSG_PAINTRENDERF * pmsgR = (GMSG_PAINTRENDERF *) pmsgPaint;
OnDraw(pmsgR->pgpgr, pmsgR);
}
break;
default:
Trace("WARNING: Unknown surface type\n");
}
return DU_S_PARTIAL;
}
}
break;
case GM_INPUT:
{
GMSG_INPUT * pmsgI = (GMSG_INPUT *) pmsg;
switch (pmsgI->nDevice)
{
case GINPUT_KEYBOARD:
{
GMSG_KEYBOARD * pmsgK = (GMSG_KEYBOARD *) pmsgI;
switch (pmsgK->nCode)
{
case GKEY_DOWN:
{
//
// GKEY_DOWN is the raw key-press event that contains any key that
// is pressed.
//
WCHAR chKey = pmsgK->ch;
switch (chKey)
{
case VK_LEFT:
if (m_idxchCaret > 0) {
m_idxchCaret--;
UpdateCaretPos();
}
break;
case VK_RIGHT:
if (m_idxchCaret < m_cchSize) {
m_idxchCaret++;
UpdateCaretPos();
}
break;
case VK_BACK:
DeleteChars(m_idxchCaret - 1, 1);
UpdateCaretPos();
break;
case VK_DELETE:
DeleteChars(m_idxchCaret, 1);
UpdateCaretPos();
break;
case VK_HOME:
m_idxchCaret = 0;
UpdateCaretPos();
break;
case VK_END:
m_idxchCaret = m_cchSize;
UpdateCaretPos();
break;
case VK_RETURN:
InsertChar('\r\n');
break;
}
}
break;
case GKEY_CHAR:
{
//
// GKEY_CHAR is a "processed" key-press event that gets
// generated from other messages. This will take into account
// the caps-lock state, etc., so it is useful to get
// characters.
//
WCHAR chKey = pmsgK->ch;
if (chKey >= ' ') {
InsertChar(chKey);
}
}
break;
}
}
break; // GINPUT_KEYBOARD
case GINPUT_MOUSE:
{
GMSG_MOUSE * pmsgM = (GMSG_MOUSE *) pmsg;
switch (pmsgM->nCode)
{
case GMOUSE_DOWN:
case GMOUSE_UP:
if (pmsgM->bButton == GBUTTON_LEFT) {
POINT ptOffsetPxl = pmsgM->ptClientPxl;
int idxchNew = ComputeMouseChar(ptOffsetPxl);
if (idxchNew != m_idxchCaret) {
m_idxchCaret = idxchNew;
UpdateCaretPos();
}
return DU_S_COMPLETE;
}
}
}
break; // GINPUT_MOUSE
}
}
break; // GM_INPUT
case GM_CHANGESTATE:
{
GMSG_CHANGESTATE * pmsgS = (GMSG_CHANGESTATE *) pmsg;
if (pmsgS->nCode == GSTATE_KEYBOARDFOCUS) {
UpdateFocus(pmsgS->nCmd);
}
}
break;
case GM_QUERY:
{
GMSG_QUERY * pmsgQ = (GMSG_QUERY *) pmsg;
switch (pmsgQ->nCode)
{
case GQUERY_DESCRIPTION:
{
GMSG_QUERYDESC * pmsgQD = (GMSG_QUERYDESC *) pmsg;
CopyString(pmsgQD->szName, m_szBuffer, _countof(pmsgQD->szName));
CopyString(pmsgQD->szType, L"SmEditLineF", _countof(pmsgQD->szType));
return DU_S_COMPLETE;
}
}
}
break;
}
}
return SVisual::ApiOnEvent(pmsg);
}
//------------------------------------------------------------------------------
void
SmEditLineF::UpdateFocus(UINT nCmd)
{
m_fFocus = (nCmd == GSC_SET);
if (m_fFocus) {
//
// This control is getting the focus.
//
//
// Create an action that can be used to blink the edit control. Each thread
// needs to do this since timers are per-thread and Gadget functions need
// to execute in the correct Context.
//
GMA_ACTION gma;
ZeroMemory(&gma, sizeof(gma));
gma.cbSize = sizeof(gma);
gma.cRepeat = (UINT) -1;
gma.flDelay = 0.0f;
gma.flPeriod = GetCaretBlinkTime() / 1000.0f;
gma.flDuration = 0.0f;
gma.pfnProc = BlinkActionProc;
gma.pvData = this;
m_hactBlink = CreateAction(&gma);
} else {
//
// Loosing focus, so change to NULL if currently point
// to this Edit control.
//
::DeleteHandle(m_hactBlink);
m_hactBlink = NULL;
}
GetStub()->Invalidate();
}
//------------------------------------------------------------------------------
void
SmEditLineF::InsertChar(WCHAR chKey)
{
if (m_cchSize < m_cchMax) {
//
// Move the text after current position down one character
//
int cchMove = m_cchSize - m_idxchCaret + 1;
if (cchMove > 0) {
MoveMemory(&m_szBuffer[m_idxchCaret + 1], &m_szBuffer[m_idxchCaret], cchMove * sizeof(WCHAR));
}
//
// Insert the new character
//
m_szBuffer[m_idxchCaret] = chKey;
//
// Check if need to re-NULL-terminate
//
if (m_idxchCaret > m_cchSize) {
m_szBuffer[m_idxchCaret] = '\0';
}
//
// Update position
//
m_idxchCaret++;
m_cchSize++;
UpdateCaretPos();
}
}
//------------------------------------------------------------------------------
void
SmEditLineF::DeleteChars(int idxchStart, int cchDel)
{
if ((idxchStart >= m_cchSize) || (idxchStart < 0)) {
// Nothing to delete
return;
}
// Make sure deleting within range
cchDel = min(cchDel, m_cchSize - idxchStart);
int idxchDest = idxchStart;
int idxchSrc = idxchStart + cchDel;
int chMove = m_cchSize - idxchSrc;
MoveMemory(&m_szBuffer[idxchDest], &m_szBuffer[idxchSrc], chMove * sizeof(WCHAR));
m_cchSize -= cchDel;
if (m_idxchCaret > idxchStart) {
m_idxchCaret = idxchStart;
}
}
//------------------------------------------------------------------------------
void
SmEditLineF::OnDraw(Gdiplus::Graphics * pgpgr, GMSG_PAINTRENDERF * pmsgR)
{
pgpgr->DrawString(m_szBuffer, m_cchSize, m_pgpfnt, *pmsgR->prcGadgetPxl, 0, m_pgpbrText);
if (m_fFocus && m_fCaretShown) {
Gdiplus::PointF rgpt[2];
rgpt[0].X = m_ptCaretPxl.X;
rgpt[0].Y = m_ptCaretPxl.Y + m_yCaretOffsetPxl;
rgpt[1].X = m_ptCaretPxl.X;
rgpt[1].Y = m_ptCaretPxl.Y + m_cyCaretPxl + m_yCaretOffsetPxl;
pgpgr->DrawLine(GetStdColorPenF(SC_MidnightBlue), rgpt[0], rgpt[1]);
}
}
//------------------------------------------------------------------------------
HRESULT
SmEditLineF::ApiGetFont(EditLineFGadget::GetFontMsg * pmsg)
{
pmsg->pgpfnt = m_pgpfnt;
return S_OK;
}
//------------------------------------------------------------------------------
HRESULT
SmEditLineF::ApiSetFont(EditLineFGadget::SetFontMsg * pmsg)
{
if (m_fOwnFont) {
delete m_pgpfnt;
}
m_pgpfnt = pmsg->pgpfnt;
m_fOwnFont = pmsg->fPassOwnership;
RebuildCaret();
UpdateCaretPos();
return S_OK;
}
//------------------------------------------------------------------------------
HRESULT
SmEditLineF::ApiGetText(EditLineFGadget::GetTextMsg * pmsg)
{
pmsg->pszText = m_szBuffer;
return S_OK;
}
//------------------------------------------------------------------------------
HRESULT
SmEditLineF::ApiSetText(EditLineFGadget::SetTextMsg * pmsg)
{
wcsncpy(m_szBuffer, pmsg->pszText, m_cchMax);
m_szBuffer[m_cchMax] = '\0';
m_cchSize = lstrlenW(m_szBuffer);
if (m_idxchCaret > m_cchSize) {
m_idxchCaret = m_cchSize;
}
UpdateCaretPos();
return S_OK;
}
//------------------------------------------------------------------------------
HRESULT
SmEditLineF::ApiGetTextFill(EditLineFGadget::GetTextFillMsg * pmsg)
{
pmsg->pgpbrFill = m_pgpbrText;
return S_OK;
}
//------------------------------------------------------------------------------
HRESULT
SmEditLineF::ApiSetTextFill(EditLineFGadget::SetTextFillMsg * pmsg)
{
m_pgpbrText = pmsg->pgpbrFill;
return S_OK;
}
//------------------------------------------------------------------------------
void
SmEditLineF::RebuildCaret()
{
m_cyLinePxl = (int) (m_pgpfnt->GetSize() * 1.5f);
m_cyCaretPxl = m_cyLinePxl;
m_yCaretOffsetPxl = 0;
}
//------------------------------------------------------------------------------
void
SmEditLineF::UpdateCaretPos()
{
Gdiplus::Graphics gpgr(GetDesktopWindow());
Gdiplus::RectF rcBound, rcLayout;
RECT rcThis;
GetStub()->GetRect(SGR_CLIENT, &rcThis);
rcLayout.X = 0;
rcLayout.Y = 0;
rcLayout.Width = (float) rcThis.right;
rcLayout.Height = (float) rcThis.bottom;
gpgr.MeasureString(m_szBuffer, m_idxchCaret, m_pgpfnt, rcLayout, &rcBound);
m_ptCaretPxl.X = rcBound.Width;
m_ptCaretPxl.Y = 0;
GetStub()->Invalidate();
}
//------------------------------------------------------------------------------
int
SmEditLineF::ComputeMouseChar(POINT ptOffsetPxl)
{
//
// Check boundary conditions
//
if (ptOffsetPxl.x < 0) {
return 0;
}
SIZE sizePxl;
GetStub()->GetSize(&sizePxl);
if (ptOffsetPxl.x > sizePxl.cx) {
return m_cchSize;
}
//
// This implementation is very poor, but we don't care for now because this
// is a sample control and is only for single-line.
//
int ichFound = 0;
Gdiplus::Graphics gpgr(GetDesktopWindow());
Gdiplus::RectF rcBound, rcLayout;
rcLayout.X = 0;
rcLayout.Y = 0;
rcLayout.Width = (float) sizePxl.cx;
rcLayout.Height = (float) sizePxl.cy;
for (int cch = 0; cch < m_cchSize; cch++) {
gpgr.MeasureString(m_szBuffer, cch, m_pgpfnt, rcLayout, &rcBound);
if (rcBound.Width > (float) ptOffsetPxl.x) {
// We are greater, so return previous character
AssertMsg(cch > 0, "Should have at least one character");
ichFound = cch - 1;
goto Cleanup;
}
}
Cleanup:
return ichFound;
}
#endif // ENABLE_MSGTABLE_API