|
35 | 35 |
|
36 | 36 | BOOL GetPrinterStatusDescription(DWORD dwStatus, TCHAR *szStatus, size_t cchMax); |
37 | 37 |
|
38 | | -/* Queueing model: |
39 | | -When first browsing into a folder, all items in queue |
40 | | -are cleared. |
41 | | -Then, all new items are added. |
42 | | -Finally, the APC is queued to the worker thread. |
43 | | -
|
44 | | -The worker thread will process items, removing items |
45 | | -from the queue as it does. Interlocking is required |
46 | | -between queue removal and emptying the queue. |
47 | | -
|
48 | | -Folder sizes are NOT calculated here. They are done |
49 | | -within a separate thread called from the main thread. */ |
50 | | -void CShellBrowser::AddToColumnQueue(int iItem) |
51 | | -{ |
52 | | - m_pColumnInfoList.push_back(iItem); |
53 | | -} |
54 | | - |
55 | | -void CShellBrowser::EmptyColumnQueue(void) |
56 | | -{ |
57 | | - EnterCriticalSection(&m_column_cs); |
58 | | - |
59 | | - m_pColumnInfoList.clear(); |
60 | | - |
61 | | - LeaveCriticalSection(&m_column_cs); |
62 | | -} |
63 | | - |
64 | | -BOOL CShellBrowser::RemoveFromColumnQueue(int *iItem) |
65 | | -{ |
66 | | - BOOL bQueueNotEmpty; |
67 | | - |
68 | | - EnterCriticalSection(&m_column_cs); |
69 | | - |
70 | | - if(m_pColumnInfoList.empty() == TRUE) |
71 | | - { |
72 | | - SetEvent(m_hColumnQueueEvent); |
73 | | - bQueueNotEmpty = FALSE; |
74 | | - } |
75 | | - else |
76 | | - { |
77 | | - std::list<int>::iterator itr; |
78 | | - |
79 | | - itr = m_pColumnInfoList.begin(); |
80 | | - |
81 | | - *iItem = *itr; |
82 | | - |
83 | | - ResetEvent(m_hColumnQueueEvent); |
84 | | - |
85 | | - m_pColumnInfoList.erase(itr); |
86 | | - |
87 | | - bQueueNotEmpty = TRUE; |
88 | | - } |
89 | | - |
90 | | - LeaveCriticalSection(&m_column_cs); |
91 | | - |
92 | | - return bQueueNotEmpty; |
93 | | -} |
94 | | - |
95 | 38 | void CShellBrowser::AddToFolderQueue(int iItem) |
96 | 39 | { |
97 | 40 | m_pFolderInfoList.push_back(iItem); |
@@ -223,33 +166,122 @@ int CShellBrowser::SetAllFolderSizeColumnData(void) |
223 | 166 | return 0; |
224 | 167 | } |
225 | 168 |
|
226 | | -void CALLBACK SetAllColumnDataAPC(ULONG_PTR dwParam) |
| 169 | +void CShellBrowser::QueueColumnTask(int itemInternalIndex, int columnIndex) |
227 | 170 | { |
228 | | - CShellBrowser *pShellBrowser = reinterpret_cast<CShellBrowser *>(dwParam); |
229 | | - pShellBrowser->SetAllColumnText(); |
| 171 | + auto columnID = GetColumnIdByIndex(columnIndex); |
| 172 | + |
| 173 | + if (!columnID) |
| 174 | + { |
| 175 | + return; |
| 176 | + } |
| 177 | + |
| 178 | + int columnResultID = m_columnResultIDCounter++; |
| 179 | + |
| 180 | + auto result = m_columnThreadPool.push([this, columnResultID, columnID, itemInternalIndex](int id) { |
| 181 | + UNREFERENCED_PARAMETER(id); |
| 182 | + |
| 183 | + return this->GetColumnTextAsync(columnResultID, *columnID, itemInternalIndex); |
| 184 | + }); |
| 185 | + |
| 186 | + // The function call above might finish before this line runs, |
| 187 | + // but that doesn't matter, as the results won't be processed |
| 188 | + // until a message posted to the main thread has been handled |
| 189 | + // (which can only occur after this function has returned). |
| 190 | + m_columnResults.insert({ columnResultID, std::move(result) }); |
230 | 191 | } |
231 | 192 |
|
232 | | -void CShellBrowser::SetAllColumnText(void) |
| 193 | +CShellBrowser::ColumnResult_t CShellBrowser::GetColumnTextAsync(int columnResultId, unsigned int ColumnID, int InternalIndex) const |
233 | 194 | { |
234 | | - int ItemIndex; |
235 | | - BOOL QueueNotEmpty = RemoveFromColumnQueue(&ItemIndex); |
| 195 | + std::wstring columnText = GetColumnText(ColumnID, InternalIndex); |
236 | 196 |
|
237 | | - while(QueueNotEmpty) |
| 197 | + // This message may be delivered before this function has returned. |
| 198 | + // That doesn't actually matter, since the message handler will |
| 199 | + // simply wait for the result to be returned. |
| 200 | + PostMessage(m_hListView, WM_APP_COLUMN_RESULT_READY, columnResultId, 0); |
| 201 | + |
| 202 | + ColumnResult_t result; |
| 203 | + result.itemInternalIndex = InternalIndex; |
| 204 | + result.columnID = ColumnID; |
| 205 | + result.columnText = columnText; |
| 206 | + |
| 207 | + return result; |
| 208 | +} |
| 209 | + |
| 210 | +void CShellBrowser::ProcessColumnResult(int columnResultId) |
| 211 | +{ |
| 212 | + auto itr = m_columnResults.find(columnResultId); |
| 213 | + |
| 214 | + if (itr == m_columnResults.end()) |
| 215 | + { |
| 216 | + // This result is for a previous folder. It can be ignored. |
| 217 | + return; |
| 218 | + } |
| 219 | + |
| 220 | + auto result = itr->second.get(); |
| 221 | + |
| 222 | + auto index = LocateItemByInternalIndex(result.itemInternalIndex); |
| 223 | + |
| 224 | + if (!index) |
| 225 | + { |
| 226 | + // This is a valid state. The item may simply have been deleted. |
| 227 | + return; |
| 228 | + } |
| 229 | + |
| 230 | + auto columnIndex = GetColumnIndexById(result.columnID); |
| 231 | + |
| 232 | + if (!columnIndex) |
238 | 233 | { |
239 | | - int iColumnIndex = 0; |
| 234 | + // This is also a valid state. The column may have been removed. |
| 235 | + return; |
| 236 | + } |
| 237 | + |
| 238 | + auto columnText = std::make_unique<TCHAR[]>(result.columnText.size() + 1); |
| 239 | + StringCchCopy(columnText.get(), result.columnText.size() + 1, result.columnText.c_str()); |
| 240 | + ListView_SetItemText(m_hListView, *index, *columnIndex, columnText.get()); |
240 | 241 |
|
241 | | - for(auto itr = m_pActiveColumnList->begin();itr != m_pActiveColumnList->end();itr++) |
| 242 | + m_columnResults.erase(itr); |
| 243 | +} |
| 244 | + |
| 245 | +boost::optional<int> CShellBrowser::GetColumnIndexById(unsigned int id) const |
| 246 | +{ |
| 247 | + HWND header = ListView_GetHeader(m_hListView); |
| 248 | + |
| 249 | + int numItems = Header_GetItemCount(header); |
| 250 | + |
| 251 | + for (int i = 0; i < numItems; i++) |
| 252 | + { |
| 253 | + HDITEM hdItem; |
| 254 | + hdItem.mask = HDI_LPARAM; |
| 255 | + BOOL res = Header_GetItem(header, i, &hdItem); |
| 256 | + |
| 257 | + if (!res) |
242 | 258 | { |
243 | | - if(itr->bChecked) |
244 | | - { |
245 | | - SetColumnText(itr->id,ItemIndex,iColumnIndex++); |
246 | | - } |
| 259 | + continue; |
247 | 260 | } |
248 | 261 |
|
249 | | - QueueNotEmpty = RemoveFromColumnQueue(&ItemIndex); |
| 262 | + if (hdItem.lParam == id) |
| 263 | + { |
| 264 | + return i; |
| 265 | + } |
250 | 266 | } |
251 | 267 |
|
252 | | - ApplyHeaderSortArrow(); |
| 268 | + return boost::none; |
| 269 | +} |
| 270 | + |
| 271 | +boost::optional<unsigned int> CShellBrowser::GetColumnIdByIndex(int index) const |
| 272 | +{ |
| 273 | + HWND hHeader = ListView_GetHeader(m_hListView); |
| 274 | + |
| 275 | + HDITEM hdItem; |
| 276 | + hdItem.mask = HDI_LPARAM; |
| 277 | + BOOL res = Header_GetItem(hHeader, index, &hdItem); |
| 278 | + |
| 279 | + if (!res) |
| 280 | + { |
| 281 | + return boost::none; |
| 282 | + } |
| 283 | + |
| 284 | + return hdItem.lParam; |
253 | 285 | } |
254 | 286 |
|
255 | 287 | void CShellBrowser::SetColumnText(UINT ColumnID,int ItemIndex,int ColumnIndex) |
|
0 commit comments