mirror of
https://github.com/dashr9230/SA-MP.git
synced 2026-10-10 03:33:04 +08:00
551 lines
17 KiB
C++
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;
|
|
}
|