685 lines
14 KiB
C++
685 lines
14 KiB
C++
|
/*
|
||
|
** Command & Conquer Renegade(tm)
|
||
|
** Copyright 2025 Electronic Arts Inc.
|
||
|
**
|
||
|
** This program is free software: you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation, either version 3 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "IMECandidateCtrl.h"
|
||
|
#include "IMECandidate.h"
|
||
|
#include "DialogBase.h"
|
||
|
#include "StyleMgr.h"
|
||
|
#include "MouseMgr.h"
|
||
|
|
||
|
#define BORDER_WIDTH 2
|
||
|
#define BORDER_HEIGHT 2
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::IMECandidateCtrl
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
* NONE
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
IMECandidateCtrl::IMECandidateCtrl() :
|
||
|
mFullRect(0,0,0,0),
|
||
|
mCellSize(0,0),
|
||
|
mCurrSel(-1),
|
||
|
mScrollPos(0),
|
||
|
mCellsPerPage(0),
|
||
|
mCandidate(NULL)
|
||
|
{
|
||
|
StyleMgrClass::Assign_Font(&mTextRenderer, StyleMgrClass::FONT_LISTS);
|
||
|
|
||
|
// Candidate control never wants to take focus
|
||
|
Set_Wants_Focus(false);
|
||
|
|
||
|
// Setup child scrollbar
|
||
|
mScrollBarCtrl.Set_Wants_Focus(false);
|
||
|
mScrollBarCtrl.Set_Small_BMP_Mode(true);
|
||
|
mScrollBarCtrl.Set_Advise_Sink(this);
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::~IMECandidateCtrl
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
* NONE
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
IMECandidateCtrl::~IMECandidateCtrl()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::Init
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::Init(IME::IMECandidate* candidate)
|
||
|
{
|
||
|
WWDEBUG_SAY(("IMECandidateCtrl: Init\n"));
|
||
|
|
||
|
Reset();
|
||
|
mCandidate = candidate;
|
||
|
Changed(candidate);
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::Changed
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::Changed(IME::IMECandidate* candidate)
|
||
|
{
|
||
|
WWDEBUG_SAY(("IMECandidateCtrl: Changed\n"));
|
||
|
WWASSERT(mCandidate == candidate);
|
||
|
|
||
|
if (candidate)
|
||
|
{
|
||
|
mScrollPos = candidate->GetPageStart();
|
||
|
|
||
|
unsigned long candSel = candidate->GetSelection();
|
||
|
|
||
|
if (candSel != (unsigned long)mCurrSel)
|
||
|
{
|
||
|
mCurrSel = candSel;
|
||
|
UpdateScrollPos();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Set_Dirty();
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::Reset
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
* NONE
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::Reset(void)
|
||
|
{
|
||
|
mCellSize.Set(0,0);
|
||
|
mCurrSel = -1;
|
||
|
mScrollPos = 0;
|
||
|
mCellsPerPage = 0;
|
||
|
mScrollBarCtrl.Show(false);
|
||
|
mCandidate = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::CreateControlRenderer
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
* NONE
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::CreateControlRenderer(void)
|
||
|
{
|
||
|
Render2DClass& renderer = mControlRenderer;
|
||
|
|
||
|
// Configure this renderer
|
||
|
renderer.Reset();
|
||
|
renderer.Enable_Texturing(false);
|
||
|
renderer.Set_Coordinate_Range(Render2DClass::Get_Screen_Resolution());
|
||
|
|
||
|
renderer.Add_Quad(mFullRect, RGBA_TO_INT32(0, 0, 0, 170));
|
||
|
|
||
|
// Determine which color to draw the outline in
|
||
|
int color = StyleMgrClass::Get_Line_Color();
|
||
|
int bkColor = StyleMgrClass::Get_Bk_Color();
|
||
|
|
||
|
if (IsEnabled == false)
|
||
|
{
|
||
|
color = StyleMgrClass::Get_Disabled_Line_Color();
|
||
|
bkColor = StyleMgrClass::Get_Disabled_Bk_Color();
|
||
|
}
|
||
|
|
||
|
// Draw the control outline
|
||
|
RectClass rect = Rect;
|
||
|
renderer.Add_Outline(rect, 1.0F, color);
|
||
|
|
||
|
// Now draw the background
|
||
|
rect.Right -= 1;
|
||
|
rect.Bottom -= 1;
|
||
|
renderer.Add_Quad(rect, bkColor);
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::CreateTextRenderer
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
* NONE
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::CreateTextRenderer(void)
|
||
|
{
|
||
|
mHilightRenderer.Reset();
|
||
|
mHilightRenderer.Set_Coordinate_Range(Render2DClass::Get_Screen_Resolution());
|
||
|
StyleMgrClass::Configure_Hilighter(&mHilightRenderer);
|
||
|
|
||
|
mTextRenderer.Reset();
|
||
|
|
||
|
if (mCandidate)
|
||
|
{
|
||
|
// Add each candidate to the list
|
||
|
const unsigned int selIndexBias = (mCandidate->IsStartFrom1() ? 1 : 0);
|
||
|
|
||
|
float currYPos = ClientRect.Top;
|
||
|
const unsigned long candidateCount = mCandidate->GetCount();
|
||
|
|
||
|
for (unsigned long index = mScrollPos; index < candidateCount; ++index)
|
||
|
{
|
||
|
// Build the rectangle we will draw the text into
|
||
|
RectClass textRect;
|
||
|
textRect.Left = ClientRect.Left;
|
||
|
textRect.Top = currYPos;
|
||
|
textRect.Right = (textRect.Left + mCellSize.X);
|
||
|
textRect.Bottom = (textRect.Top + mCellSize.Y);
|
||
|
|
||
|
// If this cell extends past the end of the control then stop
|
||
|
if (textRect.Bottom > ClientRect.Bottom)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Draw the text
|
||
|
const WCHAR* text = mCandidate->GetCandidate(index);
|
||
|
|
||
|
WideStringClass entry(0, true);
|
||
|
entry.Format(L"%d. %s", ((index - mScrollPos) + selIndexBias), text);
|
||
|
|
||
|
StyleMgrClass::Render_Text(entry, &mTextRenderer, textRect, true, true);
|
||
|
|
||
|
// Hilight this entry (if its the currently selected one)
|
||
|
if ((int)index == mCurrSel)
|
||
|
{
|
||
|
StyleMgrClass::Render_Hilight(&mHilightRenderer, textRect);
|
||
|
}
|
||
|
|
||
|
currYPos += mCellSize.Y;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::Render
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
* NONE
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::Render(void)
|
||
|
{
|
||
|
// Recreate the renderers (if necessary)
|
||
|
if (IsDirty)
|
||
|
{
|
||
|
CreateControlRenderer();
|
||
|
CreateTextRenderer();
|
||
|
}
|
||
|
|
||
|
mControlRenderer.Render();
|
||
|
mTextRenderer.Render();
|
||
|
mHilightRenderer.Render();
|
||
|
|
||
|
DialogControlClass::Render();
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::SetCurrSel
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::SetCurrSel(int index)
|
||
|
{
|
||
|
if (mCandidate)
|
||
|
{
|
||
|
if ((index != mCurrSel) && (index == -1) || ((index >= 0) && (unsigned long)index < mCandidate->GetCount()))
|
||
|
{
|
||
|
mCurrSel = index;
|
||
|
Set_Dirty();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::EntryFromPos
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
*
|
||
|
* RESULT
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
int IMECandidateCtrl::EntryFromPos(const Vector2& mousePos)
|
||
|
{
|
||
|
if (mCandidate)
|
||
|
{
|
||
|
// Loop over all the entries in our current view
|
||
|
float currYPos = ClientRect.Top;
|
||
|
|
||
|
const unsigned long candidateCount = mCandidate->GetCount();
|
||
|
|
||
|
for (unsigned long index = mScrollPos; index < candidateCount; ++index)
|
||
|
{
|
||
|
// Is ths mouse over this entry?
|
||
|
if ((mousePos.Y >= currYPos && mousePos.Y <= (currYPos + mCellSize.Y))
|
||
|
|| mousePos.Y > ClientRect.Bottom)
|
||
|
{
|
||
|
return index;
|
||
|
}
|
||
|
|
||
|
currYPos += mCellSize.Y;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::UpdateScrollPos
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
* NONE
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::UpdateScrollPos(void)
|
||
|
{
|
||
|
if (mCurrSel != -1)
|
||
|
{
|
||
|
unsigned int scrollPos = mScrollPos;
|
||
|
|
||
|
if (mCurrSel < (int)scrollPos)
|
||
|
{
|
||
|
// Scroll up so the current selection is in view
|
||
|
scrollPos = mCurrSel;
|
||
|
Set_Dirty();
|
||
|
}
|
||
|
else if (mCurrSel >= (int)(scrollPos + mCellsPerPage))
|
||
|
{
|
||
|
// Scroll down so the current selection is in view
|
||
|
scrollPos = max<unsigned int>((unsigned int)mCurrSel - (mCellsPerPage - 1), 0);
|
||
|
Set_Dirty();
|
||
|
}
|
||
|
|
||
|
if (scrollPos != mScrollPos)
|
||
|
{
|
||
|
// Update the scrollbar
|
||
|
mScrollBarCtrl.Set_Pos(mScrollPos, false);
|
||
|
|
||
|
if (mCandidate)
|
||
|
{
|
||
|
mCandidate->SetPageStart(mScrollPos);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::Update_Client_Rect
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
* NONE
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::Update_Client_Rect(void)
|
||
|
{
|
||
|
WWDEBUG_SAY(("IMECandidateCtrl: Update_Client_Rect()\n"));
|
||
|
|
||
|
Vector2 pageSize;
|
||
|
CalculateCandidatePageExtent(pageSize, mCellSize);
|
||
|
|
||
|
mCellsPerPage = (unsigned int)(pageSize.Y / mCellSize.Y);
|
||
|
|
||
|
Rect.Right = (Rect.Left + (pageSize.X + (BORDER_WIDTH * 2.0f)));
|
||
|
Rect.Bottom = (Rect.Top + (pageSize.Y + (BORDER_HEIGHT * 2.0f)));
|
||
|
|
||
|
mFullRect = Rect;
|
||
|
|
||
|
ClientRect = Rect;
|
||
|
ClientRect.Inflate(Vector2(-BORDER_WIDTH, -BORDER_HEIGHT));
|
||
|
|
||
|
if (mCandidate)
|
||
|
{
|
||
|
WWASSERT(mCandidate->GetPageSize() <= mCellsPerPage);
|
||
|
|
||
|
// Do we need to show a scroll bar?
|
||
|
const unsigned long candidateCount = mCandidate->GetCount();
|
||
|
|
||
|
if ((unsigned long)mCellsPerPage < candidateCount)
|
||
|
{
|
||
|
// Position the scrollbar to the right of the list
|
||
|
RectClass scrollRect;
|
||
|
scrollRect.Left = mFullRect.Right;
|
||
|
scrollRect.Top = mFullRect.Top;
|
||
|
scrollRect.Right = -1;
|
||
|
scrollRect.Bottom = mFullRect.Bottom;
|
||
|
|
||
|
// Size the scroll bar
|
||
|
mScrollBarCtrl.Set_Window_Rect(scrollRect);
|
||
|
mScrollBarCtrl.Set_Page_Size(mCellsPerPage - 1);
|
||
|
mScrollBarCtrl.Set_Range(0, (candidateCount - mCellsPerPage));
|
||
|
mScrollBarCtrl.Show(true);
|
||
|
|
||
|
const RectClass& rect = mScrollBarCtrl.Get_Window_Rect();
|
||
|
mFullRect.Right = rect.Right;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Set_Dirty();
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::CalculateCandidatePageExtent(Vector2& outExtent, Vector2& outCellSize)
|
||
|
{
|
||
|
if (mCandidate)
|
||
|
{
|
||
|
// Get the size of the widest candidate string.
|
||
|
float maxCandWidth = 0.0f;
|
||
|
const unsigned long candidateCount = mCandidate->GetCount();
|
||
|
|
||
|
for (unsigned long index = 0; index < candidateCount; ++index)
|
||
|
{
|
||
|
// Get the extent of the current entry
|
||
|
const WCHAR* text = mCandidate->GetCandidate(index);
|
||
|
Vector2 textExtent = mTextRenderer.Get_Text_Extents(text);
|
||
|
|
||
|
if (textExtent.X > maxCandWidth)
|
||
|
{
|
||
|
maxCandWidth = textExtent.X;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Vector2 charSize = mTextRenderer.Get_Text_Extents(L"W");
|
||
|
|
||
|
// The cell size is the maximum candidate size plus some spacing.
|
||
|
outCellSize.X = (maxCandWidth + (charSize.X * 3.0f));
|
||
|
outCellSize.Y = (charSize.Y * 1.25f);
|
||
|
|
||
|
outExtent.X = outCellSize.X;
|
||
|
outExtent.Y = (mCellSize.Y * (float)mCandidate->GetPageSize());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
outExtent.X = 0.0f;
|
||
|
outExtent.Y = 0.0f;
|
||
|
|
||
|
outCellSize.X = 0.0f;
|
||
|
outCellSize.Y = 0.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::On_Set_Cursor
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::On_Set_Cursor(const Vector2& mousePos)
|
||
|
{
|
||
|
// Change the mouse cursor if necessary
|
||
|
if (ClientRect.Contains(mousePos))
|
||
|
{
|
||
|
MouseMgrClass::Set_Cursor(MouseMgrClass::CURSOR_ACTION);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::On_LButton_Down
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::On_LButton_Down(const Vector2& mousePos)
|
||
|
{
|
||
|
SetCurrSel(EntryFromPos(mousePos));
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::On_LButton_Up
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::On_LButton_Up(const Vector2& mousePos)
|
||
|
{
|
||
|
int sel = EntryFromPos(mousePos);
|
||
|
SetCurrSel(sel);
|
||
|
|
||
|
// Process candidate selection here
|
||
|
if (mCandidate && (sel >= 0))
|
||
|
{
|
||
|
const wchar_t* string = mCandidate->GetCandidate(sel);
|
||
|
WWDEBUG_SAY(("*** Selected Candidate: %d %04x\n", sel, *string));
|
||
|
mCandidate->SelectCandidate((unsigned long)sel);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::On_Add_To_Dialog
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
* NONE
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::On_Add_To_Dialog(void)
|
||
|
{
|
||
|
Parent->Add_Control(&mScrollBarCtrl);
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::On_Remove_From_Dialog
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
* NONE
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::On_Remove_From_Dialog(void)
|
||
|
{
|
||
|
Parent->Remove_Control(&mScrollBarCtrl);
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
* NAME
|
||
|
* IMECandidateCtrl::On_VScroll
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
*
|
||
|
* INPUTS
|
||
|
*
|
||
|
* RESULT
|
||
|
* NONE
|
||
|
*
|
||
|
******************************************************************************/
|
||
|
|
||
|
void IMECandidateCtrl::On_VScroll(ScrollBarCtrlClass*, int , int newPos)
|
||
|
{
|
||
|
mScrollPos = newPos;
|
||
|
|
||
|
if (mCandidate)
|
||
|
{
|
||
|
mCandidate->SetPageStart(mScrollPos);
|
||
|
}
|
||
|
|
||
|
Set_Dirty();
|
||
|
}
|