DragonNest/Common/EtInterface/EtUITextBox.cpp
Cussrro 47f7895977 Revert "修复编码问题"
This reverts commit 9e69c01767.
2024-12-21 10:04:04 +08:00

2040 lines
No EOL
48 KiB
C++

#include "StdAfx.h"
#include "EtUITextBox.h"
#include "EtUIDialog.h"
#include "StringUtil.h"
#include "EtUINameLinkMgr.h"
#include "EternityEngine.h"
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
#endif
CEtUITextBox::CEtUITextBox( CEtUIDialog *pParent )
: CEtUIControl( pParent )
, m_nStartLine(-1)
, m_nEndLine(-1)
, m_nCurLine(-1)
, m_nVisibleCount(0)
, m_bAutoScroll(true)
, m_fSymbolWidth(0.0f)
, m_fTextMargin(0.0f)
, m_ScrollBar( pParent )
, m_bUseWordBreak(true)
, m_pNextPageTextBox(NULL)
, m_eNextPageCondition(NextPage_DlgScreenHeight)
, m_nNextPageLineCount(0)
, m_nAdjustValue(0)
, m_bAddTextToNextPage(false)
, m_bNextPage(false)
, m_nMaxLine(200)
, m_RenderTextColor(0)
, m_bUseSelect(false)
{
m_Property.UIType = UI_CONTROL_TEXTBOX;
#ifdef TEXTBOX_RENDERLOCK
m_nRenderingState = 0;
#endif
}
CEtUITextBox::~CEtUITextBox(void)
{
SAFE_DELETE_PVEC( m_vecLine );
}
void CEtUITextBox::Initialize( SUIControlProperty *pProperty )
{
CEtUIControl::Initialize( pProperty );
m_ScrollBar.Initialize( NULL );
m_ScrollBar.SetParentControl( this );
if( m_Property.TextBoxProperty.nScrollBarTemplate != -1 )
{
m_ScrollBar.SetTemplate( m_Property.TextBoxProperty.nScrollBarTemplate );
m_ScrollBar.UpdateRects();
}
// Note : 심볼의 크기를 계산한다.
CalcSymbolTextRect();
m_ScrollBar.SetTrackRange( 0, 0 );
if( m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_HEIGHT ||
m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_BOTH )
{
// Note : 가변길이 텍스트 박스이면 스크롤바는 무시한다.
m_Property.TextBoxProperty.bVerticalScrollBar = false;
}
SUIElement *pElement = GetElement(0);
if( pElement )
{
pElement->ShadowFontColor.dwCurrentColor = pElement->ShadowFontColor.dwColor[UI_STATE_NORMAL];
}
}
void CEtUITextBox::Render( float fElapsedTime )
{
//if( !IsShow() )
// return;
SUIElement *pElement = GetElement(0);
if( !pElement ) return;
if( m_bExistTemplateTexture )
m_pParent->DrawSprite( m_Template.m_hTemplateTexture, pElement->TemplateUVCoord, pElement->TextureColor.dwCurrentColor, m_Property.UICoord, 0.0f, m_fZValue );
else
m_pParent->DrawSprite( pElement->UVCoord, pElement->TextureColor.dwCurrentColor, m_Property.UICoord, 0.0f, m_fZValue );
// Note : 디버그 때문에 추가
//m_pParent->DrawRect( m_Property.UICoord, EtInterface::debug::BLUE );
#ifdef TEXTBOX_RENDERLOCK
InterlockedExchange( &m_nRenderingState, 1 );
#endif
RenderText( fElapsedTime );
#ifdef TEXTBOX_RENDERLOCK
InterlockedExchange( &m_nRenderingState, 0 );
#endif
if( m_Property.TextBoxProperty.bVerticalScrollBar )
{
// Note : 마우스를 계속 클릭하고 있을때 스크롤을 하기 위한 변수
int nOldPosition=0, nCurPosition;
if( !m_ScrollBar.IsArrowStateClear() )
{
nOldPosition = m_ScrollBar.GetTrackPos();
}
m_ScrollBar.Render( fElapsedTime );
if( !m_ScrollBar.IsArrowStateClear() )
{
nCurPosition = m_ScrollBar.GetTrackPos();
Scroll( nCurPosition-nOldPosition );
}
}
}
void CEtUITextBox::RenderText( float fElapsedTime )
{
if( m_nCurLine == -1 )
return;
SUICoord sLineCoord;
int nCount(0);
int nVecSize = (int)m_vecLine.size();
for( int i=m_nCurLine; i<nVecSize; i++, nCount++ )
{
if( nCount >= m_nVisibleCount )
break;
if( m_Property.TextBoxProperty.bRollOver )
{
if( IsMouseEnter() && m_vecLine[i]->IsMouseInLine() )
{
//sLineCoord = m_vecLine[i].m_uiCoord;
//sLineCoord.fWidth = m_Property.UICoord.fWidth-m_Property.TextBoxProperty.fScrollBarSize;
//m_pParent->DrawRect( sLineCoord, textcolor::DARKGRAY );
RenderLine( fElapsedTime, m_vecLine[i], true );
continue;
}
else if( m_vecLine[i]->IsSelected() )
{
RenderLine( fElapsedTime, m_vecLine[i], true );
continue;
}
}
RenderLine( fElapsedTime, m_vecLine[i] );
//m_pParent->DrawRect( m_vecLine[i].m_uiCoord, EtInterface::debug::BLUE );
}
}
void CEtUITextBox::RenderLine( float fElapsedTime, CLine* sLine, bool bRollOver )
{
VECWORD & vecWord = sLine->m_vecWord;
#ifdef TEXTBOX_RENDERLOCK
// 원래 제대로 렌더에 락건다면, 지금처럼 하는게 아니라
// 렌더링 시작하기 전에 AddText, SetText, AppendText등의 텍스트 추가함수의 동작이 완전히 멈췄는지를 판단해야한다.
// 그런데 이렇게 하려면,
// 렌더링여부만 판단하는 m_nRenderingState(0,1값)에다가 Add중임을 알리는 상태도 추가해야하고
// 렌더링호출 앞뒤에다가도 락을 걸어야만 한다.
// 현재 렌더락이 많이 사용되는 관계로 덤프안나면서 락을 최소한으로 줄이기 위해 이 방법을 택한다.
int nBorderFlag = 0;
for( int i=0; i<(int)vecWord.size(); i++ )
#else
int nWordSize = (int)vecWord.size();
for( int i=0; i<nWordSize; i++ )
#endif
{
if( vecWord[i].m_sProperty.BgColor )
{
nBorderFlag = 0;
if( i == 0 ) nBorderFlag |= 1;
if( i == (int)vecWord.size()-1 ) nBorderFlag |= 2;
nBorderFlag |= 4;
nBorderFlag |= 8;
}
RenderWord( fElapsedTime, vecWord[i], bRollOver, nBorderFlag );
}
}
void CEtUITextBox::RenderWord( float fElapsedTime, CWord& sWord, bool bRollOver, int nBorderFlag )
{
std::wstring strRenderString;
SUICoord uiRenderCoord;
strRenderString = sWord.m_strWord;
uiRenderCoord = sWord.m_sProperty.uiCoord;
if( sWord.m_sProperty.bSymbol )
{
strRenderString += L"...";
uiRenderCoord.fWidth += m_fSymbolWidth;
}
UI_CONTROL_STATE currentState = UI_STATE_NORMAL;
if( !IsShow() )
{
currentState = UI_STATE_HIDDEN;
}
else if( !IsEnable() )
{
currentState = UI_STATE_DISABLED;
}
sWord.m_sProperty.uiColor.Blend( currentState, fElapsedTime, m_fBlendRate );
SUIElement *pElement = GetElement(0);
if( !pElement ) return;
uiRenderCoord.fX += pElement->fFontHoriOffset;
uiRenderCoord.fY += pElement->fFontVertOffset;
if( bRollOver )
{
m_pParent->DrawDlgText( strRenderString.c_str(), pElement, sWord.m_sProperty.uiColor.dwCurrentColor, uiRenderCoord, -1, -1, true, m_fZValue, sWord.m_sProperty.BgColor, nBorderFlag );
}
else
{
m_pParent->DrawDlgText( strRenderString.c_str(), pElement, sWord.m_sProperty.uiColor.dwCurrentColor, uiRenderCoord, -1, -1, true, m_fZValue, sWord.m_sProperty.BgColor, nBorderFlag );
}
//m_pParent->DrawRect( uiRenderCoord, EtInterface::debug::RED );
}
void CEtUITextBox::UpdateRects()
{
//UpdateText();
UpdateAlignText();
if( m_Property.TextBoxProperty.bVerticalScrollBar )
{
if( m_Property.TextBoxProperty.bLeftScrollBar )
{
m_ScrollBar.SetPosition( m_Property.UICoord.fX, m_Property.UICoord.fY );
}
else
{
m_ScrollBar.SetPosition( m_Property.UICoord.Right()-m_Property.TextBoxProperty.fScrollBarSize, m_Property.UICoord.fY );
}
m_ScrollBar.SetSize( m_Property.TextBoxProperty.fScrollBarSize, m_Property.UICoord.fHeight );
}
}
void CEtUITextBox::UpdateText()
{
if( !UpdateVisibleCount() )
return;
float fX = m_Property.UICoord.fX;
float fY = m_Property.UICoord.fY;
if( m_Property.TextBoxProperty.bVerticalScrollBar && m_Property.TextBoxProperty.bLeftScrollBar )
{
fX += m_Property.TextBoxProperty.fScrollBarSize + 0.004f;
}
fY += m_fTextMargin;
int nCount(0);
int nVecSize = (int)m_vecLine.size();
for( int i=m_nCurLine; i<nVecSize; i++, nCount++ )
{
if( nCount >= m_nVisibleCount )
break;
m_vecLine[i]->UpdatePos( fX, fY );
fY += m_vecLine[i]->m_uiCoord.fHeight;
}
}
void CEtUITextBox::UpdateText( float fX, float fY )
{
//if( !UpdateVisibleCount() )
// return;
if( m_Property.TextBoxProperty.bVerticalScrollBar && m_Property.TextBoxProperty.bLeftScrollBar )
{
fX += m_Property.TextBoxProperty.fScrollBarSize + 0.004f;
}
fY += m_fTextMargin;
int nCount(0);
int nVecSize = (int)m_vecLine.size();
for( int i=m_nCurLine; i<nVecSize; i++, nCount++ )
{
if( nCount >= m_nVisibleCount )
break;
m_vecLine[i]->UpdatePos( fX, fY );
fY += m_vecLine[i]->m_uiCoord.fHeight;
}
}
bool CEtUITextBox::UpdateVisibleCount()
{
if( m_vecLine.empty() )
return false;
if( m_Property.TextBoxProperty.AllignVert == AT_VERT_BOTTOM )
{
m_nVisibleCount = (int)(m_Property.UICoord.fHeight/m_vecLine[0]->m_uiCoord.fHeight);
}
else
{
// BOTTOM일때와 달리 컨트롤전체 Height 구해서 계산하지 않고,
// 현재 존재하는 라인개수에 맞춰서 구해야한다.
float fUIHeight, fLineHeight(0.0f);
fUIHeight = m_Property.UICoord.fHeight;
int nCount(0);
for( int i=m_nCurLine; i<(int)m_vecLine.size(); i++, nCount++ )
{
fLineHeight += m_vecLine[i]->m_uiCoord.fHeight;
if( fLineHeight > fUIHeight )
break;
}
m_nVisibleCount = nCount;
}
m_ScrollBar.SetPageSize( m_nVisibleCount );
return true;
}
void CEtUITextBox::SetText( const LPCWSTR szText, const D3DCOLOR TextColor )
{
ASSERT( szText );
std::wstring strTemp(szText), strSubStr;
std::wstring::size_type textSize, begIdx, endIdx;
textSize = strTemp.size();
begIdx = 0;
endIdx = strTemp.find_first_of(L"\n", begIdx);
while( begIdx < textSize )
{
if( endIdx != std::wstring::npos )
{
strSubStr = strTemp.substr(begIdx, endIdx-begIdx);
endIdx++;
}
else
{
strSubStr = strTemp.substr(begIdx);
}
if( strSubStr.empty() )
{
AddLine();
}
else
{
DoAddText(strSubStr.c_str(), L"", TextColor);
}
begIdx = endIdx;
endIdx = strTemp.find_first_of(L"\n", begIdx);
}
}
void CEtUITextBox::AddText( LPCWSTR szText, const D3DCOLOR TextColor, DWORD dwFormat, const D3DCOLOR BgColor )
{
if( m_bAddTextToNextPage ) {
m_pNextPageTextBox->AddText( szText, TextColor, dwFormat, BgColor );
return;
}
AddLine();
SENTENCE curSentence;
SWORD_PROPERTY basicProperty;
basicProperty.uiColor.dwColor[UI_STATE_NORMAL] = TextColor;
basicProperty.uiColor.dwCurrentColor = TextColor;
basicProperty.dwFormat = dwFormat;
basicProperty.bAdd = false;
basicProperty.BgColor = BgColor;
const CEtUINameLinkMgr& nameLinkMgr = EtInterface::GetNameLinkMgr();
nameLinkMgr.TranslateText(curSentence, basicProperty, szText);
m_OriginalStrings.push_back(curSentence);
if( (int)m_OriginalStrings.size() > m_nMaxLine ) {
m_OriginalStrings.pop_front();
}
std::wstring translated;
SENTENCE::const_iterator iter = curSentence.begin();
for (; iter != curSentence.end(); ++iter)
{
const CWord& curWord = *iter;
const SWORD_PROPERTY& prop = curWord.m_sProperty;
if( m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_WIDTH ||
m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_BOTH )
{
AppendTextV( curWord.m_strWord.c_str(), curWord.m_strWordWithTag.c_str(), prop.uiColor.dwCurrentColor, prop.dwFormat, true, prop.BgColor );
}
else
{
AppendTextF( curWord.m_strWord.c_str(), curWord.m_strWordWithTag.c_str(), prop.uiColor.dwCurrentColor, prop.dwFormat, true, prop.BgColor );
}
translated += L"\n";
m_szText = translated.c_str();
}
ClearLineData();
}
void CEtUITextBox::DoAddText( LPCWSTR szText, LPCWSTR szTextWithTag, const D3DCOLOR TextColor, DWORD dwFormat, const D3DCOLOR BgColor )
{
if( m_bAddTextToNextPage ) {
m_pNextPageTextBox->DoAddText( szText, szTextWithTag, TextColor, dwFormat, BgColor );
return;
}
AddLine();
if( m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_WIDTH ||
m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_BOTH )
{
AppendTextV( szText, szTextWithTag, TextColor, dwFormat, true, BgColor );
}
else
{
AppendTextF( szText, szTextWithTag, TextColor, dwFormat, true, BgColor );
}
ClearLineData();
}
void CEtUITextBox::AppendText( LPCWSTR szText, const D3DCOLOR TextColor, DWORD dwFormat, bool bAdd, const D3DCOLOR BgColor, LPCWSTR szTextWithTag )
{
if( m_bAddTextToNextPage ) {
m_pNextPageTextBox->AppendText( szText, TextColor, dwFormat, bAdd, BgColor, szTextWithTag );
return;
}
if( m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_WIDTH ||
m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_BOTH )
AppendTextV( szText, szTextWithTag, TextColor, dwFormat, bAdd, BgColor );
else
AppendTextF( szText, szTextWithTag, TextColor, dwFormat, bAdd, BgColor );
m_szText += szText;
ClearLineData();
}
void CEtUITextBox::AppendTextF( const LPCWSTR szText, const LPCWSTR szTextWithTag, const D3DCOLOR TextColor, DWORD dwFormat, bool bAdd, const D3DCOLOR BgColor )
{
if( CEtFontMng::s_bUseUniscribe )
{
if( m_vecLine.empty() )
{
AddLine();
}
SUIElement *pElement;
pElement = GetElement(0);
SUICoord sTextCoord;
m_pParent->CalcTextRect( szText, pElement, sTextCoord );
// Note : 현재 라인의 남아있는 공간을 계산한다.
float fRemainWidth = m_Property.UICoord.fWidth;
if( m_Property.TextBoxProperty.bLeftScrollBar || m_Property.TextBoxProperty.bVerticalScrollBar )
fRemainWidth -= m_Property.TextBoxProperty.fScrollBarSize;
float fOriginWidth = fRemainWidth;
CLine* pLine = GetEndLine();
if( pLine )
fRemainWidth -= pLine->m_uiCoord.fWidth;
if( fRemainWidth <= 0.0f )
{
DoAddText( szText, szTextWithTag, TextColor, dwFormat );
return;
}
SWORD_PROPERTY wordProperty;
wordProperty.uiColor.dwColor[UI_STATE_NORMAL] = TextColor;
wordProperty.uiColor.dwCurrentColor = TextColor;
wordProperty.dwFormat = dwFormat;
wordProperty.bAdd = bAdd;
wordProperty.BgColor = BgColor;
if( dwFormat & UITEXT_SYMBOL )
{
fRemainWidth -= m_fSymbolWidth;
}
vector<wstring> vecStrLine;
int nMaxWidth;
if( !CEtFontMng::GetInstance().GetWordBreakText( std::wstring( szText ), pElement->nFontIndex, pElement->nFontHeight,
fRemainWidth * m_pParent->GetScreenWidth(), vecStrLine, nMaxWidth, true, fOriginWidth * m_pParent->GetScreenWidth() ) )
return;
bool bAddLine = false;
if( static_cast<int>( vecStrLine.size() ) > 1 )
bAddLine = true;
if( static_cast<int>( vecStrLine.size() ) > 0 && bAddLine && dwFormat & UITEXT_SYMBOL && !m_Property.TextBoxProperty.bVerticalScrollBar )
{
wordProperty.bSymbol = true;
SetTooltipText( szText );
AddWord( vecStrLine[0].c_str(), szTextWithTag, wordProperty );
}
else
{
for( int i=0; i<static_cast<int>( vecStrLine.size() ); i++ )
{
AddWord( vecStrLine[i].c_str(), szTextWithTag, wordProperty );
if( bAddLine && i < static_cast<int>( vecStrLine.size() ) - 1 )
AddLine();
}
}
}
else
{
if( m_vecLine.empty() )
{
AddLine();
}
SUIElement *pElement;
pElement = GetElement(0);
SUICoord sTextCoord;
m_pParent->CalcTextRect( szText, pElement, sTextCoord );
// Note : 현재 라인의 남아있는 공간을 계산한다.
float fRemainWidth = m_Property.UICoord.fWidth;
fRemainWidth -= m_Property.TextBoxProperty.fScrollBarSize;
CLine* pLine = GetEndLine();
if( pLine )
fRemainWidth -= pLine->m_uiCoord.fWidth;
if( fRemainWidth <= 0.0f )
{
DoAddText( szText, szTextWithTag, TextColor, dwFormat );
return;
}
SWORD_PROPERTY wordProperty;
wordProperty.uiColor.dwColor[UI_STATE_NORMAL] = TextColor;
wordProperty.uiColor.dwCurrentColor = TextColor;
wordProperty.dwFormat = dwFormat;
wordProperty.bAdd = bAdd;
wordProperty.BgColor = BgColor;
if( sTextCoord.fWidth > fRemainWidth )
{
std::wstring strText, strSub;
strText = szText;
if( dwFormat & UITEXT_SYMBOL )
{
fRemainWidth -= m_fSymbolWidth;
}
if( fRemainWidth <= 0.0f )
{
DoAddText(szText, szTextWithTag, TextColor, dwFormat);
return;
}
int nTrail = 0;
int nCaret = CEtFontMng::GetInstance().GetCaretFromCaretPos( strText.c_str(), pElement->nFontIndex, pElement->nFontHeight,
int(fRemainWidth*m_pParent->GetScreenWidth()), nTrail );
if( m_bUseWordBreak )
{
// 워드 브레이크 적용해야한다면, 적용한다. 현재 국내, 해외 빌드 가리지 않고, 영문, 숫자일 경우엔 처리하는 것으로 한다.
// FontMng에서의 워드 브레이크는 어차피 해상도마다 달라지기때문에 보이기에 깔끔하지 않을 수 있어서, 우선 적용안하는 것으로 하겠다.
bool bFirstWordInLine = ( pLine && pLine->m_uiCoord.fWidth == 0.0f );
nCaret = GetCaretWithWordBreak( strText, nCaret, bFirstWordInLine );
}
if( nCaret < (int)strText.size() )
{
if( dwFormat & UITEXT_SYMBOL )
{
wordProperty.bSymbol = true;
SetTooltipText( strText.c_str() );
}
strSub = strText.substr(0,nCaret+nTrail);
AddWord( strSub.c_str(), szTextWithTag, wordProperty );
if( dwFormat & (UITEXT_CLIP|UITEXT_SYMBOL) )
goto RETURN;
}
// 무한루프 방지
if( nCaret+nTrail == 0 && pLine && pLine->m_uiCoord.fWidth == 0.0f )
goto RETURN;
// Note : 남은 스트링은 다름 라인에 추가를 계속 한다.
AddLine();
wstring strLeftString = strText.substr( nCaret+nTrail );
AppendTextF(strLeftString.c_str(), szTextWithTag, TextColor, dwFormat, bAdd, BgColor);
}
else
{
AddWord( szText, szTextWithTag, wordProperty );
}
}
RETURN:
UpdateAlignText();
}
void CEtUITextBox::AppendTextV( const LPCWSTR szText, const LPCWSTR szTextWithTag, const D3DCOLOR TextColor, DWORD dwFormat, bool bAdd, const D3DCOLOR BgColor )
{
if( m_vecLine.empty() )
{
AddLine();
}
SWORD_PROPERTY wordProperty;
wordProperty.uiColor.dwColor[UI_STATE_NORMAL] = TextColor;
wordProperty.uiColor.dwCurrentColor = TextColor;
wordProperty.bAdd = bAdd;
wordProperty.BgColor = BgColor;
//wordProperty.dwFormat = dwFormat;
AddWord( szText, szTextWithTag, wordProperty );
UpdateAlignText();
}
void CEtUITextBox::AddLine()
{
#ifdef TEXTBOX_RENDERLOCK
while( m_nRenderingState == 1 ) {}
#endif
SUIElement *pElement = GetElement(0);
if( !pElement ) return;
SUICoord sTextCoord;
m_pParent->CalcTextRect( L"x", pElement, sTextCoord );
CLine *sLine = new CLine;
if( m_vecLine.empty() )
{
// Note : 최초 추가시, 첫번째 라인의 좌표를 설정
// 시작라인과 현재 라인의 인덱스를 0으로 설정
sLine->m_uiCoord = m_Property.UICoord;
m_nStartLine = 0;
m_nCurLine = 0;
}
else
{
CLine* sCurLine = GetEndLine();
if (sCurLine)
{
sLine->m_uiCoord.fX = sCurLine->m_uiCoord.fX;
sLine->m_uiCoord.fY = sCurLine->m_uiCoord.Bottom();
}
}
sLine->m_uiCoord.fWidth = 0.0f;
sLine->m_uiCoord.fHeight = sTextCoord.fHeight;
sLine->SetLineSpace( m_Property.TextBoxProperty.fLineSpace );
sLine->SetLineData( GetLineData() );
m_vecLine.push_back( sLine );
m_nEndLine++;
if( m_nEndLine >= m_nMaxLine ) {
SAFE_DELETE( m_vecLine[0] );
m_vecLine.erase( m_vecLine.begin() );
m_nEndLine--;
}
m_ScrollBar.SetTrackRange( 0, ( int )m_vecLine.size() );
// Note : 가변길이 텍스트 박스 업데이트
UpdateTextBox();
}
void CEtUITextBox::AddImage( WCHAR *wszIamgeName, int nWidth, int nHeight )
{
#ifdef TEXTBOX_RENDERLOCK
while( m_nRenderingState == 1 ) {}
#endif
SUIElement *pElement = GetElement(0);
if( !pElement ) return;
SUICoord sTextCoord;
m_pParent->CalcTextRect( L"x", pElement, sTextCoord );
std::string szTextureName;
ToMultiString( wszIamgeName, szTextureName );
EtTextureHandle hTexture = EternityEngine::LoadTexture( szTextureName.c_str() );
if( !hTexture ) return;
SUICoord sUVCoord;
int nWidthTemp = (int)( m_Property.UICoord.fWidth * DEFAULT_UI_SCREEN_WIDTH );
int nHeightTemp = (int)( ( ( sTextCoord.fHeight + m_Property.TextBoxProperty.fLineSpace ) ) * DEFAULT_UI_SCREEN_HEIGHT );
int nCount = (int)( nHeight / nHeightTemp );
float fUVHeight = ( 1.f / (float)nHeight ) * (float)( ( ( sTextCoord.fHeight + m_Property.TextBoxProperty.fLineSpace ) ) * DEFAULT_UI_SCREEN_HEIGHT );
// 만약 구형 그래픽카드에서 텍스처가 2의 승수 아닌것때문에 깨져서 나온다고 하면 텍스처 모두 2의 승수로 바꾸고 아래를 활성화시키면 된다.
//float fUVHeight = ( 1.f / (float)hTexture->OriginalHeight() ) * (float)( ( ( sTextCoord.fHeight + m_Property.TextBoxProperty.fLineSpace ) ) * DEFAULT_UI_SCREEN_HEIGHT );
fUVHeight = ( (int)( fUVHeight * hTexture->OriginalHeight() ) / (float)hTexture->OriginalHeight() );
if( nHeight % nHeightTemp != 0 ) nCount++;
float fCoordWidth = nWidth / (float)DEFAULT_UI_SCREEN_WIDTH;
if( fCoordWidth > m_Property.UICoord.fWidth ) fCoordWidth = m_Property.UICoord.fWidth;
for( int i=0; i<nCount; i++ ) {
CImageLine *sLine = new CImageLine;
if( m_vecLine.empty() )
{
// Note : 최초 추가시, 첫번째 라인의 좌표를 설정
// 시작라인과 현재 라인의 인덱스를 0으로 설정
sLine->m_uiCoord = m_Property.UICoord;
m_nStartLine = 0;
m_nCurLine = 0;
}
else
{
CLine* sCurLine = GetEndLine();
if (sCurLine)
{
sLine->m_uiCoord.fX = sCurLine->m_uiCoord.fX;
sLine->m_uiCoord.fY = sCurLine->m_uiCoord.Bottom();
}
}
sLine->m_uiCoord.fWidth = fCoordWidth;
sLine->m_uiCoord.fHeight = sTextCoord.fHeight;
sLine->SetLineSpace( m_Property.TextBoxProperty.fLineSpace );
sLine->SetLineData( GetLineData() );
sUVCoord.fX = 0.f;
sUVCoord.fY = fUVHeight * i;
sUVCoord.fWidth = 1.f;
// 구형 그래픽카드 대체용.
//sUVCoord.fWidth = (float)nWidth / (float)hTexture->OriginalWidth();
sUVCoord.fHeight = fUVHeight;
sLine->SetImage( (char*)szTextureName.c_str(), sUVCoord );
m_vecLine.push_back( sLine );
m_nEndLine++;
if( m_nEndLine >= m_nMaxLine ) {
SAFE_DELETE( m_vecLine[0] );
m_vecLine.erase( m_vecLine.begin() );
m_nEndLine--;
}
}
SAFE_RELEASE_SPTR( hTexture );
AddLine();
m_ScrollBar.SetTrackRange( 0, ( int )m_vecLine.size() );
// Note : 가변길이 텍스트 박스 업데이트
UpdateTextBox();
}
void CEtUITextBox::AddWord( const LPCWSTR szText, const LPCWSTR szTextWithTag, SWORD_PROPERTY &wordProperty )
{
#ifdef TEXTBOX_RENDERLOCK
while( m_nRenderingState == 1 ) {}
#endif
SUICoord sTextCoord;
m_pParent->CalcTextRect( szText, GetElement(0), sTextCoord );
CLine* pEndLine = GetEndLine();
if (pEndLine == NULL)
return;
CLine& sEndLine = *pEndLine;
CWord sWord;
sWord.m_strWord = szText;
sWord.m_strWordWithTag = szTextWithTag;
if( wordProperty.dwFormat & UITEXT_RIGHT )
{
wordProperty.uiCoord.fX = m_Property.UICoord.fWidth - sTextCoord.fWidth;
}
else if( wordProperty.dwFormat & UITEXT_CENTER )
{
// 여기서 이렇게 계산하긴 해도,
// 나중에 UpdateText가 호출되면서 각 라인별로 void CLine::UpdatePos( float fX, float fY ) 함수가 호출된다.
// 이 함수에서 다시 한번 위치 계산을 하게 된다.
wordProperty.uiCoord.fX = (m_Property.UICoord.fWidth - sTextCoord.fWidth)*0.5f;
}
else
{
wordProperty.uiCoord.fX = sEndLine.m_uiCoord.Right();
}
wordProperty.uiCoord.fY = sEndLine.m_uiCoord.fY+sEndLine.GetLineSpace();
if( wordProperty.bSymbol ) sTextCoord.fWidth += m_fSymbolWidth;
wordProperty.uiCoord.fWidth = sTextCoord.fWidth;
wordProperty.uiCoord.fHeight = sTextCoord.fHeight;
sWord.m_sProperty = wordProperty;
sEndLine.m_vecWord.push_back( sWord );
// Note : 라인의 크기를 텍스트의 가장 큰 높이로 한다.
if( sEndLine.m_uiCoord.fHeight < wordProperty.uiCoord.fHeight )
sEndLine.m_uiCoord.fHeight = wordProperty.uiCoord.fHeight;
if( wordProperty.dwFormat & (UITEXT_RIGHT|UITEXT_CENTER) )
{
// Note : 워드 정렬 플래그가 들어오면 라인의 길이는 텍스트박스의 크기가 된다.
sEndLine.m_uiCoord.fWidth = m_Property.UICoord.fWidth;
}
else
{
sEndLine.m_uiCoord.fWidth += wordProperty.uiCoord.fWidth;
}
// Note : 가변길이 텍스트 박스 업데이트
UpdateTextBox();
}
void CEtUITextBox::AddColorText( LPCWSTR szText, const D3DCOLOR TextColor, const D3DCOLOR BgColor, const D3DCOLOR DecreaseColor )
{
DWORD dwFontColor = TextColor;
std::wstring szStr = szText;
if( DecreaseColor ) {
dwFontColor = CalcDecreaseColor( dwFontColor, DecreaseColor );
}
while( !szStr.empty() )
{
bool bStringLF = true;
std::wstring::size_type nPosInStr;
std::wstring::size_type nPosInStr1 = szStr.find_first_of( L"\n" ); // 실제 개행문자일수도 있고,
std::wstring::size_type nPosInStr2 = szStr.find( L"\\n" ); // 문자열로 직접 입력된 개행문자일수도 있다. 두글자 이상일땐 find 검색.
// 둘 중 먼저 나오는 것으로 처리한다.
if( nPosInStr1 < nPosInStr2 )
bStringLF = false;
nPosInStr = min(nPosInStr1, nPosInStr2);
std::wstring szCurLine = szStr.substr(0, nPosInStr);
AddText( L"" );
while( !szCurLine.empty() )
{
std::wstring::size_type nLength = szCurLine.size();
std::wstring::size_type nPosInLine = szCurLine.find_first_of( L"#" );
std::wstring szCurWord = szCurLine.substr(0, nPosInLine);
if( !szCurWord.empty() )
AppendText( szCurWord.c_str(), dwFontColor, UITEXT_NONE, false, BgColor );
if( nPosInLine != std::wstring::npos && nLength >= 2 )
{
wchar_t cColorValue = szCurLine[nPosInLine+1];
switch(cColorValue)
{
case 'r': dwFontColor = descritioncolor::RED; break; // 레드
case 'g': dwFontColor = descritioncolor::GREEN; break; // 그린
case 'b': dwFontColor = descritioncolor::DODGERBLUE; break; // 블루
case 'y': dwFontColor = descritioncolor::YELLOW1; break; // 노란
case 'e': dwFontColor = descritioncolor::YELLOW2; break; // 노란
case 'v': dwFontColor = descritioncolor::VIOLET; break; // 보라
case 's': dwFontColor = descritioncolor::SKY; break; // 하늘
case 'j': dwFontColor = descritioncolor::ORANGE; break; // 주황
case 'w': dwFontColor = descritioncolor::WHITE; break; // 흰
case 'h': dwFontColor = descritioncolor::GREY; break; // 회색
case 'd': dwFontColor = TextColor; break; // 처음 인자로 받은 디폴트컬러
default: break; // 잘못 적으면 아무일 안함.
}
if( DecreaseColor ) {
dwFontColor = CalcDecreaseColor( dwFontColor, DecreaseColor );
}
}
std::wstring::size_type nNextPos = (nLength >= 2) ? nPosInLine+2 : nPosInLine+1;
szCurLine = szCurLine.substr(nNextPos);
if( nPosInLine == std::wstring::npos )
break;
}
szStr = szStr.substr(nPosInStr+(bStringLF?2:1));
if( nPosInStr == std::wstring::npos )
break;
}
}
int CEtUITextBox::Scroll( int nScrollAmount )
{
if( !IsScrollMode() || nScrollAmount == 0 )
return 0;
int nScrollPos(0);
if( nScrollAmount < 0 )
{
nScrollAmount = -nScrollAmount;
if( m_nCurLine >= nScrollAmount )
{
m_nCurLine -= nScrollAmount;
}
else
{
m_nCurLine = 0;
}
nScrollPos = m_nCurLine;
}
else if( nScrollAmount > 0 )
{
int nTemp = (int)m_vecLine.size() - m_nCurLine - m_nVisibleCount;
if( nTemp >= nScrollAmount )
{
m_nCurLine += nScrollAmount;
}
else
{
m_nCurLine += nTemp;
}
nScrollPos = m_nCurLine+m_nVisibleCount-1;
}
UpdateText();
return nScrollPos;
}
void CEtUITextBox::ScrollLineUp()
{
if( !IsScrollMode() )
return;
if( m_nCurLine > 1 )
{
m_nCurLine -= 2;
}
else if( m_nCurLine > 0 )
{
m_nCurLine--;
}
UpdateText();
m_ScrollBar.ShowItem( m_nCurLine );
}
void CEtUITextBox::ScrollLineDown()
{
if( !IsScrollMode() )
return;
int nTemp = (int)m_vecLine.size() - m_nCurLine;
nTemp -= m_nVisibleCount;
if( nTemp > 1 )
{
m_nCurLine += 2;
}
else if( nTemp > 0 )
{
m_nCurLine++;
}
UpdateText();
m_ScrollBar.ShowItem( m_nCurLine+m_nVisibleCount-1 );
}
void CEtUITextBox::ScrollPageUp()
{
if( !IsScrollMode() )
return;
if( m_nCurLine > m_nVisibleCount )
{
m_nCurLine -= m_nVisibleCount;
}
else
{
m_nCurLine = 0;
}
UpdateText();
m_ScrollBar.ShowItem( m_nCurLine );
}
void CEtUITextBox::ScrollPageDown()
{
if( !IsScrollMode() )
return;
int nTemp = (int)m_vecLine.size() - m_nCurLine;
nTemp -= m_nVisibleCount;
if( nTemp > m_nVisibleCount )
{
m_nCurLine += m_nVisibleCount;
}
else
{
m_nCurLine += nTemp;
}
UpdateText();
m_ScrollBar.ShowItem( m_nCurLine+m_nVisibleCount-1 );
}
void CEtUITextBox::ScrollHome()
{
if( !IsScrollMode() )
return;
m_nCurLine = 0;
UpdateText();
m_ScrollBar.ShowItem( m_nCurLine );
}
void CEtUITextBox::ScrollEnd()
{
if( !IsScrollMode() )
return;
int nVecSize = (int)m_vecLine.size();
m_nCurLine = nVecSize - m_nVisibleCount;
UpdateText();
m_ScrollBar.ShowItem( nVecSize-1 );
}
void CEtUITextBox::ScrollPageTurn()
{
if( !IsScrollMode() || IsLastPage() )
return;
m_nCurLine += m_nVisibleCount;
UpdateText();
m_ScrollBar.ShowItem( m_nCurLine );
}
bool CEtUITextBox::IsLastPage()
{
if( (int)m_vecLine.size()-m_nCurLine <= m_nVisibleCount )
{
return true;
}
return false;
}
void CEtUITextBox::SetLineSpace( float fSpace )
{
m_Property.TextBoxProperty.fLineSpace = fSpace;
int nVecSize = (int)m_vecLine.size();
for( int i=0; i<nVecSize; i++ )
{
m_vecLine[i]->SetLineSpace( fSpace );
}
UpdateRects();
}
void CEtUITextBox::UpdateAlignText()
{
if( !UpdateVisibleCount() )
return;
float fX = m_Property.UICoord.fX;
float fY = m_Property.UICoord.fY;
switch( m_Property.TextBoxProperty.AllignVert )
{
case AT_VERT_NONE:
break;
case AT_VERT_TOP:
break;
case AT_VERT_CENTER:
{
int nVecSize = (int)m_vecLine.size();
if( nVecSize < m_nVisibleCount )
{
CLine* pLine = GetEndLine();
if (pLine)
{
float fTemp = (m_nVisibleCount - nVecSize) * pLine->m_uiCoord.fHeight;
fTemp *= 0.5f;
fY += fTemp;
}
}
}
break;
case AT_VERT_BOTTOM:
{
if( m_bAutoScroll )
{
int nVecSize = (int)m_vecLine.size();
if( nVecSize > m_nVisibleCount )
{
m_nCurLine = nVecSize - m_nVisibleCount;
}
else
{
m_nCurLine = 0;
CLine* pLine = GetEndLine();
if (pLine)
fY += (m_nVisibleCount - nVecSize) * pLine->m_uiCoord.fHeight;
}
// Note : 아래 정렬일 경우 남는 공간을 위쪽에 넣어주기 위해 계산한다.
//
m_fTextMargin = m_Property.UICoord.fHeight-(m_vecLine[0]->m_uiCoord.fHeight*m_nVisibleCount);
m_ScrollBar.ShowItem( nVecSize-1 );
}
else
{
m_ScrollBar.ShowItem( m_nCurLine + m_nVisibleCount - 1 );
}
}
break;
}
UpdateText( fX, fY );
switch( m_Property.TextBoxProperty.AllignHori )
{
case AT_HORI_NONE:
break;
case AT_HORI_LEFT:
break;
case AT_HORI_CENTER:
case AT_HORI_RIGHT:
{
CLine *pLine(NULL);
float fGap;
SUICoord sTextBoxCoord;
GetUICoord(sTextBoxCoord);
int nVecSize = (int)m_vecLine.size();
for( int i=0; i<nVecSize; i++ )
{
pLine = m_vecLine[i];
fGap = sTextBoxCoord.fWidth - pLine->m_uiCoord.fWidth;
if( m_Property.TextBoxProperty.bVerticalScrollBar )
{
if( !m_Property.TextBoxProperty.bLeftScrollBar )
{
fGap -= m_Property.TextBoxProperty.fScrollBarSize;
}
}
if( m_Property.TextBoxProperty.AllignHori == AT_HORI_CENTER)
{
fGap *= 0.5f;
}
pLine->UpdatePos(pLine->m_uiCoord.fX+fGap, pLine->m_uiCoord.fY);
}
}
break;
}
}
void CEtUITextBox::ClearText()
{
if( m_pNextPageTextBox && m_bAddTextToNextPage ) {
m_pNextPageTextBox->ClearText();
m_bAddTextToNextPage = false;
}
m_szText.clear();
SAFE_DELETE_PVEC( m_vecLine );
// m_vecLine.clear();
m_nStartLine = -1;
m_nEndLine = -1;
m_nCurLine = -1;
m_nVisibleCount = 0;
m_ScrollBar.SetTrackRange( 0, 0 );
}
void CEtUITextBox::CalcSymbolTextRect()
{
SUICoord sSymbolCoord;
m_pParent->CalcTextRect( L"...", GetElement(0), sSymbolCoord );
m_fSymbolWidth = sSymbolCoord.fWidth;
}
bool CEtUITextBox::HandleKeyboard( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
if( !IsEnable() || !IsShow() )
{
return false;
}
if( !m_Property.TextBoxProperty.bVerticalScrollBar )
{
return false;
}
if( m_ScrollBar.HandleKeyboard( uMsg, wParam, lParam ) )
{
return true;
}
if( uMsg == WM_KEYDOWN )
{
switch( wParam )
{
case VK_UP: ScrollLineUp(); return true;
case VK_DOWN: ScrollLineDown(); return true;
case VK_PRIOR: ScrollPageUp(); return true;
case VK_NEXT: ScrollPageDown(); return true;
case VK_HOME: ScrollHome(); return true;
case VK_END: ScrollEnd(); return true;
}
}
return false;
}
bool CEtUITextBox::HandleMouse( UINT uMsg, float fX, float fY, WPARAM wParam, LPARAM lParam )
{
if( !IsEnable() || !IsShow() )
{
return false;
}
if( ( WM_LBUTTONDOWN == uMsg ) && ( !m_bFocus ) )
{
m_pParent->RequestFocus( this );
}
int nOldPosition = m_ScrollBar.GetTrackPos();
if( m_ScrollBar.HandleMouse( uMsg, fX, fY, wParam, lParam ) )
{
int nCurPosition = m_ScrollBar.GetTrackPos();
Scroll( nCurPosition-nOldPosition );
return true;
}
switch( uMsg )
{
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
{
if( IsInside( fX, fY ) )
{
m_bPressed = true;
SetCapture( m_pParent->GetHWnd() );
if( !m_bFocus )
{
m_pParent->RequestFocus( this );
}
return true;
}
}
break;
case WM_LBUTTONUP:
{
if( m_bPressed )
{
m_bPressed = false;
ReleaseCapture();
if( IsInside( fX, fY ) )
{
UINT uMsgInner(WM_LBUTTONUP);
if( wParam & MK_CONTROL )
{
uMsgInner |= 0x0100;
}
if( m_Property.TextBoxProperty.bRollOver )
{
if( SelectWord( fX, fY ) )
{
m_pParent->ProcessCommand( EVENT_TEXTBOX_SELECTION, true, this, uMsgInner );
}
}
}
return true;
}
}
break;
case WM_MOUSEMOVE:
{
if( IsInside( fX, fY ) )
{
if( m_Property.TextBoxProperty.bRollOver )
{
SetMouseInWord(fX, fY);
}
}
}
break;
case WM_MOUSEWHEEL:
{
UINT uLines;
SystemParametersInfo( SPI_GETWHEELSCROLLLINES, 0, &uLines, 0 );
int nScrollAmount = int( ( short )HIWORD( wParam ) ) / WHEEL_DELTA * uLines;
int nScrollPos = Scroll( -nScrollAmount );
m_ScrollBar.ShowItem( nScrollPos );
return true;
}
break;
}
return false;
}
std::wstring CEtUITextBox::GetLineText(int nIndex)
{
if(nIndex >= (int)m_vecLine.size())
return L"";
return m_vecLine[nIndex]->GetText();
}
CLine* CEtUITextBox::GetEndLine()
{
ASSERT(m_nEndLine>=0);
ASSERT(!m_vecLine.empty());
// 여기서 죽는 경우가 있어서 방어코드 넣어봅니다.
// 나중에 AddLine 넘어가서 return sLine 까지 가면 문제가 있는것입니다.
if( m_nEndLine < 0 || m_nEndLine >= (int)m_vecLine.size() ) {
AddLine();
if( m_nEndLine < 0 || m_nEndLine >= (int)m_vecLine.size() ) {
return NULL;
}
}
return m_vecLine[m_nEndLine];
}
void CEtUITextBox::SetMouseInLine( float fX, float fY )
{
ClearMouseEnteredLine();
if( m_nCurLine >= 0 )
{
int nCount(0);
SUICoord uiLineCoord;
int nVecSize = (int)m_vecLine.size();
for( int i=m_nCurLine; i<nVecSize; i++, nCount++ )
{
if( nCount >= m_nVisibleCount )
break;
uiLineCoord = m_vecLine[i]->m_uiCoord;
uiLineCoord.fWidth = m_Property.UICoord.fWidth;
if( uiLineCoord.IsInside( fX, fY ) )
{
if( (int)m_vecLine[i]->m_vecWord.size() > 0 )
{
m_vecLine[i]->SetMouseInLine(true);
}
return;
}
}
}
}
void CEtUITextBox::SetMouseInWord( float fX, float fY )
{
ClearMouseEnteredLine();
m_WordMouseOver.Clear();
if( m_nCurLine >= 0 )
{
int nCount(0);
SUICoord uiLineCoord;
int nVecSize = (int)m_vecLine.size();
for( int i=m_nCurLine; i<nVecSize; i++, nCount++ )
{
if( nCount >= m_nVisibleCount )
break;
uiLineCoord = m_vecLine[i]->m_uiCoord;
uiLineCoord.fWidth = m_Property.UICoord.fWidth;
if( uiLineCoord.IsInside( fX, fY ) )
{
if( (int)m_vecLine[i]->m_vecWord.size() > 0 )
{
m_vecLine[i]->SetMouseInLine(true);
const VECWORD& wordList = m_vecLine[i]->m_vecWord;
VECWORD::const_iterator iter = wordList.begin();
for (; iter != wordList.end(); ++iter)
{
const CWord& word = *iter;
if (word.m_sProperty.uiCoord.IsInside(fX, fY))
{
m_WordMouseOver = word;
return;
}
}
}
return;
}
}
}
}
int CEtUITextBox::GetMouseEnteredLine()
{
if( m_nCurLine >= 0 )
{
int nCount(0);
int nVecSize = (int)m_vecLine.size();
for( int i=m_nCurLine; i<nVecSize; i++, nCount++ )
{
if( nCount >= m_nVisibleCount )
break;
if( m_vecLine[i]->IsMouseInLine() )
{
return i;
}
}
}
return -1;
}
bool CEtUITextBox::SelectLine( float fX, float fY )
{
// Note : 마우스 클릭으로 라인을 선택한다.
// 한번에 하나의 라인만 선택될 수 있다.
// 멀티라인 선택은 따로 구현해야 한다.
ClearSelectedLine();
if( m_nCurLine >= 0 )
{
int nCount(0);
SUICoord uiLineCoord;
int nVecSize = (int)m_vecLine.size();
for( int i=m_nCurLine; i<nVecSize; i++, nCount++ )
{
if( nCount >= m_nVisibleCount )
break;
// Note : m_Property의 Width를 더해주어야 라인 전체 크기의 좌표가 된다.
// 그렇지 않으면 글자가 보이는 만큼의 길로 한정된다.
uiLineCoord = m_vecLine[i]->m_uiCoord;
uiLineCoord.fWidth = m_Property.UICoord.fWidth;
if( uiLineCoord.IsInside( fX, fY ) )
{
if( (int)m_vecLine[i]->m_vecWord.size() > 0 )
{
int nCurSelLine = GetMouseEnteredLine();
if( nCurSelLine == i )
{
m_vecLine[nCurSelLine]->Select(true);
m_vecLine[nCurSelLine]->SetMouseInLine(false);
return true;
}
}
return false;
}
}
}
return false;
}
bool CEtUITextBox::SelectWord(float fX, float fY)
{
ClearSelectedLine();
m_WordSelected.Clear();
if( m_nCurLine >= 0 )
{
int nCount(0);
SUICoord uiLineCoord;
int nVecSize = (int)m_vecLine.size();
for( int i=m_nCurLine; i<nVecSize; i++, nCount++ )
{
if( nCount >= m_nVisibleCount )
break;
// Note : m_Property의 Width를 더해주어야 라인 전체 크기의 좌표가 된다.
// 그렇지 않으면 글자가 보이는 만큼의 길로 한정된다.
uiLineCoord = m_vecLine[i]->m_uiCoord;
uiLineCoord.fWidth = m_Property.UICoord.fWidth;
if( uiLineCoord.IsInside( fX, fY ) )
{
if( (int)m_vecLine[i]->m_vecWord.size() > 0 )
{
int nCurSelLine = GetMouseEnteredLine();
if( nCurSelLine == i )
{
m_vecLine[nCurSelLine]->Select(true);
m_vecLine[nCurSelLine]->SetMouseInLine(false);
const VECWORD& wordList = m_vecLine[nCurSelLine]->m_vecWord;
VECWORD::const_iterator iter = wordList.begin();
for (; iter != wordList.end(); ++iter)
{
const CWord& word = *iter;
if (word.m_sProperty.uiCoord.IsInside(fX, fY))
{
m_WordSelected = word;
return true;
}
}
}
}
return false;
}
}
}
return false;
}
int CEtUITextBox::GetSelectedLineIndex( bool bClearSelection /* = true */ ) const
{
if( m_nCurLine >= 0 )
{
int nCount(0);
int nVecSize = (int)m_vecLine.size();
for( int i=m_nCurLine; i<nVecSize; i++, nCount++ )
{
if( nCount >= m_nVisibleCount )
break;
if( m_vecLine[i]->IsSelected() )
{
if( bClearSelection )
m_vecLine[i]->Select(false);
return i;
}
}
}
return -1;
}
void CEtUITextBox::UpdateTextBoxHeight()
{
if( m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_HEIGHT ||
m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_BOTH )
{
float fLineHeight(0);
int nVecSize = (int)m_vecLine.size();
for( int i=0; i<nVecSize; i++ )
{
CLine *line = m_vecLine[i];
fLineHeight += line->m_uiCoord.fHeight;
}
float fBeforeHeight, fAfterHeight;
SUICoord sTextBoxCoord;
GetUICoord(sTextBoxCoord);
fBeforeHeight = sTextBoxCoord.fHeight;
sTextBoxCoord.fHeight = fLineHeight;
fAfterHeight = sTextBoxCoord.fHeight;
SetUICoord(sTextBoxCoord);
if( !m_bNextPage )
m_pParent->UpdateDlgCoord( 0, 0, 0, (fAfterHeight-fBeforeHeight) );
// NextPage를 사용해야하는지 확인
if( m_pNextPageTextBox ) // && m_bAddTextToNextPage == false ) Remove로 줄어들때도 있으니 플래그 검사는 제외.
{
if( m_eNextPageCondition == NextPage_DlgScreenHeight )
{
// 첫번째 페이지의 경우엔 이렇게 검사하고,
if( !m_bNextPage )
{
SUIElement *pElement = GetElement(0);
if( pElement ) {
SUICoord sTextCoord, sDlgCoord;
m_pParent->CalcTextRect( L"x", pElement, sTextCoord );
m_pParent->GetDlgCoord( sDlgCoord );
if( sDlgCoord.fHeight + sTextCoord.fHeight > m_pParent->GetScreenHeightRatio() ) {
m_bAddTextToNextPage = true;
m_pNextPageTextBox->SetNextPageLineCount( GetLineSize(), m_nAdjustValue );
}
}
}
else
{
// 그 이후 페이지들은 설정값
if( GetLineSize() >= m_nNextPageLineCount )
m_bAddTextToNextPage = true;
}
}
}
}
}
void CEtUITextBox::UpdateTextBoxWidth()
{
if( m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_WIDTH ||
m_Property.TextBoxProperty.VariableType == UI_TEXTBOX_BOTH )
{
float fLineWidth(0);
int nVecSize = (int)m_vecLine.size();
for( int i=0; i<nVecSize; i++ )
{
CLine *line = m_vecLine[i];
if( fLineWidth < line->m_uiCoord.fWidth )
{
// Note : 가장 긴 라인을 저장한다.
//
fLineWidth = line->m_uiCoord.fWidth;
}
}
float fBeforeWidth, fAfterWidth;
SUICoord sTextBoxCoord;
GetUICoord(sTextBoxCoord);
fBeforeWidth = sTextBoxCoord.fWidth;
sTextBoxCoord.fWidth = fLineWidth;
fAfterWidth = sTextBoxCoord.fWidth;
SetUICoord(sTextBoxCoord);
m_pParent->UpdateDlgCoord(0, 0, (fAfterWidth-fBeforeWidth), 0);
}
}
void CEtUITextBox::FitTextBoxToLine()
{
if( m_vecLine.empty() )
return;
int nvecLineSize = (int)m_vecLine.size();
int nTemp = nvecLineSize - m_nCurLine;
if( nTemp < m_nVisibleCount )
{
int nLineHeight = (int)(m_vecLine[0]->m_uiCoord.fHeight*m_pParent->GetScreenHeight());
SUICoord uiCoord;
GetUICoord(uiCoord);
uiCoord.fHeight = (nTemp*nLineHeight)/m_pParent->GetScreenHeight();
SetUICoord(uiCoord);
}
}
void CEtUITextBox::ClearMouseEnteredLine()
{
int nCurSelLine = GetMouseEnteredLine();
if( nCurSelLine != -1 )
{
m_vecLine[nCurSelLine]->SetMouseInLine(false);
}
}
void CEtUITextBox::ClearSelectedLine()
{
int nCurSelLine = GetSelectedLineIndex();
if( nCurSelLine != -1 )
{
m_vecLine[nCurSelLine]->Select(false);
}
}
void CEtUITextBox::UpdateTextBox()
{
UpdateTextBoxWidth();
UpdateTextBoxHeight();
UpdateVisibleCount();
// [5/1/2009 nextome]
// 이걸 해줘야 초기에 스크롤바 필요없으면 disable 된다.
m_ScrollBar.SetPageSize( m_nVisibleCount );
m_ScrollBar.SetTrackRange( 0, ( int )m_vecLine.size() );
m_ScrollBar.UpdateRects();
}
bool CEtUITextBox::GetSelectedLineData( SLineData &sLineData, bool bClearSelect ) const
{
int nCurSelLine = GetSelectedLineIndex(bClearSelect);
if( nCurSelLine != -1 )
{
sLineData = m_vecLine[nCurSelLine]->GetLineData();
return true;
}
return false;
}
const CWord& CEtUITextBox::GetSelectedWordData() const
{
return m_WordSelected;
}
std::wstring CEtUITextBox::GetLastLineText()
{
if( GetUsedNextPage() ) {
return m_pNextPageTextBox->GetLastLineText();
}
if( m_vecLine.size() )
return m_vecLine[m_vecLine.size()-1]->GetText();
return L"";
}
bool CEtUITextBox::GetLastLineCoord( SUICoord &Coord )
{
if( GetUsedNextPage() ) {
return m_pNextPageTextBox->GetLastLineCoord( Coord );
}
if( m_vecLine.size() )
{
Coord = m_vecLine[m_vecLine.size()-1]->m_uiCoord;
return true;
}
return false;
}
void CEtUITextBox::RemoveLastLine()
{
if( GetUsedNextPage() ) {
m_pNextPageTextBox->RemoveLastLine();
return;
}
// 현재 툴팁에서만 검증되어있으므로 다른 곳에서 사용시 테스트 해볼 것을 권장.
if( m_vecLine.size() )
{
SAFE_DELETE( m_vecLine[m_vecLine.size() - 1] );
m_vecLine.pop_back();
m_nEndLine--;
int nCurLine = m_nCurLine;
std::wstring::size_type endIdx;
endIdx = m_szText.find_last_of(L"\n");
m_szText = m_szText.substr(0, endIdx);
m_ScrollBar.SetTrackRange( 0, (int)m_vecLine.size() );
UpdateTextBox();
}
}
void CEtUITextBox::ResizeLineTextWithSymbol(int nLineSize , std::wstring wszSymbol)
{
int nSize = GetLineSize();
for(int i=0;i<nSize - nLineSize;i++)
RemoveLastLine();
std::wstring wszString = GetLineText(nLineSize-1);
std::wstring wszSubString;
wszSubString = wszString.substr(0, wszString.size() - wszSymbol.size() );
wszSubString += wszSymbol;
ClearText();
AppendText( wszSubString.c_str() );
}
void CEtUITextBox::SetNextPageTextBox( CEtUITextBox *pControl, eNextPageCondition eCondition, int nAdjustValue )
{
m_pNextPageTextBox = pControl;
m_eNextPageCondition = eCondition;
m_nAdjustValue = nAdjustValue;
pControl->SetNextPage( true );
}
void CEtUITextBox::SetNextPageLineCount( int nCount, int nAdjustValue )
{
m_nNextPageLineCount = nCount + nAdjustValue;
if( m_pNextPageTextBox ) m_pNextPageTextBox->SetNextPageLineCount( nCount, nAdjustValue );
}
int CEtUITextBox::GetUsedNextPage()
{
if( !m_pNextPageTextBox ) return 0;
if( m_pNextPageTextBox->IsEmpty() )
return 0;
// NextPage를 쭉 연결해서 3개, 4개 쓸때도 제대로 작동하도록 재귀로 만들어둔다.
return m_pNextPageTextBox->GetUsedNextPage() + 1;
}
void CEtUITextBox::Process( float fElapsedTime )
{
m_CurrentState = UI_STATE_NORMAL;
if( !IsShow() )
{
m_CurrentState = UI_STATE_HIDDEN;
}
else if( !IsEnable() )
{
m_CurrentState = UI_STATE_DISABLED;
}
SUIElement *pElement = GetElement(0);
if( !pElement ) return;
pElement->TextureColor.Blend( m_CurrentState, fElapsedTime, m_fBlendRate );
pElement->ShadowFontColor.Blend( m_CurrentState, fElapsedTime, m_fBlendRate );
}
int CEtUITextBox::GetCaretWithWordBreak( const std::wstring& strText, const int nOrigCaret, bool bFirstWordInLine )
{
int nNewCaret = nOrigCaret;
// 스트링을 공백단위로 분할한 후
std::vector<std::wstring> tokens;
TokenizeW( strText, tokens );
// 완전 공백일때는 nOrigCaret으로 리턴해 원래대로 처리.
if ( tokens.size() == 0 )
return nOrigCaret;
// 완전 공백 말고도 처리해야할 예외들이 몇가지 더 있다.
// 1. A A(A찍고 스페이스로 한줄넘게 공백 만든 후 A)
// 2. A
// 3. A
// 4. A (공백 A 또 공백)
// 이런 것들은 원래 처리하던대로 워드브레이크 없이 개행처리 될 것이다.
// 분할한 스트링안에 -이 있는지 확인한다. 있다면 분리해서 넣는다. ex:knocked-down => knocked-, down
for( std::vector<std::wstring>::iterator iter = tokens.begin(); iter != tokens.end(); ++iter )
{
std::wstring::size_type nPos;
if( (nPos = iter->find_first_of( L"-" )) != std::wstring::npos )
{
std::wstring wszWord1 = iter->substr(0, nPos+1);
std::wstring wszWord2 = iter->substr(nPos+1);
iter = tokens.erase(iter);
iter = tokens.insert(iter, wszWord2);
iter = tokens.insert(iter, wszWord1);
}
}
// 분할한 스트링마다의 시작위치, 끝위치를 계산한 후
std::vector<int> vecTokenBeginPos;
int nBeginPos = 0;
for(int i = 0; i < (int)tokens.size(); ++i)
{
// 공백의 갯수를 세서 추가로 더해준다. 공백으로 시작할 수도 있기때문에 공백검사를 먼저 한다.
int nSpaceCount = 0;
while( strText[nBeginPos+nSpaceCount] == L' ' )
++nSpaceCount;
nBeginPos += nSpaceCount;
vecTokenBeginPos.push_back(nBeginPos);
nBeginPos += (int)tokens[i].size();
}
// 처음 공백이 이미 한줄을 넘어간다면 그냥 원래 캐럿으로 개행처리한다. 예외 3번 처리.
if( (int)vecTokenBeginPos.size() > 0 && nOrigCaret < vecTokenBeginPos[0] )
return nOrigCaret;
// 첫번째 단어자체가 너무 길어 한줄을 넘어서면 Orig으로 리턴해준다.
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 이런 예외 상황.
if( bFirstWordInLine && (int)vecTokenBeginPos.size() > 0 && vecTokenBeginPos[0] < nOrigCaret && nOrigCaret < vecTokenBeginPos[0]+(int)tokens[0].size() )
return nOrigCaret;
// 첫번째 단어 후의 공백을 다시 계산해서,
// 한줄이 넘어가는 워드+공백이 처음으로 오면 원래 캐럿대로 잘라 처리한다. 예외 1,2번 처리.
int nFirstWordSpaceCount = 0;
while( strText[vecTokenBeginPos[0]+(int)tokens[0].size()+nFirstWordSpaceCount] == L' ' )
++nFirstWordSpaceCount;
if( vecTokenBeginPos[0]+(int)tokens[0].size() < nOrigCaret &&
nOrigCaret <= vecTokenBeginPos[0]+(int)tokens[0].size()+nFirstWordSpaceCount )
return nOrigCaret;
// 워드 단위 사이로 갱신
std::wstring strWord;
for(int i = 0; i < (int)vecTokenBeginPos.size(); ++i)
{
if( nNewCaret < vecTokenBeginPos[i] )
{
nNewCaret = vecTokenBeginPos[i-1];
strWord = tokens[i-1];
break;
}
}
if( nNewCaret > vecTokenBeginPos[(int)vecTokenBeginPos.size()-1] )
{
nNewCaret = vecTokenBeginPos[(int)vecTokenBeginPos.size()-1];
strWord = tokens[(int)vecTokenBeginPos.size()-1];
}
// word-break를 적용할 조건인지 확인한다.
/*
bool bWordBreakApply1 = false;
bool bWordBreakApply2 = false;
bool bWordBreakApply3 = true;
// 숫자% 일 경우에도 word-break를 적용한다.
if( strWord.size() > 1 && strWord[(int)strWord.size()-1] == L'%' )
{
bWordBreakApply1 = true;
for( int i = 0; i < (int)strWord.size()-1; ++i )
{
if( L'0' <= strWord[i] && strWord[i] <= L'9' )
{
}
else
{
bWordBreakApply1 = false;
break;
}
}
}
// 영어. 영어, 일 경우에도 word-break를 적용한다.
if( strWord.size() > 1 && (strWord[(int)strWord.size()-1] == L'.' || strWord[(int)strWord.size()-1] == L','))
{
bWordBreakApply2 = true;
for( int i = 0; i < (int)strWord.size()-1; ++i )
{
if( ((L'a' <= strWord[i] && strWord[i] <= L'z') ||
(L'A' <= strWord[i] && strWord[i] <= L'Z') )
{
}
else
{
bWordBreakApply2 = false;
break;
}
}
}
*/
bool bWordBreakApply = true;
// .으로 끝나는 영문, ,로 끝나는 영문, %로 끝나는 숫자 등으로 개별 검사하니 너무 많아진다.
// 그래서 그냥 문자집합 안에 포함되면 다 되는걸로 처리한다.
// 3th, 4m 이런것도 있기 때문이다.
for( int i = 0; i < (int)strWord.size(); ++i )
{
//if( iswalpha(strWord[i]) == false ) // iswalpha로 검사하면 한글도 alpha로 나온다. 그냥 iswletter인듯.
if( (L'a' <= strWord[i] && strWord[i] <= L'z') ||
(L'A' <= strWord[i] && strWord[i] <= L'Z') ||
(0x0400 <= strWord[i] && strWord[i] <= 0x052F) || // 러시아어 추가.
(L'0' <= strWord[i] && strWord[i] <= L'9') ||
strWord[i] == L'.' ||
strWord[i] == L',' ||
strWord[i] == L'*' ||
strWord[i] == L'%' ||
strWord[i] == L'-' ||
strWord[i] == L'/' ||
strWord[i] == L'(' ||
strWord[i] == L')' ||
strWord[i] == L'[' ||
strWord[i] == L']' ||
strWord[i] == L':' ||
strWord[i] == L'!' ||
strWord[i] == L'?' ||
strWord[i] == L'\"' ||
strWord[i] == L'\'' ) // I'll 또는 target's
{
}
else
{
bWordBreakApply = false;
break;
}
}
//if( bWordBreakApply1 || bWordBreakApply2 || bWordBreakApply3 )
if( bWordBreakApply )
{
return nNewCaret;
}
return nOrigCaret;
}
void CEtUITextBox::FindInputPos( std::vector<EtVector2> &vecPos )
{
CEtUIDialog *pDialog = GetParent();
if( !pDialog ) return;
if( m_vecLine.size() > 0 )
{
m_ScrollBar.FindInputPos( vecPos );
}
}