#ifndef _CLCDLIST_H_ #define _CLCDLIST_H_ #include "CLCDTextObject.h" #include "CLCDBar.h" #include enum EListEntryType { ROOT = 0,CONTAINER = 1,ITEM = 2}; template class CListEntry { public: CListEntry(CListEntry *pParent) { m_iIndex = -1; m_iEntryCount = 0; m_Position = nullptr; m_pParent = pParent; if(pParent == nullptr) { m_iLevel = 0; m_eType = ROOT; m_pRoot = this; } else m_iLevel = m_pParent->GetLevel()+1; } virtual ~CListEntry() { } int GetLevel() { return m_iLevel; } virtual CListEntry *GetNextEntry() { if(m_pParent == nullptr) return nullptr; return m_pParent->GetNextEntry(this); } virtual CListEntry *GetPreviousEntry() { if(m_pParent == nullptr) return nullptr; return m_pParent->GetPreviousEntry(this); } virtual CListEntry *GetNextEntry(CListEntry*) { return nullptr; } virtual CListEntry *GetPreviousEntry(CListEntry*) { return nullptr; } EListEntryType GetType() { return m_eType; } int GetEntryCount() { return m_iEntryCount; } virtual void UpdateEntryCount() { m_iEntryCount = 0; } void SetRoot(CListEntry* pRoot) { m_pRoot = pRoot; } virtual void DeleteItem(T) { } virtual void DeleteGroup(G) { } CListEntry *GetPosition() { return m_Position; } virtual void SetPosition(CListEntry *pPosition) { m_Position = pPosition; } CListEntry *GetParent() { return m_pParent; } int GetIndex() { return m_iIndex; } void SetIndex(int iIndex) { m_iIndex = iIndex; } protected: int m_iIndex; int m_iEntryCount; int m_iLevel; EListEntryType m_eType; CListEntry *m_pParent; CListEntry *m_pRoot; CListEntry *m_Position; }; template class CListItem : public CListEntry { public: CListItem(CListEntry *pParent,T Entry) : CListEntry(pParent) { m_Item = Entry; m_eType = ITEM; } ~CListItem() { m_pRoot->DeleteItem(GetItemData()); } T GetItemData() { return m_Item; } private: T m_Item; }; template class CListContainer : public CListEntry { public: typedef typename list* >::iterator iterator; typename list* >::iterator end() { return m_Entrys.end(); } typename list* >::iterator begin() { return m_Entrys.begin(); } typename list* >::size_type size() { return m_Entrys.size(); } bool empty() { return m_Entrys.empty(); } CListContainer(CListEntry *pParent) : CListEntry(pParent) { if(m_pParent != nullptr) { m_eType = CONTAINER; m_bOpen = false; } else m_bOpen = true; } ~CListContainer() { if(m_pRoot != nullptr) { m_pRoot->DeleteGroup(GetGroupData()); } Clear(); } void Clear() { list< CListEntry* >::iterator iter = m_Entrys.begin(); while(iter != m_Entrys.end()) { delete *iter; if(m_pRoot && m_pRoot->GetPosition() == *iter) { if(GetType() == ROOT) m_pRoot->SetPosition(nullptr); else m_pRoot->SetPosition(this); } iter++; } m_Entrys.clear(); } void SetGroupData(G GroupData) { m_GroupData = GroupData; } bool IsEmpty() { return m_Entrys.empty(); } CListEntry *GetLastOwnEntry() { if(m_Entrys.empty()) return nullptr; return *(--m_Entrys.end()); } CListEntry *GetLastEntry() { if(!m_Entrys.empty()) { CListEntry *pEntry = *(--m_Entrys.end()); if(pEntry->GetType() == ITEM || !((CListContainer*)pEntry)->IsOpen() || ((CListContainer*)pEntry)->IsEmpty()) return pEntry; return ((CListContainer*)pEntry)->GetLastEntry(); } return nullptr; } CListEntry *GetFirstEntry() { if(!m_Entrys.empty()) return *(m_Entrys.begin()); return nullptr; } CListEntry *GetNextEntry() { if(!IsOpen() || m_Entrys.empty()) { if(!m_pParent) return nullptr; return m_pParent->GetNextEntry(this); } return *m_Entrys.begin(); } CListEntry *GetNextEntry(CListEntry *pEntry) { list< CListEntry* >::iterator iter = m_Entrys.begin(); while(iter != m_Entrys.end()) { if((CListEntry*)(*iter) == pEntry) { if(++iter == m_Entrys.end()) { if(m_pParent == nullptr) return nullptr; return m_pParent->GetNextEntry(this); } else return *iter; } iter++; } return nullptr; } CListEntry *GetPreviousEntry(CListEntry *pEntry) { list< CListEntry* >::iterator iter = m_Entrys.begin(); while(iter != m_Entrys.end()) { if((CListEntry*)(*iter) == pEntry) { if(iter == m_Entrys.begin()) { if(m_pParent == nullptr) return nullptr; return this; } else { iter--; if((*iter)->GetType() == CONTAINER) { CListContainer* pContainer = (CListContainer*)*iter; if(pContainer->IsOpen() && !pContainer->IsEmpty()) return pContainer->GetLastEntry(); } return *iter; } } iter++; } return nullptr; } virtual CListItem *InsertItem(iterator _Where,T Entry) { CListItem *pItem = new CListItem(this,Entry); pItem->SetRoot(m_pRoot); m_Entrys.insert(_Where,pItem); m_pRoot->UpdateEntryCount(); return pItem; } virtual CListContainer *InsertGroup(iterator _Where,G Group) { CListContainer *pGroup = new CListContainer(this); pGroup->SetGroupData(Group); pGroup->SetRoot(m_pRoot); m_Entrys.insert(_Where,(CListEntry*)pGroup); m_pRoot->UpdateEntryCount(); return pGroup; } virtual CListItem * AddItem(T Entry) { return InsertItem(end(),Entry); } virtual CListContainer * AddGroup(G Group) { return InsertGroup(end(),Group); } virtual void RemoveGroup(G Group) { list< CListEntry* >::iterator iter = m_Entrys.begin(); CListContainer *pContainer = nullptr; while(iter != m_Entrys.end()) { if((*iter)->GetType() == CONTAINER) { pContainer = (CListContainer*)(*iter); if(pContainer->GetGroupData() == Group) { pContainer->Clear(); if(m_pRoot && m_pRoot->GetPosition() == *iter) { CListEntry *pPosition = (*iter)->GetPreviousEntry(); if(!pPosition) pPosition = (*iter)->GetNextEntry(); m_pRoot->SetPosition(pPosition); } delete *iter; m_Entrys.erase(iter); if (m_pRoot) m_pRoot->UpdateEntryCount(); return; } } iter++; } } virtual void RemoveItem(T Entry) { list< CListEntry* >::iterator iter = m_Entrys.begin(); CListItem *pItem = nullptr; while(iter != m_Entrys.end()) { if((*iter)->GetType() == ITEM) { pItem = (CListItem*)(*iter); if(pItem->GetItemData() == Entry) { if(m_pRoot && m_pRoot->GetPosition() == *iter) { CListEntry *pPosition = (*iter)->GetPreviousEntry(); if(!pPosition) pPosition = (*iter)->GetNextEntry(); m_pRoot->SetPosition(pPosition); } delete *iter; m_Entrys.erase(iter); if (m_pRoot) m_pRoot->UpdateEntryCount(); return; } } iter++; } } CListContainer *GetGroup(G Group) { list< CListEntry* >::iterator iter = m_Entrys.begin(); CListContainer *pContainer = nullptr; while(iter != m_Entrys.end()) { if((*iter)->GetType() == CONTAINER) { pContainer = (CListContainer*)(*iter); if(pContainer->GetGroupData() == Group) return pContainer; } iter++; } return nullptr; } G GetGroupData() { return m_GroupData; } bool IsOpen() { return m_bOpen; } void ToggleOpen() { m_bOpen = !m_bOpen; if (m_pRoot) { m_pRoot->UpdateEntryCount(); m_pRoot->SetPosition(this); } } void SetOpen(bool bOpen = true) { if(bOpen == m_bOpen) return; m_bOpen = bOpen; if (m_pRoot) { m_pRoot->UpdateEntryCount(); m_pRoot->SetPosition(this); } } void CollapseAll() { list< CListEntry* >::iterator iter = m_Entrys.begin(); CListContainer* pContainer = nullptr; while(iter != m_Entrys.end()) { if((*iter)->GetType() == CONTAINER) { pContainer = (CListContainer*)(*iter); pContainer->CollapseAll(); pContainer->SetOpen(false); } iter++; } } void ExpandAll() { list< CListEntry* >::iterator iter = m_Entrys.begin(); CListContainer* pContainer = nullptr; while(iter != m_Entrys.end()) { if((*iter)->GetType() == CONTAINER) { pContainer = (CListContainer*)(*iter); pContainer->ExpandAll(); pContainer->SetOpen(true); } iter++; } } void UpdateEntryCount() { m_iEntryCount = 0; int iIndex = GetIndex()+1; if(!IsOpen()) return; list< CListEntry* >::iterator iter = m_Entrys.begin(); while(iter != m_Entrys.end()) { (*iter)->SetIndex(iIndex+m_iEntryCount); (*iter)->UpdateEntryCount(); m_iEntryCount += 1+(*iter)->GetEntryCount(); iter++; } if(GetType() == ROOT) { if(GetPosition() == nullptr && !m_Entrys.empty()) SetPosition(*m_Entrys.begin()); else SetPosition(GetPosition()); } } template void sort(_Pr3 _Pred) { m_Entrys.sort(_Pred); UpdateEntryCount(); m_pRoot->SetPosition(m_pRoot->GetPosition()); } private: typename list< CListEntry* > m_Entrys; G m_GroupData; bool m_bOpen; }; template class CLCDList : public CLCDTextObject, public CListContainer { friend CListContainer; friend CListItem; public: //************************************************************************ // Constructor //************************************************************************ CLCDList() : CListContainer(nullptr) { m_pScrollbar = nullptr; m_iIndention = 10; m_iColumns = 1; m_bDrawTreeLines = true; m_iEntryHeight = 10; } //************************************************************************ // Destructor //************************************************************************ ~CLCDList() { } //************************************************************************ // Initializes the list //************************************************************************ bool Initialize() { if(!CLCDTextObject::Initialize()) return false; return true; } //************************************************************************ // Deinitializes the list //************************************************************************ bool Shutdown() { if(!CLCDTextObject::Shutdown()) return false; Clear(); return true; } //************************************************************************ // updates the list //************************************************************************ bool Update() { if(!CLCDTextObject::Update()) return false; return true; } //************************************************************************ // draws the list //************************************************************************ bool Draw(CLCDGfx *pGfx) { if(!CLCDTextObject::Draw(pGfx)) return false; SelectObject(pGfx->GetHDC(),m_hFont); POINT ptPrevViewportOrg = { 0, 0 }; int iHeight = 0; int iYOffset = 0, iXOffset=0; int iColWidth = (GetWidth()- (m_iColumns-1)*3)/m_iColumns; int iSpace = GetHeight() - (GetHeight()/m_iEntryHeight)*m_iEntryHeight; int iPerPage = (GetHeight()/m_iEntryHeight)*m_iColumns; int iEntriesDrawn = 0; CListEntry *pPosition = m_Position; // if nothing is selected, skip drawing if(pPosition == nullptr) return true; bool bDrawGroup = false; bool bSelected = false; // calculate the start offset if(m_iStartIndex < pPosition->GetIndex()) { while(pPosition && pPosition->GetIndex() != m_iStartIndex) pPosition = pPosition->GetPreviousEntry(); } if(m_iStartIndex > 0 && pPosition->GetIndex() > 0) pPosition = pPosition->GetPreviousEntry(); for(int iCol = 0;iColGetIndex() < m_iStartIndex) iHeight -= m_iEntryHeight-iSpace; else if(GetEntryCount() >= (iPerPage/m_iColumns) +1) iHeight = iSpace; } // bottom selection while(pPosition != nullptr) { iYOffset = iHeight; bSelected = m_Position == pPosition; bDrawGroup = pPosition->GetType() == CONTAINER; // ~~~~~~~~~~~~~~~~~~~~~~ // Draw tree lines // ~~~~~~~~~~~~~~~~~~~~~~ // set the clip region for the entry int iClipHeight = m_iEntryHeight; if(GetOrigin().y+iYOffset+iClipHeight > GetOrigin().y + GetHeight()) iClipHeight = GetHeight() - iYOffset; pGfx->SetClipRegion(GetOrigin().x+iXOffset,GetOrigin().y+iYOffset, iColWidth, iClipHeight); // offset the control at its origin so entry use (0,0) SetViewportOrgEx(pGfx->GetHDC(), GetOrigin().x+iXOffset, GetOrigin().y+iYOffset, &ptPrevViewportOrg); if(m_bDrawTreeLines) { for(int i=1;iGetLevel();i++) { if(i == pPosition->GetLevel()-1) { // - pGfx->DrawLine((i-1)*m_iIndention+m_iIndention/2,m_iEntryHeight/2,i*m_iIndention,m_iEntryHeight/2); // | if(pPosition == ((CListContainer*)pPosition->GetParent())->GetLastOwnEntry()) pGfx->DrawLine((i-1)*m_iIndention+m_iIndention/2,0,(i-1)*m_iIndention+m_iIndention/2,m_iEntryHeight/2); // | // | else pGfx->DrawLine((i-1)*m_iIndention+m_iIndention/2,0,(i-1)*m_iIndention+m_iIndention/2,m_iEntryHeight); } else { CListEntry *pPosition2 = pPosition; for(int j = pPosition->GetLevel();j>i+1;j--) pPosition2 = pPosition2->GetParent(); // | // | if(pPosition2 != ((CListContainer*)pPosition2->GetParent())->GetLastOwnEntry()) pGfx->DrawLine((i-1)*m_iIndention+m_iIndention/2,0,(i-1)*m_iIndention+m_iIndention/2,m_iEntryHeight); } } } // ~~~~~~~~~~~~~~~~~~~~~~ // Draw the entry // ~~~~~~~~~~~~~~~~~~~~~~ pGfx->SetClipRegion(GetOrigin().x+(pPosition->GetLevel()-1)*m_iIndention+iXOffset, GetOrigin().y+iYOffset, iColWidth-(pPosition->GetLevel()-1)*m_iIndention, iClipHeight); // set the offset SetViewportOrgEx(pGfx->GetHDC(), GetOrigin().x+(pPosition->GetLevel()-1)*m_iIndention+iXOffset, GetOrigin().y+iYOffset, &ptPrevViewportOrg); // draw the entry if(!bDrawGroup) DrawEntry(pGfx,((CListItem*)pPosition)->GetItemData(),bSelected); else // draw the group DrawGroup(pGfx,((CListContainer*)pPosition)->GetGroupData(),((CListContainer*)pPosition)->IsOpen(),bSelected); // ~~~~~~~~~~~~~~~~~~~~~~ if(pPosition->GetIndex() >= m_iStartIndex && iHeight + m_iEntryHeight <= GetHeight()) iEntriesDrawn++; iHeight += m_iEntryHeight; pPosition = pPosition->GetNextEntry(); if(iHeight >= GetHeight()) break; } if(iCol != m_iColumns-1) { pGfx->SetClipRegion(GetOrigin().x, GetOrigin().y, GetWidth(), GetHeight()); // set the offset SetViewportOrgEx(pGfx->GetHDC(), GetOrigin().x, GetOrigin().y, &ptPrevViewportOrg); pGfx->DrawLine(iCol*3 + iColWidth + 1,0,iCol*3 + iColWidth + 1,GetHeight()); } iXOffset += 3 + iColWidth; } if(m_pScrollbar) { m_pScrollbar->ScrollTo(m_iStartIndex); m_pScrollbar->SetSliderSize(iEntriesDrawn); } return true; } void SetPosition(CListEntry *pEntry) { CListContainer::SetPosition(pEntry); if(pEntry == nullptr) return; int iPerPage = (GetHeight()/m_iEntryHeight)*m_iColumns; m_iStartIndex = pEntry->GetIndex(); if(m_iStartIndex + (iPerPage-1) > GetEntryCount()-1) m_iStartIndex = (GetEntryCount()-1)-(iPerPage-1); if(m_iStartIndex < 0) m_iStartIndex = 0; } //************************************************************************ // scrolls up //************************************************************************ bool ScrollUp() { if(m_Position != nullptr) { CListEntry *pEntry = m_Position->GetPreviousEntry(); if(pEntry != nullptr) { m_Position = pEntry; if(m_Position->GetIndex() < m_iStartIndex) m_iStartIndex--; return true; } } return false; } //************************************************************************ // scrolls down //************************************************************************ bool ScrollDown() { if(m_Position != nullptr) { CListEntry *pEntry = m_Position->GetNextEntry(); if(pEntry != nullptr) { m_Position = pEntry; int iPerPage = (GetHeight()/m_iEntryHeight)*m_iColumns; if(m_Position->GetIndex() >= m_iStartIndex + iPerPage) m_iStartIndex++; return true; } } return false; } //************************************************************************ // returns the selected list entry //************************************************************************ CListEntry *GetSelectedEntry() { return m_Position; } //************************************************************************ // associates a scrollbar with the list //************************************************************************ void SetScrollbar(CLCDBar *pScrollbar) { m_pScrollbar = pScrollbar; if(m_pScrollbar) { m_pScrollbar->SetRange(0, m_iEntryCount - 1); m_pScrollbar->ScrollTo(m_Position != nullptr ? m_Position->GetIndex() : 0); m_pScrollbar->SetAlignment(TOP); } } //************************************************************************ // sets the group indention in pixels //************************************************************************ void SetIndention(int iIndention) { m_iIndention = iIndention; } //************************************************************************ // sets the lists entry height //************************************************************************ void SetEntryHeight(int iEntryHeight) { m_iEntryHeight = iEntryHeight; } //************************************************************************ // returns the lists entry height //************************************************************************ int GetEntryHeight() { return m_iEntryHeight; } //************************************************************************ // enables/disables drawing of treelines //************************************************************************ void SetDrawTreeLines(bool bDraw) { m_bDrawTreeLines = bDraw; } //************************************************************************ // sets the amount of columns the list uses //************************************************************************ void SetColumns(int iColumns) { if(m_iColumns == iColumns) return; m_iColumns = iColumns; SetPosition(GetPosition()); } protected: //************************************************************************ // called when the lists size has changed //************************************************************************ void OnSizeChanged() { SetPosition(GetPosition()); } //************************************************************************ // updates the list's entry count //************************************************************************ void UpdateEntryCount() { CListContainer::UpdateEntryCount(); if(m_pScrollbar) { m_pScrollbar->SetRange(0,m_iEntryCount-1); if(GetPosition() != nullptr) m_pScrollbar->ScrollTo(GetPosition()->GetIndex()); } } //************************************************************************ // Called to delete the specified entry //************************************************************************ virtual void DeleteEntry(T) { } //************************************************************************ // Called to delete the specified group //************************************************************************ virtual void DeleteGroup(G) { } //************************************************************************ // Called to draw the specified entry //************************************************************************ virtual void DrawEntry(CLCDGfx*, T, bool) { } //************************************************************************ // Called to draw the specified entry //************************************************************************ virtual void DrawGroup(CLCDGfx*, G, bool, bool) { } protected: int m_iStartIndex; int m_iColumns; bool m_bDrawTreeLines; int m_iIndention; int m_iEntryHeight; CLCDBar *m_pScrollbar; }; #endif