Files
SA-MP/saco/d3d9/common/DXUTUniBuffer.cpp
T

551 lines
17 KiB
C++

//--------------------------------------------------------------------------------------
// File: DXUTUniBuffer.cpp
//
// Desc: CUniBuffer, the Uniscribe-backed text buffer behind the edit controls
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#include "dxstdafx.h"
// <new.h> omits operator delete's throw(), which forces unwind frames into
// every dtor that deletes
#include <new>
#include "DXUTgui.h"
#define UNISCRIBE_DLLNAME "\\usp10.dll"
#define GETPROCADDRESS( Module, APIName, Temp ) \
Temp = GetProcAddress( Module, #APIName ); \
if( Temp ) \
*(FARPROC*)&_##APIName = Temp
#define PLACEHOLDERPROC( APIName ) \
_##APIName = Dummy_##APIName
// DXUT_MAX_EDITBOXLENGTH is the maximum string length allowed in edit boxes,
// including the NULL terminator.
//
// Uniscribe does not support strings having bigger-than-16-bits length.
// This means that the string must be less than 65536 characters long,
// including the NULL terminator.
#define DXUT_MAX_EDITBOXLENGTH 0xFFFF
// stays in DXUTgui.cpp; retail reaches it across the tu boundary
LPCWSTR MaskText( LPCWSTR wszText );
// Static member initialization
HINSTANCE CUniBuffer::s_hDll = NULL;
HRESULT (WINAPI *CUniBuffer::_ScriptApplyDigitSubstitution)( const SCRIPT_DIGITSUBSTITUTE*, SCRIPT_CONTROL*, SCRIPT_STATE* ) = Dummy_ScriptApplyDigitSubstitution;
HRESULT (WINAPI *CUniBuffer::_ScriptStringAnalyse)( HDC, const void *, int, int, int, DWORD, int, SCRIPT_CONTROL*, SCRIPT_STATE*, const int*, SCRIPT_TABDEF*, const BYTE*, SCRIPT_STRING_ANALYSIS* ) = Dummy_ScriptStringAnalyse;
HRESULT (WINAPI *CUniBuffer::_ScriptStringCPtoX)( SCRIPT_STRING_ANALYSIS, int, BOOL, int* ) = Dummy_ScriptStringCPtoX;
HRESULT (WINAPI *CUniBuffer::_ScriptStringXtoCP)( SCRIPT_STRING_ANALYSIS, int, int*, int* ) = Dummy_ScriptStringXtoCP;
HRESULT (WINAPI *CUniBuffer::_ScriptStringFree)( SCRIPT_STRING_ANALYSIS* ) = Dummy_ScriptStringFree;
const SCRIPT_LOGATTR* (WINAPI *CUniBuffer::_ScriptString_pLogAttr)( SCRIPT_STRING_ANALYSIS ) = Dummy_ScriptString_pLogAttr;
const int* (WINAPI *CUniBuffer::_ScriptString_pcOutChars)( SCRIPT_STRING_ANALYSIS ) = Dummy_ScriptString_pcOutChars;
//--------------------------------------------------------------------------------------
void CUniBuffer::Initialize()
{
if( s_hDll ) // Only need to do once
return;
TCHAR wszPath[MAX_PATH+1];
if( !::GetSystemDirectory( wszPath, MAX_PATH+1 ) )
return;
// Verify whether it is safe to concatenate these strings
int len1 = lstrlen(wszPath);
int len2 = lstrlen(UNISCRIBE_DLLNAME);
if (len1 + len2 > MAX_PATH)
{
return;
}
// We have verified that the concatenated string will fit into wszPath,
// so it is safe to concatenate them.
StringCchCat( wszPath, MAX_PATH, UNISCRIBE_DLLNAME );
s_hDll = LoadLibrary( wszPath );
if( s_hDll )
{
FARPROC Temp;
GETPROCADDRESS( s_hDll, ScriptApplyDigitSubstitution, Temp );
GETPROCADDRESS( s_hDll, ScriptStringAnalyse, Temp );
GETPROCADDRESS( s_hDll, ScriptStringCPtoX, Temp );
GETPROCADDRESS( s_hDll, ScriptStringXtoCP, Temp );
GETPROCADDRESS( s_hDll, ScriptStringFree, Temp );
GETPROCADDRESS( s_hDll, ScriptString_pLogAttr, Temp );
GETPROCADDRESS( s_hDll, ScriptString_pcOutChars, Temp );
}
}
//--------------------------------------------------------------------------------------
void CUniBuffer::Uninitialize()
{
if( s_hDll )
{
PLACEHOLDERPROC( ScriptApplyDigitSubstitution );
PLACEHOLDERPROC( ScriptStringAnalyse );
PLACEHOLDERPROC( ScriptStringCPtoX );
PLACEHOLDERPROC( ScriptStringXtoCP );
PLACEHOLDERPROC( ScriptStringFree );
PLACEHOLDERPROC( ScriptString_pLogAttr );
PLACEHOLDERPROC( ScriptString_pcOutChars );
FreeLibrary( s_hDll );
s_hDll = NULL;
}
}
//--------------------------------------------------------------------------------------
bool CUniBuffer::SetBufferSize( int nNewSize )
{
// If the current size is already the maximum allowed,
// we can't possibly allocate more.
if( m_nBufferSize == DXUT_MAX_EDITBOXLENGTH )
return false;
int nAllocateSize = ( nNewSize == -1 || nNewSize < m_nBufferSize * 2 ) ? ( m_nBufferSize ? m_nBufferSize * 2 : 256 ) : nNewSize * 2;
// Cap the buffer size at the maximum allowed.
if( nAllocateSize > DXUT_MAX_EDITBOXLENGTH )
nAllocateSize = DXUT_MAX_EDITBOXLENGTH;
WCHAR *pTempBuffer = new WCHAR[nAllocateSize];
if( !pTempBuffer )
return false;
if( m_pwszBuffer )
{
CopyMemory( pTempBuffer, m_pwszBuffer, m_nBufferSize * sizeof(WCHAR) );
delete[] m_pwszBuffer;
}
else
{
ZeroMemory( pTempBuffer, sizeof(WCHAR) * nAllocateSize );
}
m_pwszBuffer = pTempBuffer;
m_nBufferSize = nAllocateSize;
return true;
}
//--------------------------------------------------------------------------------------
// Uniscribe -- Analyse() analyses the string in the buffer
//--------------------------------------------------------------------------------------
HRESULT CUniBuffer::Analyse()
{
if( m_Analysis )
_ScriptStringFree( &m_Analysis );
SCRIPT_CONTROL ScriptControl; // For uniscribe
SCRIPT_STATE ScriptState; // For uniscribe
ZeroMemory( &ScriptControl, sizeof(ScriptControl) );
ZeroMemory( &ScriptState, sizeof(ScriptState) );
_ScriptApplyDigitSubstitution ( NULL, &ScriptControl, &ScriptState );
if( !m_pFontNode )
return E_FAIL;
HRESULT hr;
if( !field_12 )
{
hr = _ScriptStringAnalyse( m_pFontNode->pFont ? m_pFontNode->pFont->GetDC() : NULL,
m_pwszBuffer,
lstrlenW( m_pwszBuffer ) + 1, // NULL is also analyzed.
lstrlenW( m_pwszBuffer ) * 3 / 2 + 16,
-1,
SSA_BREAK | SSA_GLYPHS | SSA_FALLBACK | SSA_LINK,
0,
&ScriptControl,
&ScriptState,
NULL,
NULL,
NULL,
&m_Analysis );
}
else
{
hr = _ScriptStringAnalyse( m_pFontNode->pFont ? m_pFontNode->pFont->GetDC() : NULL,
MaskText( m_pwszBuffer ),
lstrlenW( MaskText( m_pwszBuffer ) ) + 1, // NULL is also analyzed.
lstrlenW( MaskText( m_pwszBuffer ) ) * 3 / 2 + 16,
-1,
SSA_BREAK | SSA_GLYPHS | SSA_FALLBACK | SSA_LINK,
0,
&ScriptControl,
&ScriptState,
NULL,
NULL,
NULL,
&m_Analysis );
}
if( SUCCEEDED( hr ) )
m_bAnalyseRequired = false; // Analysis is up-to-date
return hr;
}
//--------------------------------------------------------------------------------------
CUniBuffer::CUniBuffer( int nInitialSize )
{
CUniBuffer::Initialize(); // ensure static vars are properly init'ed first
m_nBufferSize = 0;
m_pwszBuffer = NULL;
m_bAnalyseRequired = true;
m_Analysis = NULL;
m_pFontNode = NULL;
field_8 = 0;
field_12 = 0;
if( nInitialSize > 0 )
SetBufferSize( nInitialSize );
}
//--------------------------------------------------------------------------------------
CUniBuffer::~CUniBuffer()
{
delete[] m_pwszBuffer;
if( m_Analysis )
_ScriptStringFree( &m_Analysis );
}
//--------------------------------------------------------------------------------------
WCHAR& CUniBuffer::operator[]( int n ) // No param checking
{
// This version of operator[] is called only
// if we are asking for write access, so
// re-analysis is required.
m_bAnalyseRequired = true;
return m_pwszBuffer[n];
}
//--------------------------------------------------------------------------------------
void CUniBuffer::Clear()
{
*m_pwszBuffer = L'\0';
m_bAnalyseRequired = true;
}
//--------------------------------------------------------------------------------------
// Inserts the char at specified index.
// If nIndex == -1, insert to the end.
//--------------------------------------------------------------------------------------
bool CUniBuffer::InsertChar( int nIndex, WCHAR tchr )
{
assert( nIndex >= 0 );
if( nIndex < 0 || nIndex > lstrlenW( m_pwszBuffer ) )
return false; // invalid index
// Check for maximum length allowed
if( GetTextSize() + 1 >= DXUT_MAX_EDITBOXLENGTH )
return false;
if( lstrlenW( m_pwszBuffer ) + 1 >= m_nBufferSize )
{
if( !SetBufferSize( -1 ) )
return false; // out of memory
}
assert( m_nBufferSize >= 2 );
// Shift the characters after the index, start by copying the null terminator
WCHAR* dest = m_pwszBuffer + lstrlenW(m_pwszBuffer)+1;
WCHAR* stop = m_pwszBuffer + nIndex;
WCHAR* src = dest - 1;
while( dest > stop )
{
*dest-- = *src--;
}
// Set new character
m_pwszBuffer[ nIndex ] = tchr;
m_bAnalyseRequired = true;
return true;
}
bool CUniBuffer::InsertChar(int nIndex, CHAR tchr)
{
WCHAR WideCharStr[2];
ZeroMemory(&WideCharStr[0], sizeof(WideCharStr));
if(field_8)
{
CHAR MultiByteStr[3];
MultiByteStr[0] = field_8;
MultiByteStr[1] = tchr;
MultiByteStr[2] = 0;
MultiByteToWideChar(CP_ACP, 0, MultiByteStr, 2, WideCharStr, 1);
InsertChar(nIndex, WideCharStr[0]);
field_8 = 0;
return true;
}
else if(IsDBCSLeadByteEx(CP_ACP, tchr))
{
field_8 = tchr;
return false;
}
else
{
MultiByteToWideChar(CP_ACP, 0, &tchr, 1, WideCharStr, 1);
InsertChar(nIndex, WideCharStr[0]);
return true;
}
}
// overwrite variant of InsertChar; the buffer index assignment is samp's, not a typo of ours
bool CUniBuffer::SetChar(int nIndex, CHAR tchr)
{
WCHAR WideCharStr[2];
ZeroMemory(&WideCharStr[0], sizeof(WideCharStr));
if(field_8)
{
CHAR MultiByteStr[3];
MultiByteStr[0] = field_8;
MultiByteStr[1] = tchr;
MultiByteStr[2] = 0;
MultiByteToWideChar(CP_ACP, 0, MultiByteStr, 2, WideCharStr, 1);
InsertChar(nIndex, WideCharStr[0]);
field_8 = 0;
return true;
}
else if(IsDBCSLeadByteEx(CP_ACP, tchr))
{
field_8 = tchr;
return false;
}
else
{
MultiByteToWideChar(CP_ACP, 0, &tchr, 1, WideCharStr, 1);
this[nIndex] = WideCharStr[0];
return true;
}
}
//--------------------------------------------------------------------------------------
// Removes the char at specified index.
// If nIndex == -1, remove the last char.
//--------------------------------------------------------------------------------------
bool CUniBuffer::RemoveChar( int nIndex )
{
if( !lstrlenW( m_pwszBuffer ) || nIndex < 0 || nIndex >= lstrlenW( m_pwszBuffer ) )
return false; // Invalid index
MoveMemory( m_pwszBuffer + nIndex, m_pwszBuffer + nIndex + 1, sizeof(WCHAR) * ( lstrlenW( m_pwszBuffer ) - nIndex ) );
m_bAnalyseRequired = true;
return true;
}
//--------------------------------------------------------------------------------------
// Inserts the first nCount characters of the string pStr at specified index.
// If nCount == -1, the entire string is inserted.
// If nIndex == -1, insert to the end.
//--------------------------------------------------------------------------------------
bool CUniBuffer::InsertString( int nIndex, const WCHAR *pStr, int nCount )
{
assert( nIndex >= 0 );
if( nIndex > lstrlenW( m_pwszBuffer ) )
return false; // invalid index
if( -1 == nCount )
nCount = lstrlenW( pStr );
// Check for maximum length allowed
if( GetTextSize() + nCount >= DXUT_MAX_EDITBOXLENGTH )
return false;
if( lstrlenW( m_pwszBuffer ) + nCount >= m_nBufferSize )
{
if( !SetBufferSize( lstrlenW( m_pwszBuffer ) + nCount + 1 ) )
return false; // out of memory
}
MoveMemory( m_pwszBuffer + nIndex + nCount, m_pwszBuffer + nIndex, sizeof(WCHAR) * ( lstrlenW( m_pwszBuffer ) - nIndex + 1 ) );
CopyMemory( m_pwszBuffer + nIndex, pStr, nCount * sizeof(WCHAR) );
m_bAnalyseRequired = true;
return true;
}
//--------------------------------------------------------------------------------------
bool CUniBuffer::SetText( LPCWSTR wszText )
{
assert( wszText != NULL );
int nRequired = int(lstrlenW( wszText ) + 1);
// Check for maximum length allowed
if( nRequired >= DXUT_MAX_EDITBOXLENGTH )
return false;
while( GetBufferSize() < nRequired )
if( !SetBufferSize( -1 ) )
break;
// Check again in case out of memory occurred inside while loop.
if( GetBufferSize() >= nRequired )
{
StringCchCopyW( m_pwszBuffer, GetBufferSize(), wszText );
m_bAnalyseRequired = true;
return true;
}
else
return false;
}
//--------------------------------------------------------------------------------------
void ConvertAnsiToWide(LPCSTR wszSrc, LPWSTR wszDest, int nSize)
{
memset(wszDest, 0, sizeof(WCHAR) * nSize);
int nCount = MultiByteToWideChar(CP_ACP, 0, wszSrc, strlen(wszSrc), NULL, 0);
if(nCount < nSize)
MultiByteToWideChar(CP_ACP, 0, wszSrc, strlen(wszSrc), wszDest, nCount);
}
//--------------------------------------------------------------------------------------
bool CUniBuffer::SetText( LPCTSTR wszText )
{
if(lstrlenA(wszText) > 1024) return false;
WCHAR szBuffer[2049];
ConvertAnsiToWide(wszText, szBuffer, 2048);
SetText(szBuffer);
return true;
}
//--------------------------------------------------------------------------------------
HRESULT CUniBuffer::CPtoX( int nCP, BOOL bTrail, int *pX )
{
assert( pX );
*pX = 0; // Default
HRESULT hr = S_OK;
if( m_bAnalyseRequired )
hr = Analyse();
if( SUCCEEDED( hr ) )
hr = _ScriptStringCPtoX( m_Analysis, nCP, bTrail, pX );
return hr;
}
//--------------------------------------------------------------------------------------
HRESULT CUniBuffer::XtoCP( int nX, int *pCP, int *pnTrail )
{
assert( pCP && pnTrail );
*pCP = 0; *pnTrail = FALSE; // Default
HRESULT hr = S_OK;
if( m_bAnalyseRequired )
hr = Analyse();
if( SUCCEEDED( hr ) )
hr = _ScriptStringXtoCP( m_Analysis, nX, pCP, pnTrail );
// If the coordinate falls outside the text region, we
// can get character positions that don't exist. We must
// filter them here and convert them to those that do exist.
if( *pCP == -1 && *pnTrail == TRUE )
{
*pCP = 0; *pnTrail = FALSE;
} else
if( *pCP > lstrlenW( m_pwszBuffer ) && *pnTrail == FALSE )
{
*pCP = lstrlenW( m_pwszBuffer ); *pnTrail = TRUE;
}
return hr;
}
//--------------------------------------------------------------------------------------
void CUniBuffer::GetPriorItemPos( int nCP, int *pPrior )
{
*pPrior = nCP; // Default is the char itself
if( m_bAnalyseRequired )
if( FAILED( Analyse() ) )
return;
const SCRIPT_LOGATTR *pLogAttr = _ScriptString_pLogAttr( m_Analysis );
if( !pLogAttr )
return;
if( !_ScriptString_pcOutChars( m_Analysis ) )
return;
int nInitial = *_ScriptString_pcOutChars( m_Analysis );
if( nCP - 1 < nInitial )
nInitial = nCP - 1;
for( int i = nInitial; i > 0; --i )
if( pLogAttr[i].fWordStop || // Either the fWordStop flag is set
( !pLogAttr[i].fWhiteSpace && // Or the previous char is whitespace but this isn't.
pLogAttr[i-1].fWhiteSpace ) )
{
*pPrior = i;
return;
}
// We have reached index 0. 0 is always a break point, so simply return it.
*pPrior = 0;
}
//--------------------------------------------------------------------------------------
void CUniBuffer::GetNextItemPos( int nCP, int *pPrior )
{
*pPrior = nCP; // Default is the char itself
HRESULT hr = S_OK;
if( m_bAnalyseRequired )
hr = Analyse();
if( FAILED( hr ) )
return;
const SCRIPT_LOGATTR *pLogAttr = _ScriptString_pLogAttr( m_Analysis );
if( !pLogAttr )
return;
if( !_ScriptString_pcOutChars( m_Analysis ) )
return;
int nInitial = *_ScriptString_pcOutChars( m_Analysis );
if( nCP + 1 < nInitial )
nInitial = nCP + 1;
for( int i = nInitial; i < *_ScriptString_pcOutChars( m_Analysis ) - 1; ++i )
{
if( pLogAttr[i].fWordStop ) // Either the fWordStop flag is set
{
*pPrior = i;
return;
}
else
if( pLogAttr[i].fWhiteSpace && // Or this whitespace but the next char isn't.
!pLogAttr[i+1].fWhiteSpace )
{
*pPrior = i+1; // The next char is a word stop
return;
}
}
// We have reached the end. It's always a word stop, so simply return it.
*pPrior = *_ScriptString_pcOutChars( m_Analysis ) - 1;
}