#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; iCol < m_iColumns; iCol++) { iHeight = 0; if (iCol == 0) { if (pPosition->GetIndex() < 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; i < pPosition->GetLevel(); 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