|
1
|
1 #pragma once
|
|
|
2
|
|
|
3 // ================================================================================
|
|
|
4 // CListControlSimple
|
|
|
5 // Simplified CListControl interface; a ready-to-use class that can be instantiated
|
|
|
6 // without subclassing or setting callback objects.
|
|
|
7 // Use when you don't need advanced features such as buttons or editing.
|
|
|
8 // Maintains its own data.
|
|
|
9 // ================================================================================
|
|
|
10
|
|
|
11 #include "CListControlComplete.h"
|
|
|
12
|
|
|
13 #include <functional>
|
|
|
14 #include <vector>
|
|
|
15 #include <map>
|
|
|
16 #include <string>
|
|
|
17 #include <algorithm>
|
|
|
18
|
|
|
19
|
|
|
20 class CListControlSimple : public CListControlReadOnly {
|
|
|
21 public:
|
|
|
22 // Events
|
|
|
23 std::function<void()> onReordered; // if not set, list reordering is disabled
|
|
|
24 std::function<void()> onRemoved; // if not set, list item removal is disabled
|
|
|
25 std::function<void(size_t)> onItemAction; // optional, handle item double click or enter key
|
|
|
26 std::function<void()> onSelChange; // optional, handle selectionchange
|
|
|
27 std::function<void(size_t)> onColumnHeaderClick; // optional, handle column header click, if not set sorting will happen
|
|
|
28
|
|
|
29 size_t GetItemCount() const override {
|
|
|
30 return m_lines.size();
|
|
|
31 }
|
|
|
32 void SetItemCount( size_t count ) {
|
|
|
33 m_lines.resize( count );
|
|
|
34 ReloadData();
|
|
|
35 }
|
|
|
36 void SetItemText(size_t item, size_t subItem, const char * text, bool bRedraw = true) {
|
|
|
37 if ( item < m_lines.size() ) {
|
|
|
38 m_lines[item].text[subItem] = text;
|
|
|
39 if ( bRedraw ) ReloadItem( item );
|
|
|
40 } else {
|
|
|
41 PFC_ASSERT(!"CListControlSimple: item index out of range, call SetItemCount() first");
|
|
|
42 }
|
|
|
43 }
|
|
|
44 bool GetSubItemText(size_t item, size_t subItem, pfc::string_base & out) const override {
|
|
|
45 if ( item < m_lines.size() ) {
|
|
|
46 auto & l = m_lines[item].text;
|
|
|
47 auto iter = l.find( subItem );
|
|
|
48 if ( iter != l.end() ) {
|
|
|
49 out = iter->second.c_str();
|
|
|
50 return true;
|
|
|
51 }
|
|
|
52 }
|
|
|
53 return false;
|
|
|
54 }
|
|
|
55
|
|
|
56 uint32_t QueryDragDropTypes() const override {
|
|
|
57 return (onReordered != nullptr) ? dragDrop_reorder : 0;
|
|
|
58 }
|
|
|
59
|
|
|
60 void RequestReorder( const size_t * order, size_t count) override {
|
|
|
61 if ( onReordered == nullptr ) return;
|
|
|
62 _Reorder(order, count);
|
|
|
63 }
|
|
|
64 void RequestRemoveSelection() override {
|
|
|
65 if (onRemoved == nullptr) return;
|
|
|
66 auto mask = this->GetSelectionMask();
|
|
|
67 size_t oldCount = m_lines.size();
|
|
|
68 pfc::remove_mask_t( m_lines, mask );
|
|
|
69 this->OnItemsRemoved( mask, oldCount );
|
|
|
70 onRemoved();
|
|
|
71 }
|
|
|
72 void ExecuteDefaultAction( size_t idx ) override {
|
|
|
73 if (onItemAction != nullptr) onItemAction(idx);
|
|
|
74 }
|
|
|
75
|
|
|
76 void SetItemUserData( size_t item, size_t user ) {
|
|
|
77 if ( item < m_lines.size() ) {
|
|
|
78 m_lines[item].user = user;
|
|
|
79 }
|
|
|
80 }
|
|
|
81 size_t GetItemUserData( size_t item ) const {
|
|
|
82 size_t ret = 0;
|
|
|
83 if ( item < m_lines.size() ) {
|
|
|
84 ret = m_lines[item].user;
|
|
|
85 }
|
|
|
86 return ret;
|
|
|
87 }
|
|
|
88 void RemoveAllItems() {
|
|
|
89 RemoveItems(pfc::bit_array_true());
|
|
|
90 }
|
|
|
91 void RemoveItems( pfc::bit_array const & mask ) {
|
|
|
92 const auto oldCount = m_lines.size();
|
|
|
93 pfc::remove_mask_t( m_lines, mask );
|
|
|
94 this->OnItemsRemoved( mask, oldCount );
|
|
|
95 }
|
|
|
96 void RemoveItem( size_t which ) {
|
|
|
97 RemoveItems( pfc::bit_array_one( which ) );
|
|
|
98 }
|
|
|
99
|
|
|
100 size_t InsertItem( size_t insertAt, const char * textCol0 = nullptr ) {
|
|
|
101 if ( insertAt > m_lines.size() ) {
|
|
|
102 insertAt = m_lines.size();
|
|
|
103 }
|
|
|
104 {
|
|
|
105 line_t data;
|
|
|
106 if ( textCol0 != nullptr ) data.text[0] = textCol0;
|
|
|
107 m_lines.insert( m_lines.begin() + insertAt, std::move(data) );
|
|
|
108 }
|
|
|
109 this->OnItemsInserted( insertAt, 1, false );
|
|
|
110 return insertAt;
|
|
|
111 }
|
|
|
112 size_t AddItem( const char * textCol0 = nullptr ) {
|
|
|
113 return InsertItem( SIZE_MAX, textCol0 );
|
|
|
114 }
|
|
|
115 size_t InsertItems( size_t insertAt, size_t count ) {
|
|
|
116 if ( insertAt > m_lines.size() ) {
|
|
|
117 insertAt = m_lines.size();
|
|
|
118 }
|
|
|
119
|
|
|
120 {
|
|
|
121 line_t val;
|
|
|
122 m_lines.insert( m_lines.begin() + insertAt, count, val );
|
|
|
123 }
|
|
|
124
|
|
|
125 this->OnItemsInserted( insertAt, count, false );
|
|
|
126 return insertAt;
|
|
|
127 }
|
|
|
128 void SortBy(size_t column, bool descending) {
|
|
|
129 std::vector<size_t> order; order.resize(m_lines.size());
|
|
|
130 for (size_t walk = 0; walk < order.size(); ++walk) order[walk] = walk;
|
|
|
131 auto pred = [column, descending](const line_t& l1, const line_t& l2) {
|
|
|
132 int ret = pfc::winNaturalSortCompare(l1.at(column), l2.at(column));
|
|
|
133 if (!descending) ret = -ret;
|
|
|
134 return ret > 0;
|
|
|
135 };
|
|
|
136 auto pred_order = [&](size_t i1, size_t i2) {
|
|
|
137 return pred(m_lines[i1], m_lines[i2]);
|
|
|
138 };
|
|
|
139 std::sort(order.begin(), order.end(), pred_order);
|
|
|
140 this->_Reorder(order.data(), order.size());
|
|
|
141 this->SetSortIndicator(column, descending);
|
|
|
142 }
|
|
|
143 void SortBy(size_t column) {
|
|
|
144 HDITEM item = { HDI_FORMAT };
|
|
|
145 if (this->GetHeaderCtrl().GetItem((int)column, &item)) {
|
|
|
146 bool bDescending = (item.fmt & HDF_SORTDOWN) != 0;
|
|
|
147 this->SortBy(column, bDescending);
|
|
|
148 }
|
|
|
149 }
|
|
|
150 protected:
|
|
|
151 void OnSelectionChanged(pfc::bit_array const & affected, pfc::bit_array const & status) override {
|
|
|
152 __super::OnSelectionChanged(affected, status);
|
|
|
153 if ( onSelChange ) onSelChange();
|
|
|
154 }
|
|
|
155 void OnColumnHeaderClick(t_size index) {
|
|
|
156 __super::OnColumnHeaderClick(index);
|
|
|
157 if (onColumnHeaderClick) onColumnHeaderClick(index);
|
|
|
158 else this->SortBy(index);
|
|
|
159 }
|
|
|
160 void _Reorder(const size_t* order, size_t count) {
|
|
|
161 pfc::reorder_t(m_lines, order, count);
|
|
|
162 this->OnItemsReordered(order, count);
|
|
|
163 if (onReordered) onReordered();
|
|
|
164 }
|
|
|
165 private:
|
|
|
166 struct line_t {
|
|
|
167 std::map<size_t, std::string> text;
|
|
|
168 size_t user = 0;
|
|
|
169 const char* at(size_t i) const {
|
|
|
170 auto iter = text.find(i);
|
|
|
171 if (iter == text.end()) return "";
|
|
|
172 return iter->second.c_str();
|
|
|
173 }
|
|
|
174 };
|
|
|
175 std::vector<line_t> m_lines;
|
|
|
176 };
|