476 lines
12 KiB
C++
476 lines
12 KiB
C++
#include "StdAfx.h"
|
|
|
|
namespace ui
|
|
{
|
|
|
|
ListBox::ListBox(Layout* pLayout) :
|
|
ScrollableBox(pLayout),
|
|
m_bScrollSelect(false),
|
|
m_iCurSel(-1),
|
|
m_pCompareFunc(nullptr),
|
|
m_pCompareData(NULL),
|
|
m_bSelNextWhenRemoveActive(true)
|
|
{
|
|
|
|
}
|
|
|
|
void ListBox::SetAttribute(const std::wstring& strName, const std::wstring& strValue)
|
|
{
|
|
if( strName == _T("scrollselect") ) {
|
|
SetScrollSelect(strValue == _T("true"));
|
|
}
|
|
else {
|
|
ScrollableBox::SetAttribute(strName, strValue);
|
|
}
|
|
}
|
|
|
|
void ListBox::HandleMessage(EventArgs& event)
|
|
{
|
|
if (!IsMouseEnabled() && event.Type > kEventMouseBegin && event.Type < kEventMouseEnd) {
|
|
if (m_pParent != NULL) m_pParent->HandleMessageTemplate(event);
|
|
else ScrollableBox::HandleMessage(event);
|
|
return;
|
|
}
|
|
|
|
switch (event.Type) {
|
|
case kEventMouseButtonDown:
|
|
case kEventMouseButtonUp:
|
|
return;
|
|
case kEventKeyDown:
|
|
switch (event.chKey) {
|
|
case VK_UP:
|
|
SelectItem(FindSelectable(m_iCurSel - 1, false), true);
|
|
return;
|
|
case VK_DOWN:
|
|
SelectItem(FindSelectable(m_iCurSel + 1, true), true);
|
|
return;
|
|
case VK_HOME:
|
|
SelectItem(FindSelectable(0, false), true);
|
|
return;
|
|
case VK_END:
|
|
SelectItem(FindSelectable(GetCount() - 1, true), true);
|
|
return;
|
|
}
|
|
break;
|
|
case kEventMouseScrollWheel:
|
|
{
|
|
int detaValue = event.wParam;
|
|
if (detaValue > 0) {
|
|
if (m_bScrollSelect) {
|
|
SelectItem(FindSelectable(m_iCurSel - 1, false), true);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
else {
|
|
if (m_bScrollSelect) {
|
|
SelectItem(FindSelectable(m_iCurSel + 1, true), true);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
ScrollableBox::HandleMessage(event);
|
|
}
|
|
|
|
void ListBox::HandleMessageTemplate(EventArgs& event)
|
|
{
|
|
ScrollableBox::HandleMessageTemplate(event);
|
|
}
|
|
|
|
int ListBox::GetCurSel() const
|
|
{
|
|
return m_iCurSel;
|
|
}
|
|
void ListBox::SelectNextWhenActiveRemoved(bool bSelectNextItem)
|
|
{
|
|
m_bSelNextWhenRemoveActive = bSelectNextItem;
|
|
}
|
|
bool ListBox::SelectItem(int iIndex, bool bTakeFocus, bool bTrigger)
|
|
{
|
|
//if( iIndex == m_iCurSel ) return true;
|
|
int iOldSel = m_iCurSel;
|
|
// We should first unselect the currently selected item
|
|
if (m_iCurSel >= 0) {
|
|
Control* pControl = GetItemAt(m_iCurSel);
|
|
if (pControl != NULL) {
|
|
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(pControl);
|
|
if (pListItem != NULL) pListItem->OptionTemplate<Box>::Selected(false, bTrigger);
|
|
}
|
|
|
|
m_iCurSel = -1;
|
|
}
|
|
if (iIndex < 0) return false;
|
|
|
|
Control* pControl = GetItemAt(iIndex);
|
|
if (pControl == NULL) return false;
|
|
if (!pControl->IsVisible()) return false;
|
|
if (!pControl->IsEnabled()) return false;
|
|
|
|
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(pControl);
|
|
if (pListItem == NULL) return false;
|
|
m_iCurSel = iIndex;
|
|
pListItem->OptionTemplate<Box>::Selected(true, bTrigger);
|
|
|
|
if (GetItemAt(m_iCurSel)) {
|
|
UiRect rcItem = GetItemAt(m_iCurSel)->GetPos();
|
|
EnsureVisible(rcItem);
|
|
}
|
|
|
|
if (bTakeFocus) pControl->SetFocus();
|
|
if (m_pWindow != NULL && bTrigger) {
|
|
m_pWindow->SendNotify(this, kEventSelect, m_iCurSel, iOldSel);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ListBox::EnsureVisible(const UiRect& rcItem)
|
|
{
|
|
UiRect rcNewItem = rcItem;
|
|
rcNewItem.Offset(-GetScrollPos().cx, -GetScrollPos().cy);
|
|
UiRect rcList = GetPos();
|
|
UiRect rcListInset = m_pLayout->GetPadding();
|
|
|
|
rcList.left += rcListInset.left;
|
|
rcList.top += rcListInset.top;
|
|
rcList.right -= rcListInset.right;
|
|
rcList.bottom -= rcListInset.bottom;
|
|
|
|
ScrollBar* pHorizontalScrollBar = GetHorizontalScrollBar();
|
|
if (pHorizontalScrollBar && pHorizontalScrollBar->IsVisible()) rcList.bottom -= pHorizontalScrollBar->GetFixedHeight();
|
|
|
|
if (rcNewItem.left >= rcList.left && rcNewItem.top >= rcList.top
|
|
&& rcNewItem.right <= rcList.right && rcNewItem.bottom <= rcList.bottom) {
|
|
if (m_pParent && dynamic_cast<ListContainerElement*>(m_pParent) != NULL) {
|
|
dynamic_cast<ListContainerElement*>(m_pParent)->GetOwner()->EnsureVisible(rcNewItem);
|
|
}
|
|
return;
|
|
}
|
|
|
|
int dx = 0;
|
|
if (rcNewItem.left < rcList.left) dx = rcNewItem.left - rcList.left;
|
|
if (rcNewItem.right > rcList.right) dx = rcNewItem.right - rcList.right;
|
|
int dy = 0;
|
|
if (rcNewItem.top < rcList.top) dy = rcNewItem.top - rcList.top;
|
|
if (rcNewItem.bottom > rcList.bottom) dy = rcNewItem.bottom - rcList.bottom;
|
|
|
|
CSize sz = GetScrollPos();
|
|
SetScrollPos(CSize(sz.cx + dx, sz.cy + dy));
|
|
}
|
|
|
|
void ListBox::StopScroll()
|
|
{
|
|
m_scrollAnimation.Reset();
|
|
}
|
|
|
|
bool ListBox::ButtonDown(EventArgs& msg)
|
|
{
|
|
bool ret = __super::ButtonDown(msg);
|
|
StopScroll();
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool ListBox::ScrollItemToTop(const std::wstring& strItemName)
|
|
{
|
|
for (auto it = m_items.begin(); it != m_items.end(); it++) {
|
|
if ((*it)->GetName() == strItemName) {
|
|
if (GetScrollRange().cy != 0) {
|
|
CSize scrollPos = GetScrollPos();
|
|
scrollPos.cy = (*it)->GetPos().top - m_pLayout->GetInternalPos().top;
|
|
if (scrollPos.cy >= 0) {
|
|
SetScrollPos(scrollPos);
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Control* ListBox::GetTopItem()
|
|
{
|
|
int listTop = GetPos().top + m_pLayout->GetPadding().top + GetScrollPos().cy;
|
|
for (auto it = m_items.begin(); it != m_items.end(); it++) {
|
|
if ((*it)->IsVisible() && !(*it)->IsFloat() && (*it)->GetPos().bottom >= listTop) {
|
|
return (*it);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool ListBox::SetItemIndex(Control* pControl, std::size_t iIndex)
|
|
{
|
|
int iOrginIndex = GetItemIndex(pControl);
|
|
if( iOrginIndex == -1 ) return false;
|
|
if( iOrginIndex == (int)iIndex ) return true;
|
|
|
|
ListContainerElement* pSelectedListItem = NULL;
|
|
if( m_iCurSel >= 0 ) pSelectedListItem = dynamic_cast<ListContainerElement*>(GetItemAt(m_iCurSel));
|
|
if( !ScrollableBox::SetItemIndex(pControl, iIndex) ) return false;
|
|
std::size_t iMinIndex = min((std::size_t)iOrginIndex, iIndex);
|
|
std::size_t iMaxIndex = max((std::size_t)iOrginIndex, iIndex);
|
|
for(std::size_t i = iMinIndex; i < iMaxIndex + 1; ++i) {
|
|
Control* pControl = GetItemAt(i);
|
|
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(pControl);
|
|
if( pListItem != NULL ) {
|
|
pListItem->SetIndex((int)i);
|
|
}
|
|
}
|
|
if( m_iCurSel >= 0 && pSelectedListItem != NULL ) m_iCurSel = pSelectedListItem->GetIndex();
|
|
return true;
|
|
}
|
|
|
|
void ListBox::Previous()
|
|
{
|
|
if (m_iCurSel > 0) {
|
|
SelectItem(m_iCurSel - 1);
|
|
}
|
|
}
|
|
|
|
void ListBox::Next()
|
|
{
|
|
int count = GetCount();
|
|
if (m_iCurSel < count - 1) {
|
|
SelectItem(m_iCurSel + 1);
|
|
}
|
|
}
|
|
|
|
void ListBox::ActiveItem()
|
|
{
|
|
if (m_iCurSel >= 0) {
|
|
ListContainerElement* item = dynamic_cast<ListContainerElement*>( GetItemAt(m_iCurSel) );
|
|
item->InvokeDoubleClickEvent();
|
|
}
|
|
}
|
|
|
|
bool ListBox::Add(Control* pControl)
|
|
{
|
|
// Override the Add() method so we can add items specifically to
|
|
// the intended widgets. Headers are assumed to be
|
|
// answer the correct interface so we can add multiple list headers.
|
|
// The list items should know about us
|
|
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(pControl);
|
|
if( pListItem != NULL ) {
|
|
pListItem->SetOwner(this);
|
|
pListItem->SetIndex(GetCount());
|
|
}
|
|
return ScrollableBox::Add(pControl);
|
|
}
|
|
|
|
bool ListBox::AddAt(Control* pControl, int iIndex)
|
|
{
|
|
// Override the AddAt() method so we can add items specifically to
|
|
// the intended widgets. Headers and are assumed to be
|
|
// answer the correct interface so we can add multiple list headers.
|
|
|
|
if (!ScrollableBox::AddAt(pControl, iIndex)) return false;
|
|
|
|
// The list items should know about us
|
|
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(pControl);
|
|
if( pListItem != NULL ) {
|
|
pListItem->SetOwner(this);
|
|
pListItem->SetIndex(iIndex);
|
|
}
|
|
|
|
for(int i = iIndex + 1; i < GetCount(); ++i) {
|
|
Control* p = GetItemAt(i);
|
|
pListItem = dynamic_cast<ListContainerElement*>(p);
|
|
if( pListItem != NULL ) {
|
|
pListItem->SetIndex(i);
|
|
}
|
|
}
|
|
if( m_iCurSel >= iIndex ) m_iCurSel += 1;
|
|
return true;
|
|
}
|
|
|
|
bool ListBox::Remove(Control* pControl)
|
|
{
|
|
int iIndex = GetItemIndex(pControl);
|
|
if (iIndex == -1) return false;
|
|
|
|
return RemoveAt(iIndex);
|
|
}
|
|
|
|
bool ListBox::RemoveAt(int iIndex)
|
|
{
|
|
if (!ScrollableBox::RemoveAt(iIndex)) return false;
|
|
|
|
for(int i = iIndex; i < GetCount(); ++i) {
|
|
Control* p = GetItemAt(i);
|
|
ListContainerElement* pListItem = dynamic_cast<ListContainerElement*>(p);
|
|
if( pListItem != NULL ) pListItem->SetIndex(i);
|
|
}
|
|
|
|
if( iIndex == m_iCurSel && m_iCurSel >= 0 ) {
|
|
if (m_bSelNextWhenRemoveActive)
|
|
SelectItem(FindSelectable(m_iCurSel--, false));
|
|
else
|
|
m_iCurSel = -1;
|
|
}
|
|
else if( iIndex < m_iCurSel ) m_iCurSel -= 1;
|
|
return true;
|
|
}
|
|
|
|
void ListBox::RemoveAll()
|
|
{
|
|
m_iCurSel = -1;
|
|
ScrollableBox::RemoveAll();
|
|
}
|
|
|
|
bool ListBox::SortItems(PULVCompareFunc pfnCompare, UINT_PTR dwData)
|
|
{
|
|
if (!pfnCompare)
|
|
return false;
|
|
|
|
if (m_items.size() == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
m_pCompareFunc = pfnCompare;
|
|
m_pCompareData = dwData;
|
|
qsort_s(&(*m_items.begin()), m_items.size(), sizeof(Control*), ListBox::ItemComareFunc, this);
|
|
ListContainerElement *pItem = NULL;
|
|
for (int i = 0; i < (int)m_items.size(); ++i)
|
|
{
|
|
pItem = dynamic_cast<ListContainerElement*>(static_cast<Control*>(m_items[i]));
|
|
if (pItem) {
|
|
pItem->SetIndex(i);
|
|
pItem->Selected(false, true);
|
|
}
|
|
}
|
|
SelectItem(-1);
|
|
SetPos(GetPos());
|
|
Invalidate();
|
|
|
|
return true;
|
|
}
|
|
|
|
int __cdecl ListBox::ItemComareFunc(void *pvlocale, const void *item1, const void *item2)
|
|
{
|
|
ListBox *pThis = (ListBox*)pvlocale;
|
|
if (!pThis || !item1 || !item2)
|
|
return 0;
|
|
return pThis->ItemComareFunc(item1, item2);
|
|
}
|
|
|
|
int __cdecl ListBox::ItemComareFunc(const void *item1, const void *item2)
|
|
{
|
|
Control *pControl1 = *(Control**)item1;
|
|
Control *pControl2 = *(Control**)item2;
|
|
return m_pCompareFunc((UINT_PTR)pControl1, (UINT_PTR)pControl2, m_pCompareData);
|
|
}
|
|
|
|
bool ListBox::GetScrollSelect()
|
|
{
|
|
return m_bScrollSelect;
|
|
}
|
|
|
|
void ListBox::SetScrollSelect(bool bScrollSelect)
|
|
{
|
|
m_bScrollSelect = bScrollSelect;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
ListContainerElement::ListContainerElement() :
|
|
m_iIndex(-1),
|
|
m_pOwner(nullptr)
|
|
{
|
|
m_uTextStyle = DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_NOCLIP | DT_SINGLELINE;
|
|
SetReceivePointerMsg(false);
|
|
}
|
|
|
|
void ListContainerElement::SetVisible(bool bVisible)
|
|
{
|
|
__super::SetVisible(bVisible);
|
|
if (!IsVisible() && m_bSelected) {
|
|
m_bSelected = false;
|
|
if (m_pOwner != NULL) m_pOwner->SelectItem(-1);
|
|
}
|
|
}
|
|
|
|
void ListContainerElement::Selected(bool bSelected, bool trigger)
|
|
{
|
|
if (!IsEnabled()) return;
|
|
if (bSelected && m_pOwner != NULL) m_pOwner->SelectItem(m_iIndex, false, trigger);
|
|
}
|
|
|
|
void ListContainerElement::HandleMessage(EventArgs& event)
|
|
{
|
|
if (!IsMouseEnabled() && event.Type > kEventMouseBegin && event.Type < kEventMouseEnd) {
|
|
if (m_pOwner != NULL) m_pOwner->HandleMessageTemplate(event);
|
|
else Box::HandleMessage(event);
|
|
return;
|
|
}
|
|
else if (event.Type == kEventInternalDoubleClick) {
|
|
if (IsActivatable()) {
|
|
InvokeDoubleClickEvent();
|
|
}
|
|
return;
|
|
}
|
|
else if (event.Type == kEventKeyDown && IsEnabled()) {
|
|
if (event.chKey == VK_RETURN) {
|
|
if (IsActivatable()) {
|
|
if (m_pWindow != NULL) m_pWindow->SendNotify(this, kEventReturn);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
else if (event.Type == kEventInternalMenu && IsEnabled()) {
|
|
Selected(true, true);
|
|
m_pWindow->SendNotify(this, kEventMouseMenu);
|
|
Invalidate();
|
|
|
|
return;
|
|
}
|
|
|
|
__super::HandleMessage(event);
|
|
|
|
// An important twist: The list-item will send the event not to its immediate
|
|
// parent but to the "attached" list. A list may actually embed several components
|
|
// in its path to the item, but key-presses etc. needs to go to the actual list.
|
|
//if( m_pOwner != NULL ) m_pOwner->HandleMessage(event); else Control::HandleMessage(event);
|
|
}
|
|
|
|
IListOwner* ListContainerElement::GetOwner()
|
|
{
|
|
return m_pOwner;
|
|
}
|
|
|
|
void ListContainerElement::SetOwner(IListOwner* pOwner)
|
|
{
|
|
m_pOwner = pOwner;
|
|
}
|
|
|
|
int ListContainerElement::GetIndex() const
|
|
{
|
|
return m_iIndex;
|
|
}
|
|
|
|
void ListContainerElement::SetIndex(int iIndex)
|
|
{
|
|
m_iIndex = iIndex;
|
|
}
|
|
void ListContainerElement::InvokeDoubleClickEvent()
|
|
{
|
|
if( m_pWindow != NULL ) m_pWindow->SendNotify(this, kEventMouseDoubleClick);
|
|
}
|
|
|
|
} // namespace ui
|