#include "pch.h"
class DefaultItemPainter : public ItemPainter {
public:
int GetXSize()
{
return 1;
}
int GetYSize()
{
return 1;
}
void Paint(ItemID pitem, Surface* psurface, bool bSelected, bool bFocus)
{
}
};
template<class Interface>
class ListPaneImpl :
public Interface, public IEventSink,
public IIntegerEventSink
{
private:
TRef<List> m_plist;
TRef<ItemPainter> m_ppainter;
TRef<ScrollPane> m_pscroll;
ItemID m_pitemSelection;
ItemID m_pitemSelectionPrevious;
TRef<IEventSink> m_peventSink;
TRef<IIntegerEventSink> m_pintegerEventSink;
TRef<IItemEvent::SourceImpl> m_peventSelection;
TRef<EventSourceImpl> m_peventSingleClick;
TRef<EventSourceImpl> m_peventSingleRightClick;
TRef<EventSourceImpl> m_peventDoubleClick;
TRef<EventSourceImpl> m_peventDoubleRightClick;
int m_indexSelection;
int m_posScroll;
WinPoint m_sizeMin;
bool m_bHorizontal;
TVector<ItemID> m_vectItemsOld;
bool m_bNeedScrollUpdate;
bool m_bNeedSelectionOnScreen;
int m_iOldSelection;
public:
ListPaneImpl(
const WinPoint& sizeMin,
List* plist,
ItemPainter* ppainter,
ScrollPane* pscroll,
bool bHorizontal
) :
m_sizeMin(sizeMin),
m_ppainter(ppainter),
m_pscroll(pscroll),
m_indexSelection(0),
m_posScroll(0),
m_pitemSelection(NULL),
m_pitemSelectionPrevious(NULL),
m_bHorizontal(bHorizontal),
m_iOldSelection(-1),
m_bNeedScrollUpdate(false),
m_bNeedSelectionOnScreen(false)
{
m_peventSelection = new IItemEvent::SourceImpl();
m_peventSingleClick = new EventSourceImpl();
m_peventDoubleClick = new EventSourceImpl();
m_peventSingleRightClick = new EventSourceImpl();
m_peventDoubleRightClick = new EventSourceImpl();
if (m_ppainter == NULL) {
m_ppainter = new DefaultItemPainter();
}
if (m_pscroll) {
m_pscroll->GetEventSource()->AddSink(
m_pintegerEventSink = IIntegerEventSink::CreateDelegate(this)
);
}
if (plist == NULL) {
SetList(new EmptyList());
} else {
SetList(plist);
}
UpdateScrollBar();
}
~ListPaneImpl()
{
m_plist->GetChangedEvent()->RemoveSink(m_peventSink);
if (m_pscroll) {
m_pscroll->GetEventSource()->RemoveSink(m_pintegerEventSink);
}
}
const int GetSignificantSize() {
if (m_bHorizontal)
{
return GetSize().X();
}
else
{
return GetSize().Y();
}
}
const int GetSignificantSize(ItemPainter & itempainter) {
if (m_bHorizontal)
{
return itempainter.GetXSize();
}
else
{
return itempainter.GetYSize();
}
}
int GetScrollPos()
{
return m_posScroll;
}
void SetScrollPos(int pos)
{
m_posScroll =
max(min(pos, m_plist->GetCount() * GetSignificantSize(*m_ppainter) - GetSignificantSize()), 0);
if (m_pscroll) {
m_pscroll->SetPos(m_posScroll);
}
}
void UpdateScrollBar()
{
if (m_pscroll) {
m_pscroll->SetLineSize(GetSignificantSize(*m_ppainter)); m_pscroll->SetSizes(
m_plist->GetCount() * GetSignificantSize(*m_ppainter),
GetSignificantSize() );
}
}
ScrollPane * GetScrollPane()
{
return m_pscroll;
}
void UpdateScrollPos()
{
m_bNeedScrollUpdate = true;
}
void NextItem()
{
if (m_indexSelection < m_plist->GetCount() - 1) {
m_indexSelection++;
m_pitemSelection = m_plist->GetNext(m_pitemSelection);
NeedPaint();
SelectionChanged();
m_bNeedScrollUpdate = true;
m_bNeedSelectionOnScreen = true;
}
}
void PreviousItem()
{
if (m_indexSelection != 0) {
m_indexSelection--;
m_pitemSelection = m_plist->GetItem(m_indexSelection);
NeedPaint();
SelectionChanged();
m_bNeedScrollUpdate = true;
m_bNeedSelectionOnScreen = true;
}
}
void PageUp()
{
int delta = GetSignificantSize() / GetSignificantSize(*m_ppainter);
while (delta > 0) {
PreviousItem();
delta--;
}
}
void PageDown()
{
int delta = GetSignificantSize() / GetSignificantSize(*m_ppainter);
while (delta > 0) {
NextItem();
delta--;
}
}
void SelectionChanged()
{
if (m_pitemSelectionPrevious != m_pitemSelection) {
m_peventSelection->Trigger(m_pitemSelection);
m_pitemSelectionPrevious = m_pitemSelection;
}
}
void ListChanged()
{
int index;
if (m_pitemSelection != NULL)
index = m_plist->GetIndex(m_pitemSelection);
else
index = -1;
if (index == -1)
{
m_pitemSelection = NULL;
m_iOldSelection = -1;
}
m_indexSelection = index;
UpdateScrollBar();
UpdateScrollPos();
NeedLayout();
NeedPaint();
SelectionChanged();
}
void ScrollBarChanged()
{
m_posScroll = m_pscroll->GetPos();
NeedPaint();
}
void SetList(List* plist)
{
if (plist != m_plist) {
if (m_plist != NULL) {
m_plist->GetChangedEvent()->RemoveSink(m_peventSink);
}
if (plist == NULL) {
m_plist = new EmptyList();
} else {
m_plist = plist;
}
m_plist->GetChangedEvent()->AddSink(
m_peventSink = IEventSink::CreateDelegate(this)
);
ListChanged();
}
}
void SetItemPainter(ItemPainter* ppainter)
{
if (ppainter == NULL) {
m_ppainter = new DefaultItemPainter();
} else {
m_ppainter = ppainter;
}
UpdateScrollBar();
UpdateScrollPos();
NeedLayout();
NeedPaint();
}
void SetSelection(ItemID pitem)
{
if (pitem != m_pitemSelection)
{
if (pitem)
{
m_indexSelection = m_plist->GetIndex(pitem);
if (m_indexSelection == -1)
{
assert(false);
pitem = NULL;
}
}
else
m_indexSelection = -1;
m_pitemSelection = pitem;
if (m_bNeedScrollUpdate)
m_iOldSelection = -1;
else
m_iOldSelection = m_indexSelection;
SelectionChanged();
NeedPaint();
}
}
void SetSelection(int nIndex)
{
SetSelection(m_plist->GetItem(nIndex));
}
virtual ItemID GetSelection()
{
return m_pitemSelection;
}
IItemEvent::Source* GetSelectionEventSource()
{
return m_peventSelection;
}
IEventSource* GetDoubleClickEventSource()
{
return m_peventDoubleClick;
}
IEventSource* GetDoubleRightClickEventSource()
{
return m_peventDoubleRightClick;
}
IEventSource* GetSingleClickEventSource()
{
return m_peventSingleClick;
}
IEventSource* GetSingleRightClickEventSource()
{
return m_peventSingleRightClick;
}
void ScrollToItem(ItemID pitem)
{
int index = 0;
if (pitem != NULL)
index = m_plist->GetIndex(pitem);
SetScrollPos(((2 * index + 1) * GetSignificantSize(*m_ppainter) - GetSignificantSize())/2);
}
void ForceRefresh()
{
NeedPaint();
}
bool OnEvent(IEventSource* pevent)
{
ListChanged();
return true;
}
bool OnEvent(IIntegerEventSource* pevent, int value)
{
ScrollBarChanged();
return true;
}
void UpdateLayout()
{
InternalSetSize(
WinPoint(
max(m_ppainter->GetXSize(), m_sizeMin.X()),
max( GetExpand().Y(), m_sizeMin.Y())
)
);
UpdateScrollBar();
}
void VerifyScrollPos()
{
if (m_bNeedScrollUpdate) {
m_bNeedScrollUpdate = false;
if (m_bNeedSelectionOnScreen) {
m_bNeedSelectionOnScreen = false;
int nItemSize = GetSignificantSize(*m_ppainter);
int ySelection = m_indexSelection * nItemSize - GetScrollPos();
if (ySelection + GetSignificantSize(*m_ppainter) >= GetSignificantSize()) {
ySelection = GetSignificantSize() - GetSignificantSize(*m_ppainter);
}
if (ySelection < 0) {
ySelection = 0;
}
if (m_pscroll && m_plist->GetCount() * GetSignificantSize(*m_ppainter) > GetSignificantSize()) {
SetScrollPos(m_indexSelection * GetSignificantSize(*m_ppainter) - ySelection);
} else {
SetScrollPos(0);
}
} else {
int nItemSize = GetSignificantSize(*m_ppainter);
int iOldTop = GetScrollPos() / nItemSize;
int nLinesPerScreen = GetSignificantSize() / nItemSize;
int iOldBasis;
if (
m_iOldSelection >= iOldTop
&& m_iOldSelection < iOldTop + nLinesPerScreen
) {
iOldBasis = m_iOldSelection;
} else {
iOldBasis = iOldTop;
}
int nTopFromBasis = GetScrollPos() - iOldBasis * nItemSize;
int iNewBasis = FindNearestNewIndex(iOldBasis);
SetScrollPos(iNewBasis * nItemSize + nTopFromBasis);
}
int nNewCount = m_plist->GetCount();
m_vectItemsOld.SetCount(nNewCount);
for (int i = 0; i < nNewCount; i++) {
m_vectItemsOld.Set(i, m_plist->GetItem(i));
}
m_iOldSelection = m_indexSelection;
}
}
int FindNearestNewIndex(int nOldIndex)
{
if (nOldIndex == -1 || m_vectItemsOld.GetCount() == 0)
return -1;
ZAssert(nOldIndex >= 0 && nOldIndex < m_vectItemsOld.GetCount());
int nNewIndex;
nNewIndex = m_plist->GetIndex(m_vectItemsOld[nOldIndex]);
int iDelta = 1;
while (nNewIndex == -1
&& (nOldIndex - iDelta >= 0
|| nOldIndex + iDelta < m_vectItemsOld.GetCount()))
{
if (nOldIndex - iDelta >= 0)
nNewIndex = m_plist->GetIndex(m_vectItemsOld[nOldIndex - iDelta]);
if (nNewIndex != -1 && nOldIndex + iDelta < m_vectItemsOld.GetCount())
nNewIndex = m_plist->GetIndex(m_vectItemsOld[nOldIndex - iDelta]);
++iDelta;
}
if (nNewIndex == -1)
{
nNewIndex = 0;
}
return nNewIndex;
}
void Paint(Surface* psurface)
{
VerifyScrollPos();
int ysize = GetSignificantSize(*m_ppainter);
int index = GetScrollPos() / ysize;
int y = index * ysize - GetScrollPos();
ItemID pitem = m_plist->GetItem(index);
while (
pitem != NULL
&& y < GetSignificantSize()
) {
if (m_bHorizontal)
psurface->Offset(WinPoint(y, 0));
else
psurface->Offset(WinPoint(0, y));
m_ppainter->Paint(
pitem,
psurface,
pitem == m_pitemSelection,
false
);
if (m_bHorizontal)
psurface->Offset(WinPoint(-y, 0));
else
psurface->Offset(WinPoint(0, -y));
y += ysize;
pitem = m_plist->GetNext(pitem);
}
}
MouseResult Button(
IInputProvider* pprovider,
const Point& point,
int button,
bool bCaptured,
bool bInside,
bool bDown
) {
VerifyScrollPos();
if (button == 0 && bDown) {
int nSize = GetSignificantSize(*m_ppainter);
int nSignificantMouseDim = (int)(m_bHorizontal ? point.X(): point.Y());
SetSelection(m_plist->GetItem((nSignificantMouseDim + GetScrollPos()) / nSize));
NeedPaint();
SelectionChanged();
if (pprovider->IsDoubleClick()) {
m_peventDoubleClick->Trigger();
}
else {
m_peventSingleClick->Trigger();
}
}
else if(button == 1 && bDown)
{
int nSize = GetSignificantSize(*m_ppainter);
int nSignificantMouseDim = (int)(m_bHorizontal ? point.X(): point.Y());
SetSelection(m_plist->GetItem((nSignificantMouseDim + GetScrollPos()) / nSize));
NeedPaint();
SelectionChanged();
if (pprovider->IsDoubleClick()) {
m_peventDoubleRightClick->Trigger();
}
else {
m_peventSingleRightClick->Trigger();
}
}
return MouseResult();
}
bool OnKey(IInputProvider* pprovider, const KeyState& ks, bool& fForceTranslate)
{
VerifyScrollPos();
if (ks.bDown) {
switch (ks.vk) {
case VK_DOWN:
NextItem();
SelectionChanged();
return true;
case VK_UP:
PreviousItem();
SelectionChanged();
return true;
case VK_PRIOR: PageUp();
SelectionChanged();
return true;
case VK_NEXT: PageDown();
SelectionChanged();
return true;
}
}
return false;
}
};
TRef<ListPane> CreateListPane(
const WinPoint& sizeMin,
List* plist,
ItemPainter* ppainter,
ScrollPane* pscroll,
bool bHorizontal
) {
return new ListPaneImpl<ListPane>(sizeMin, plist, ppainter, pscroll, bHorizontal);
}
class StringListImpl :
public StringList
{
private:
TRef<IEngineFont> m_pfont;
Color m_color;
Color m_colorSelected;
int m_xsize;
TRef<EventSourceImpl> m_peventSource;
TRef<StringListItem> m_pitem;
int m_count;
public:
StringListImpl(
IEngineFont* pfont,
const Color& color,
const Color& colorSelected,
int xsize
) :
m_pfont(pfont),
m_color(color),
m_colorSelected(colorSelected),
m_xsize(xsize),
m_count(0)
{
m_peventSource = new EventSourceImpl();
}
void AddItem(const ZString& str)
{
m_count++;
m_pitem = new StringListItem(str, m_pitem);
m_peventSource->Trigger();
}
void SetEmpty()
{
m_count = 0;
m_pitem = NULL;
m_peventSource->Trigger();
}
int GetCount()
{
return m_count;
}
ItemID GetItem(int index)
{
ZAssert(index >= 0);
if (index < m_count) {
StringListItem* pitem = m_pitem;
while (index > 0) {
pitem = pitem->GetNext();
index--;
}
return pitem;
} else {
return NULL;
}
}
int GetIndex(ItemID pitemFind)
{
StringListItem* pitem = m_pitem;
int index = 0;
while (pitem != NULL) {
if ((ItemID)pitem == pitemFind) {
return index;
}
pitem = pitem->GetNext();
index++;
}
return -1;
}
ItemID GetNext(ItemID pitem)
{
return ((StringListItem*)pitem)->GetNext();
}
IEventSource* GetChangedEvent()
{
return m_peventSource;
}
int GetXSize()
{
return m_xsize;
}
int GetYSize()
{
return m_pfont->GetHeight();
}
void Paint(ItemID pitemArg, Surface* psurface, bool bSelected, bool bFocus)
{
StringListItem* pitem = (StringListItem*)pitemArg;
if (bSelected) {
psurface->FillRect(
WinRect(0, 0, GetXSize(), GetYSize()),
m_colorSelected
);
}
psurface->DrawString(m_pfont, m_color, WinPoint(0, 0), pitem->GetString());
}
};
TRef<StringList> CreateStringList(
IEngineFont* pfont,
const Color& color,
const Color& colorSelected,
int xsize
) {
return
new StringListImpl(
pfont,
color,
colorSelected,
xsize
);
}
class StringListPaneImpl:
public ListPaneImpl<StringListPane>
{
public:
StringListPaneImpl(
const WinPoint& sizeMin,
List* plist,
ItemPainter* ppainter,
ScrollPane* pscroll,
bool bHorizontal,
IEngineFont* pfont,
const Color& color,
const Color& colorSelected
) :
ListPaneImpl<StringListPane>(sizeMin, plist, ppainter, pscroll, bHorizontal)
{
m_pStringList = CreateStringList(pfont, color, colorSelected, sizeMin.X());
SetList(m_pStringList);
SetItemPainter(m_pStringList);
}
TRef<StringList> GetStringList()
{
return m_pStringList;
}
private:
TRef<StringList> m_pStringList;
};
TRef<StringListPane> CreateStringListPane(
const WinPoint& sizeMin,
List* plist,
ItemPainter* ppainter,
ScrollPane* pscroll,
bool bHorizontal,
IEngineFont* pfont,
const Color& color,
const Color& colorSelected
)
{
return new StringListPaneImpl(sizeMin, plist, ppainter, pscroll, bHorizontal, pfont, color, colorSelected);
}