|
1
|
1 #include "stdafx.h"
|
|
|
2 #include <vsstyle.h>
|
|
|
3 #include <memory>
|
|
|
4 #include "CListControlWithSelection.h"
|
|
|
5 #include "PaintUtils.h"
|
|
|
6 #include "IDataObjectUtils.h"
|
|
|
7 #include "SmartStrStr.h"
|
|
|
8 #include "CListControl-Cells.h"
|
|
|
9
|
|
|
10 namespace {
|
|
|
11 class bit_array_selection_CListControl : public pfc::bit_array {
|
|
|
12 public:
|
|
|
13 bit_array_selection_CListControl(CListControlWithSelectionBase const & list) : m_list(list) {}
|
|
|
14 bool get(t_size n) const {return m_list.IsItemSelected(n);}
|
|
|
15 private:
|
|
|
16 CListControlWithSelectionBase const & m_list;
|
|
|
17 };
|
|
|
18 }
|
|
|
19
|
|
|
20 bool CListControlWithSelectionBase::SelectAll() {
|
|
|
21 SetSelection(pfc::bit_array_true(), pfc::bit_array_true() );
|
|
|
22 return true;
|
|
|
23 }
|
|
|
24 void CListControlWithSelectionBase::SelectNone() {
|
|
|
25 SetSelection(pfc::bit_array_true(), pfc::bit_array_false() );
|
|
|
26 }
|
|
|
27 void CListControlWithSelectionBase::SetSelectionAt(size_t idx, bool bSel) {
|
|
|
28 if (bSel == this->IsItemSelected(idx)) return;
|
|
|
29 this->SetSelection(pfc::bit_array_one(idx), pfc::bit_array_val(bSel));
|
|
|
30 }
|
|
|
31 void CListControlWithSelectionBase::SelectSingle(size_t which) {
|
|
|
32 this->SetFocusItem( which );
|
|
|
33 SetSelection(pfc::bit_array_true(), pfc::bit_array_one( which ) );
|
|
|
34 }
|
|
|
35
|
|
|
36 LRESULT CListControlWithSelectionBase::OnFocus(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
|
|
37 UpdateItems(pfc::bit_array_or(bit_array_selection_CListControl(*this), pfc::bit_array_one(GetFocusItem())));
|
|
|
38 bHandled = FALSE;
|
|
|
39 return 0;
|
|
|
40 }
|
|
|
41
|
|
|
42 void CListControlWithSelectionBase::OnKeyDown_SetIndexDeltaPageHelper(int p_delta, int p_keys) {
|
|
|
43 const CRect rcClient = GetClientRectHook();
|
|
|
44 int itemHeight = GetItemHeight();
|
|
|
45 if (itemHeight > 0) {
|
|
|
46 int delta = rcClient.Height() / itemHeight;
|
|
|
47 if (delta < 1) delta = 1;
|
|
|
48 OnKeyDown_SetIndexDeltaHelper(delta * p_delta,p_keys);
|
|
|
49 }
|
|
|
50 }
|
|
|
51
|
|
|
52 void CListControlWithSelectionBase::OnKeyDown_HomeEndHelper(bool isEnd, int p_keys) {
|
|
|
53 if (!isEnd) {
|
|
|
54 //SPECIAL FIX - ensure first group header visibility.
|
|
|
55 MoveViewOrigin(CPoint(GetViewOrigin().x,0));
|
|
|
56 }
|
|
|
57
|
|
|
58 const t_size itemCount = GetItemCount();
|
|
|
59 if (itemCount == 0) return;//don't bother
|
|
|
60
|
|
|
61 if ((p_keys & (MK_SHIFT|MK_CONTROL)) == (MK_SHIFT|MK_CONTROL)) {
|
|
|
62 RequestMoveSelection( isEnd ? (int)itemCount : -(int)itemCount );
|
|
|
63 } else {
|
|
|
64 OnKeyDown_SetIndexHelper(isEnd ? (int)(itemCount-1) : 0,p_keys);
|
|
|
65 }
|
|
|
66 }
|
|
|
67 void CListControlWithSelectionBase::OnKeyDown_SetIndexHelper(int p_index, int p_keys) {
|
|
|
68 const t_size count = GetItemCount();
|
|
|
69 if (count > 0) {
|
|
|
70 t_size idx = (t_size)pfc::clip_t(p_index,0,(int)(count - 1));
|
|
|
71 const t_size oldFocus = GetFocusItem();
|
|
|
72 if (p_keys & MK_CONTROL) {
|
|
|
73 SetFocusItem(idx);
|
|
|
74 } else if ((p_keys & MK_SHIFT) != 0 && this->AllowRangeSelect() ) {
|
|
|
75 t_size selStart = GetSelectionStart();
|
|
|
76 if (selStart == pfc_infinite) selStart = oldFocus;
|
|
|
77 if (selStart == pfc_infinite) selStart = idx;
|
|
|
78 t_size selFirst, selCount;
|
|
|
79 selFirst = pfc::min_t(selStart,idx);
|
|
|
80 selCount = pfc::max_t(selStart,idx) + 1 - selFirst;
|
|
|
81 SetSelection(pfc::bit_array_true(), pfc::bit_array_range(selFirst,selCount));
|
|
|
82 SetFocusItem(idx);
|
|
|
83 SetSelectionStart(selStart);
|
|
|
84 } else {
|
|
|
85 SetFocusItem(idx);
|
|
|
86 SetSelection(pfc::bit_array_true(), pfc::bit_array_one(idx));
|
|
|
87 }
|
|
|
88 }
|
|
|
89 }
|
|
|
90
|
|
|
91 void CListControlWithSelectionBase::SelectGroupHelper2(size_t base,int p_keys) {
|
|
|
92 size_t count = this->ResolveGroupRange2(base);
|
|
|
93 if ( count > 0 ) {
|
|
|
94 if (p_keys & MK_CONTROL) {
|
|
|
95 SetGroupFocusByItem(base);
|
|
|
96 } /*else if (p_keys & MK_SHIFT) {
|
|
|
97 } */else {
|
|
|
98 SetGroupFocusByItem(base);
|
|
|
99 SetSelection(pfc::bit_array_true(), pfc::bit_array_range(base,count));
|
|
|
100 }
|
|
|
101 }
|
|
|
102 }
|
|
|
103
|
|
|
104 void CListControlWithSelectionBase::OnKeyDown_SetIndexDeltaLineHelper(int p_delta, int p_keys) {
|
|
|
105 if ((p_keys & (MK_SHIFT | MK_CONTROL)) == (MK_SHIFT|MK_CONTROL)) {
|
|
|
106 this->RequestMoveSelection(p_delta);
|
|
|
107 return;
|
|
|
108 }
|
|
|
109 const t_size total = GetItemCount();
|
|
|
110 t_size current = GetFocusItem();
|
|
|
111 const size_t focusGroupItem = this->GetGroupFocus2();
|
|
|
112 if (focusGroupItem != SIZE_MAX) current = focusGroupItem;
|
|
|
113
|
|
|
114 if (current == pfc_infinite) {
|
|
|
115 OnKeyDown_SetIndexDeltaHelper(p_delta,p_keys);
|
|
|
116 return;
|
|
|
117 }
|
|
|
118
|
|
|
119 const groupID_t currentGroup = GetItemGroup(current);
|
|
|
120 if (GroupFocusActive()) {
|
|
|
121 if (p_delta < 0 && focusGroupItem > 0) {
|
|
|
122 OnKeyDown_SetIndexHelper((int)(focusGroupItem-1), p_keys);
|
|
|
123 } else if (p_delta > 0) {
|
|
|
124 OnKeyDown_SetIndexHelper((int) current, p_keys);
|
|
|
125 }
|
|
|
126 } else {
|
|
|
127 if ((p_keys & MK_SHIFT) != 0) {
|
|
|
128 OnKeyDown_SetIndexDeltaHelper(p_delta,p_keys);
|
|
|
129 } else if (p_delta < 0) {
|
|
|
130 if (currentGroup == 0 || (current > 0 && currentGroup == GetItemGroup(current - 1))) {
|
|
|
131 OnKeyDown_SetIndexDeltaHelper(p_delta,p_keys);
|
|
|
132 } else {
|
|
|
133 SelectGroupHelper2(current, p_keys);
|
|
|
134 }
|
|
|
135 } else if (p_delta > 0) {
|
|
|
136 if (current + 1 >= total || currentGroup == GetItemGroup(current + 1)) {
|
|
|
137 OnKeyDown_SetIndexDeltaHelper(p_delta,p_keys);
|
|
|
138 } else {
|
|
|
139 SelectGroupHelper2(current + 1, p_keys);
|
|
|
140 }
|
|
|
141 }
|
|
|
142 }
|
|
|
143 }
|
|
|
144
|
|
|
145 LRESULT CListControlWithSelectionBase::OnLButtonDblClk(UINT,WPARAM p_wp,LPARAM p_lp,BOOL& bHandled) {
|
|
|
146 CPoint pt(p_lp);
|
|
|
147 if (OnClickedSpecialHitTest(pt)) {
|
|
|
148 return OnButtonDown(WM_LBUTTONDOWN, p_wp, p_lp, bHandled);
|
|
|
149 }
|
|
|
150 t_size item;
|
|
|
151 if (ItemFromPoint(pt,item)) {
|
|
|
152 ExecuteDefaultAction(item);
|
|
|
153 return 0;
|
|
|
154 } else if (GroupHeaderFromPoint2(pt,item)) {
|
|
|
155 t_size count = this->ResolveGroupRange2(item);
|
|
|
156 if (count > 0) ExecuteDefaultActionGroup(item, count);
|
|
|
157 return 0;
|
|
|
158 } else if (ExecuteCanvasDefaultAction(pt)) {
|
|
|
159 return 0;
|
|
|
160 } else {
|
|
|
161 return OnButtonDown(WM_LBUTTONDOWN,p_wp,p_lp,bHandled);
|
|
|
162 }
|
|
|
163 }
|
|
|
164 void CListControlWithSelectionBase::ExecuteDefaultActionByFocus() {
|
|
|
165 size_t groupFocus = this->GetGroupFocus2();
|
|
|
166 if ( groupFocus != SIZE_MAX ) {
|
|
|
167 size_t count = this->ResolveGroupRange2(groupFocus);
|
|
|
168 if (count > 0) {
|
|
|
169 ExecuteDefaultActionGroup(groupFocus, count);
|
|
|
170 }
|
|
|
171 } else {
|
|
|
172 size_t index = this->GetFocusItem();
|
|
|
173 if (index != SIZE_MAX) this->ExecuteDefaultAction(index);
|
|
|
174 }
|
|
|
175 }
|
|
|
176
|
|
|
177 t_size CListControlWithSelectionBase::GetSingleSel() const {
|
|
|
178 t_size total = GetItemCount();
|
|
|
179 t_size first = SIZE_MAX;
|
|
|
180 for(t_size walk = 0; walk < total; ++walk) {
|
|
|
181 if (IsItemSelected(walk)) {
|
|
|
182 if (first == SIZE_MAX) first = walk;
|
|
|
183 else return SIZE_MAX;
|
|
|
184 }
|
|
|
185 }
|
|
|
186 return first;
|
|
|
187 }
|
|
|
188
|
|
|
189 t_size CListControlWithSelectionBase::GetSelectedCount(pfc::bit_array const & mask,t_size max) const {
|
|
|
190 const t_size itemCount = this->GetItemCount();
|
|
|
191 t_size found = 0;
|
|
|
192 for(t_size walk = mask.find_first(true,0,itemCount); walk < itemCount && found < max; walk = mask.find_next(true,walk,itemCount)) {
|
|
|
193 if (IsItemSelected(walk)) ++found;
|
|
|
194 }
|
|
|
195 return found;
|
|
|
196 }
|
|
|
197 LRESULT CListControlWithSelectionBase::OnButtonDown(UINT p_msg,WPARAM p_wp,LPARAM p_lp,BOOL&) {
|
|
|
198 pfc::vartoggle_t<bool> l_noEnsureVisible(m_noEnsureVisible,true);
|
|
|
199 if (m_selectDragMode) {
|
|
|
200 AbortSelectDragMode();
|
|
|
201 return 0;
|
|
|
202 }
|
|
|
203
|
|
|
204 CPoint pt(p_lp);
|
|
|
205
|
|
|
206 if (OnClickedSpecial( (DWORD) p_wp, pt)) {
|
|
|
207 return 0;
|
|
|
208 }
|
|
|
209
|
|
|
210
|
|
|
211 size_t item;
|
|
|
212 const bool isRightClick = (p_msg == WM_RBUTTONDOWN || p_msg == WM_RBUTTONDBLCLK);
|
|
|
213 const bool gotCtrl = (p_wp & MK_CONTROL) != 0 && !isRightClick;
|
|
|
214 const bool gotShift = (p_wp & MK_SHIFT) != 0 && !isRightClick;
|
|
|
215
|
|
|
216
|
|
|
217
|
|
|
218 const bool bCanSelect = !OnClickedSpecialHitTest( pt );
|
|
|
219
|
|
|
220 const bool instaDrag = false;
|
|
|
221 const bool ddSupported = IsDragDropSupported();
|
|
|
222
|
|
|
223 if (GroupHeaderFromPoint2(pt, item)) {
|
|
|
224 t_size base = item,count = ResolveGroupRange2(base);
|
|
|
225 if (AllowRangeSelect() && count > 0) {
|
|
|
226 SetGroupFocusByItem(base);
|
|
|
227 pfc::bit_array_range groupRange(base,count);
|
|
|
228 bool instaDragOverride = false;
|
|
|
229 if (gotCtrl) {
|
|
|
230 ToggleRangeSelection(groupRange);
|
|
|
231 } else if (gotShift) {
|
|
|
232 SetSelection(groupRange, pfc::bit_array_true());
|
|
|
233 } else {
|
|
|
234 if (GetSelectedCount(groupRange) == count) instaDragOverride = true;
|
|
|
235 else SetSelection(pfc::bit_array_true(),groupRange);
|
|
|
236 }
|
|
|
237 if (ddSupported && (instaDrag || instaDragOverride)) {
|
|
|
238 PrepareDragDrop(pt,isRightClick);
|
|
|
239 } else {
|
|
|
240 InitSelectDragMode(pt, isRightClick);
|
|
|
241 }
|
|
|
242 }
|
|
|
243 } else if (ItemFromPoint(pt,item)) {
|
|
|
244 const t_size oldFocus = GetFocusItem();
|
|
|
245 const t_size selStartBefore = GetSelectionStart();
|
|
|
246 if ( bCanSelect ) SetFocusItem(item);
|
|
|
247 if (gotShift && AllowRangeSelect() ) {
|
|
|
248 if (bCanSelect) {
|
|
|
249 t_size selStart = selStartBefore;
|
|
|
250 if (selStart == pfc_infinite) selStart = oldFocus;
|
|
|
251 if (selStart == pfc_infinite) selStart = item;
|
|
|
252 SetSelectionStart(selStart);
|
|
|
253 t_size selFirst, selCount;
|
|
|
254 selFirst = pfc::min_t(selStart, item);
|
|
|
255 selCount = pfc::max_t(selStart, item) + 1 - selFirst;
|
|
|
256 pfc::bit_array_range rangeMask(selFirst, selCount);
|
|
|
257 pfc::bit_array_true trueMask;
|
|
|
258 SetSelection(gotCtrl ? pfc::implicit_cast<const pfc::bit_array&>(rangeMask) : pfc::implicit_cast<const pfc::bit_array&>(trueMask), rangeMask);
|
|
|
259 //if (!instaDrag) InitSelectDragMode(pt, isRightClick);
|
|
|
260 }
|
|
|
261 } else {
|
|
|
262 if (gotCtrl) {
|
|
|
263 if (bCanSelect) SetSelection(pfc::bit_array_one(item), pfc::bit_array_val(!IsItemSelected(item)));
|
|
|
264 if (!instaDrag) InitSelectDragMode(pt, isRightClick);
|
|
|
265 } else {
|
|
|
266 if (!IsItemSelected(item)) {
|
|
|
267 if (bCanSelect) SetSelection(pfc::bit_array_true(), pfc::bit_array_one(item));
|
|
|
268 if (ddSupported && instaDrag) {
|
|
|
269 PrepareDragDrop(pt,isRightClick);
|
|
|
270 } else {
|
|
|
271 InitSelectDragMode(pt, isRightClick);
|
|
|
272 }
|
|
|
273 } else {
|
|
|
274 if (ddSupported) {
|
|
|
275 PrepareDragDrop(pt,isRightClick);
|
|
|
276 } else {
|
|
|
277 InitSelectDragMode(pt, isRightClick);
|
|
|
278 }
|
|
|
279 }
|
|
|
280 }
|
|
|
281 }
|
|
|
282 } else {
|
|
|
283 if (!gotShift && !gotCtrl && bCanSelect) SelectNone();
|
|
|
284 InitSelectDragMode(pt, isRightClick);
|
|
|
285 }
|
|
|
286 return 0;
|
|
|
287 }
|
|
|
288
|
|
|
289
|
|
|
290 void CListControlWithSelectionBase::ToggleRangeSelection(pfc::bit_array const & mask) {
|
|
|
291 SetSelection(mask, pfc::bit_array_val(GetSelectedCount(mask,1) == 0));
|
|
|
292 }
|
|
|
293 void CListControlWithSelectionBase::ToggleGroupSelection2(size_t base) {
|
|
|
294 size_t count = this->ResolveGroupRange2(base);
|
|
|
295 if (count > 0) {
|
|
|
296 ToggleRangeSelection(pfc::bit_array_range(base, count));
|
|
|
297 }
|
|
|
298 }
|
|
|
299
|
|
|
300 LRESULT CListControlWithSelectionBase::OnRButtonUp(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
|
|
301 bHandled = FALSE;
|
|
|
302 AbortPrepareDragDropMode();
|
|
|
303 AbortSelectDragMode();
|
|
|
304 return 0;
|
|
|
305 }
|
|
|
306
|
|
|
307 bool CListControlWithSelectionBase::ShouldBeginDrag(CPoint ptRef, CPoint ptNow) const {
|
|
|
308 auto threshold = PP::queryDragThresholdForDPI(this->GetDPI());
|
|
|
309 return abs(ptNow.x - ptRef.x) > threshold.cx || abs(ptNow.y - ptRef.y) > threshold.cy;
|
|
|
310 }
|
|
|
311
|
|
|
312 LRESULT CListControlWithSelectionBase::OnMouseMove(UINT,WPARAM,LPARAM p_lp,BOOL&) {
|
|
|
313 if (m_prepareDragDropMode) {
|
|
|
314 if (ShouldBeginDrag(m_prepareDragDropOrigin, CPoint(p_lp))) {
|
|
|
315 AbortPrepareDragDropMode();
|
|
|
316 if (!m_ownDDActive) {
|
|
|
317 pfc::vartoggle_t<bool> ownDD(m_ownDDActive,true);
|
|
|
318 RunDragDrop( PointClientToAbs( m_prepareDragDropOrigin ),m_prepareDragDropModeRightClick);
|
|
|
319 }
|
|
|
320 }
|
|
|
321 } else if (m_selectDragMode) {
|
|
|
322 HandleDragSel(CPoint(p_lp));
|
|
|
323 }
|
|
|
324 return 0;
|
|
|
325 }
|
|
|
326 LRESULT CListControlWithSelectionBase::OnLButtonUp(UINT,WPARAM p_wp,LPARAM p_lp,BOOL&) {
|
|
|
327 const bool wasPreparingDD = m_prepareDragDropMode;
|
|
|
328 AbortPrepareDragDropMode();
|
|
|
329 CPoint pt(p_lp);
|
|
|
330 const bool gotCtrl = (p_wp & MK_CONTROL) != 0;
|
|
|
331 const bool gotShift = (p_wp & MK_SHIFT) != 0;
|
|
|
332 bool click = false;
|
|
|
333 bool processSel = wasPreparingDD;
|
|
|
334 if (m_selectDragMode) {
|
|
|
335 processSel = !m_selectDragMoved;
|
|
|
336 AbortSelectDragMode();
|
|
|
337 }
|
|
|
338 if (processSel) {
|
|
|
339 click = true;
|
|
|
340 if (!OnClickedSpecialHitTest(pt) ) {
|
|
|
341 size_t item;
|
|
|
342 if (GroupHeaderFromPoint2(pt, item)) {
|
|
|
343 t_size base = item, count = ResolveGroupRange2(base);
|
|
|
344 if ( count > 0 ) {
|
|
|
345 if (gotCtrl) {
|
|
|
346 } else {
|
|
|
347 SetSelection(pfc::bit_array_true(), pfc::bit_array_range(base, count));
|
|
|
348 }
|
|
|
349 }
|
|
|
350 } else if (ItemFromPoint(pt, item)) {
|
|
|
351 const t_size selStartBefore = GetSelectionStart();
|
|
|
352 if (gotCtrl) {
|
|
|
353 } else if (gotShift) {
|
|
|
354 SetSelectionStart(selStartBefore);
|
|
|
355 } else {
|
|
|
356 SetSelection(pfc::bit_array_true(), pfc::bit_array_one(item));
|
|
|
357 }
|
|
|
358 }
|
|
|
359 }
|
|
|
360 }
|
|
|
361 if (click && !gotCtrl && !gotShift) {
|
|
|
362 size_t item;
|
|
|
363 if (GroupHeaderFromPoint2(pt,item)) {
|
|
|
364 OnGroupHeaderClicked(GetItemGroup(item),pt);
|
|
|
365 } else if (ItemFromPoint(pt,item)) {
|
|
|
366 OnItemClicked(item,pt);
|
|
|
367 }
|
|
|
368 }
|
|
|
369 return 0;
|
|
|
370 }
|
|
|
371
|
|
|
372 void CListControlWithSelectionBase::OnKeyDown_SetIndexDeltaHelper(int p_delta, int p_keys) {
|
|
|
373 size_t focus = SIZE_MAX;
|
|
|
374 if (this->GroupFocusActive()) {
|
|
|
375 focus = this->GetGroupFocus2();
|
|
|
376 } else {
|
|
|
377 focus = GetFocusItem();
|
|
|
378 }
|
|
|
379 int target = 0;
|
|
|
380 if (focus != SIZE_MAX) target = (int) focus + p_delta;
|
|
|
381 OnKeyDown_SetIndexHelper(target,p_keys);
|
|
|
382 }
|
|
|
383
|
|
|
384
|
|
|
385 static int _get_keyflags() {
|
|
|
386 int ret = 0;
|
|
|
387 if (IsKeyPressed(VK_CONTROL)) ret |= MK_CONTROL;
|
|
|
388 if (IsKeyPressed(VK_SHIFT)) ret |= MK_SHIFT;
|
|
|
389 return ret;
|
|
|
390 }
|
|
|
391
|
|
|
392 LRESULT CListControlWithSelectionBase::OnKeyDown(UINT,WPARAM p_wp,LPARAM,BOOL& bHandled) {
|
|
|
393 switch(p_wp) {
|
|
|
394 case VK_NEXT:
|
|
|
395 OnKeyDown_SetIndexDeltaPageHelper(1,_get_keyflags());
|
|
|
396 return 0;
|
|
|
397 case VK_PRIOR:
|
|
|
398 OnKeyDown_SetIndexDeltaPageHelper(-1,_get_keyflags());
|
|
|
399 return 0;
|
|
|
400 case VK_DOWN:
|
|
|
401 OnKeyDown_SetIndexDeltaLineHelper(1,_get_keyflags());
|
|
|
402 return 0;
|
|
|
403 case VK_UP:
|
|
|
404 OnKeyDown_SetIndexDeltaLineHelper(-1,_get_keyflags());
|
|
|
405 return 0;
|
|
|
406 case VK_HOME:
|
|
|
407 OnKeyDown_HomeEndHelper(false,_get_keyflags());
|
|
|
408 return 0;
|
|
|
409 case VK_END:
|
|
|
410 OnKeyDown_HomeEndHelper(true,_get_keyflags());
|
|
|
411 return 0;
|
|
|
412 case VK_SPACE:
|
|
|
413 if (!TypeFindCheck()) {
|
|
|
414 ToggleSelectedItems();
|
|
|
415 }
|
|
|
416 return 0;
|
|
|
417 case VK_RETURN:
|
|
|
418 ExecuteDefaultActionByFocus();
|
|
|
419 return 0;
|
|
|
420 case VK_DELETE:
|
|
|
421 if (GetHotkeyModifierFlags() == 0) {
|
|
|
422 RequestRemoveSelection();
|
|
|
423 return 0;
|
|
|
424 }
|
|
|
425 break;
|
|
|
426 case 'A':
|
|
|
427 if (GetHotkeyModifierFlags() == MOD_CONTROL) {
|
|
|
428 if (SelectAll()) {
|
|
|
429 return 0;
|
|
|
430 }
|
|
|
431 // otherwise unhandled
|
|
|
432 }
|
|
|
433 break;
|
|
|
434 }
|
|
|
435
|
|
|
436 bHandled = FALSE;
|
|
|
437 return 0;
|
|
|
438 }
|
|
|
439
|
|
|
440 void CListControlWithSelectionBase::ToggleSelectedItems() {
|
|
|
441 if (ToggleSelectedItemsHook(bit_array_selection_CListControl(*this))) return;
|
|
|
442 if (GroupFocusActive()) {
|
|
|
443 ToggleGroupSelection2(this->GetGroupFocus2());
|
|
|
444 } else {
|
|
|
445 const t_size focus = GetFocusItem();
|
|
|
446 if (focus != pfc_infinite) {
|
|
|
447 ToggleRangeSelection(pfc::bit_array_range(focus, 1));
|
|
|
448 }
|
|
|
449 }
|
|
|
450 }
|
|
|
451
|
|
|
452 LRESULT CListControlWithSelectionBase::OnTimer(UINT,WPARAM p_wp,LPARAM,BOOL& bHandled) {
|
|
|
453 switch((DWORD)p_wp) {
|
|
|
454 case (DWORD)KSelectionTimerID:
|
|
|
455 if (m_selectDragMode) {
|
|
|
456 CPoint pt;
|
|
|
457 if (GetCursorPos(&pt) && ScreenToClient(&pt)) {
|
|
|
458 const CRect client = GetClientRectHook();
|
|
|
459 CPoint delta(0, 0);
|
|
|
460 if (pt.x < client.left) {
|
|
|
461 delta.x = pt.x - client.left;
|
|
|
462 } else if (pt.x > client.right) {
|
|
|
463 delta.x = pt.x - client.right;
|
|
|
464 }
|
|
|
465 if (pt.y < client.top) {
|
|
|
466 delta.y = pt.y - client.top;
|
|
|
467 } else if (pt.y > client.bottom) {
|
|
|
468 delta.y = pt.y - client.bottom;
|
|
|
469 }
|
|
|
470
|
|
|
471 MoveViewOriginDelta(delta);
|
|
|
472 HandleDragSel(pt);
|
|
|
473 }
|
|
|
474 }
|
|
|
475 return 0;
|
|
|
476 case TDDScrollControl::KTimerID:
|
|
|
477 HandleDDScroll();
|
|
|
478 return 0;
|
|
|
479 default:
|
|
|
480 bHandled = FALSE;
|
|
|
481 return 0;
|
|
|
482 }
|
|
|
483 }
|
|
|
484
|
|
|
485 bool CListControlWithSelectionBase::MoveSelectionProbe(int delta) {
|
|
|
486 pfc::array_t<size_t> order; order.set_size(GetItemCount());
|
|
|
487 {
|
|
|
488 bit_array_selection_CListControl sel(*this);
|
|
|
489 pfc::create_move_items_permutation(order.get_ptr(), order.get_size(), sel, delta);
|
|
|
490 }
|
|
|
491 for( size_t w = 0; w < order.get_size(); ++w ) if ( order[w] != w ) return true;
|
|
|
492 return false;
|
|
|
493 }
|
|
|
494 void CListControlWithSelectionBase::RequestMoveSelection(int delta) {
|
|
|
495 pfc::array_t<size_t> order; order.set_size(GetItemCount());
|
|
|
496 {
|
|
|
497 bit_array_selection_CListControl sel(*this);
|
|
|
498 pfc::create_move_items_permutation(order.get_ptr(), order.get_size(), sel, delta);
|
|
|
499 }
|
|
|
500
|
|
|
501 this->RequestReorder(order.get_ptr(), order.get_size());
|
|
|
502
|
|
|
503 if (delta < 0) {
|
|
|
504 size_t idx = GetFirstSelected();
|
|
|
505 if (idx != pfc_infinite) EnsureItemVisible(idx);
|
|
|
506 } else {
|
|
|
507 size_t idx = GetLastSelected();
|
|
|
508 if (idx != pfc_infinite) EnsureItemVisible(idx);
|
|
|
509 }
|
|
|
510 }
|
|
|
511
|
|
|
512 void CListControlWithSelectionBase::ToggleSelection(pfc::bit_array const & mask) {
|
|
|
513 const t_size count = GetItemCount();
|
|
|
514 pfc::bit_array_bittable table(count);
|
|
|
515 for(t_size walk = mask.find_first(true,0,count); walk < count; walk = mask.find_next(true,walk,count)) {
|
|
|
516 table.set(walk,!IsItemSelected(walk));
|
|
|
517 }
|
|
|
518 this->SetSelection(mask,table);
|
|
|
519 }
|
|
|
520
|
|
|
521 static HRGN FrameRectRgn(const CRect & rect) {
|
|
|
522 CRect exterior(rect); exterior.InflateRect(1,1);
|
|
|
523 CRgn rgn; rgn.CreateRectRgnIndirect(exterior);
|
|
|
524 CRect interior(rect); interior.DeflateRect(1,1);
|
|
|
525 if (!interior.IsRectEmpty()) {
|
|
|
526 CRgn rgn2; rgn2.CreateRectRgnIndirect(interior);
|
|
|
527 rgn.CombineRgn(rgn2,RGN_DIFF);
|
|
|
528 }
|
|
|
529 return rgn.Detach();
|
|
|
530 }
|
|
|
531
|
|
|
532 void CListControlWithSelectionBase::HandleDragSel(const CPoint & p_pt) {
|
|
|
533 const CPoint pt = PointClientToAbs(p_pt);
|
|
|
534 if (m_selectDragMoved || ShouldBeginDrag(m_selectDragCurrentAbs, pt)) {
|
|
|
535
|
|
|
536 if (!this->AllowRangeSelect()) {
|
|
|
537 // simplified
|
|
|
538 m_selectDragCurrentAbs = pt;
|
|
|
539 if (pt != m_selectDragOriginAbs) m_selectDragMoved = true;
|
|
|
540 return;
|
|
|
541 }
|
|
|
542
|
|
|
543 CRect rcOld(m_selectDragOriginAbs,m_selectDragCurrentAbs); rcOld.NormalizeRect();
|
|
|
544 m_selectDragCurrentAbs = pt;
|
|
|
545 CRect rcNew(m_selectDragOriginAbs,m_selectDragCurrentAbs); rcNew.NormalizeRect();
|
|
|
546
|
|
|
547
|
|
|
548 {
|
|
|
549 CRgn rgn = FrameRectRgn(rcNew);
|
|
|
550 CRgn rgn2 = FrameRectRgn(rcOld);
|
|
|
551 rgn.CombineRgn(rgn2,RGN_OR);
|
|
|
552 rgn.OffsetRgn( - GetViewOffset() );
|
|
|
553 InvalidateRgn(rgn);
|
|
|
554 }
|
|
|
555
|
|
|
556 if (pt != m_selectDragOriginAbs) m_selectDragMoved = true;
|
|
|
557
|
|
|
558 if (m_selectDragChanged || !IsSameItemOrHeaderAbs(pt,m_selectDragOriginAbs)) {
|
|
|
559 m_selectDragChanged = true;
|
|
|
560 const int keys = _get_keyflags();
|
|
|
561 t_size base,count, baseOld, countOld;
|
|
|
562 if (!GetItemRangeAbs(rcNew,base,count)) base = count = 0;
|
|
|
563 if (!GetItemRangeAbs(rcOld,baseOld,countOld)) baseOld = countOld = 0;
|
|
|
564 {
|
|
|
565 pfc::bit_array_range rangeNew(base,count), rangeOld(baseOld,countOld);
|
|
|
566 if (keys & MK_CONTROL) {
|
|
|
567 ToggleSelection(pfc::bit_array_xor(rangeNew,rangeOld));
|
|
|
568 } else if (keys & MK_SHIFT) {
|
|
|
569 SetSelection(pfc::bit_array_or(rangeNew,rangeOld),rangeNew);
|
|
|
570 } else {
|
|
|
571 SetSelection(pfc::bit_array_true(),rangeNew);
|
|
|
572 }
|
|
|
573 }
|
|
|
574 if (ItemFromPointAbs(pt,base)) {
|
|
|
575 const CRect rcVisible = GetVisibleRectAbs(), rcItem = GetItemRectAbs(base);
|
|
|
576 if (rcItem.top >= rcVisible.top && rcItem.bottom <= rcVisible.bottom) {
|
|
|
577 SetFocusItem(base);
|
|
|
578 }
|
|
|
579 } else if (GroupHeaderFromPointAbs2(pt,base)) {
|
|
|
580 const CRect rcVisible = GetVisibleRectAbs();
|
|
|
581 CRect rcGroup;
|
|
|
582 if (GetGroupHeaderRectAbs2(base,rcGroup)) {
|
|
|
583 if (rcGroup.top >= rcVisible.top && rcGroup.bottom <= rcVisible.bottom) {
|
|
|
584 this->SetGroupFocusByItem(base);
|
|
|
585 }
|
|
|
586 }
|
|
|
587 }
|
|
|
588 }
|
|
|
589 }
|
|
|
590 }
|
|
|
591
|
|
|
592 void CListControlWithSelectionBase::InitSelectDragMode(const CPoint & p_pt,bool p_rightClick) {
|
|
|
593 // Perform the bookkeeping even if multiple selection is disabled, detection of clicks relies on it
|
|
|
594 (void)p_rightClick;
|
|
|
595 SetTimer(KSelectionTimerID,KSelectionTimerPeriod);
|
|
|
596 m_selectDragMode = true;
|
|
|
597 m_selectDragOriginAbs = m_selectDragCurrentAbs = PointClientToAbs(p_pt);
|
|
|
598 m_selectDragChanged = false; m_selectDragMoved = false;
|
|
|
599 SetCapture();
|
|
|
600 }
|
|
|
601
|
|
|
602 void CListControlWithSelectionBase::AbortSelectDragMode(bool p_lostCapture) {
|
|
|
603 if (m_selectDragMode) {
|
|
|
604 m_selectDragMode = false;
|
|
|
605 CRect rcSelect(m_selectDragOriginAbs,m_selectDragCurrentAbs); rcSelect.NormalizeRect();
|
|
|
606 rcSelect.OffsetRect( - GetViewOffset() );
|
|
|
607 if (!p_lostCapture) ::SetCapture(NULL);
|
|
|
608 rcSelect.InflateRect(1,1);
|
|
|
609 InvalidateRect(rcSelect);
|
|
|
610 KillTimer(KSelectionTimerID);
|
|
|
611 }
|
|
|
612 }
|
|
|
613
|
|
|
614
|
|
|
615 LRESULT CListControlWithSelectionBase::OnCaptureChanged(UINT,WPARAM,LPARAM,BOOL&) {
|
|
|
616 AbortPrepareDragDropMode(true);
|
|
|
617 AbortSelectDragMode(true);
|
|
|
618 return 0;
|
|
|
619 }
|
|
|
620
|
|
|
621 void CListControlWithSelectionBase::RenderOverlay2(const CRect & p_updaterect,CDCHandle p_dc) {
|
|
|
622 if (m_selectDragMode && this->AllowRangeSelect() ) {
|
|
|
623 CRect rcSelect(m_selectDragOriginAbs,m_selectDragCurrentAbs);
|
|
|
624 rcSelect.NormalizeRect();
|
|
|
625 rcSelect.OffsetRect(-GetViewOffset());
|
|
|
626 PaintUtils::FocusRect(p_dc,rcSelect);
|
|
|
627 }
|
|
|
628 if (m_dropMark != SIZE_MAX) {
|
|
|
629 RenderDropMarkerClipped2(p_dc, p_updaterect, m_dropMark, m_dropMarkInside);
|
|
|
630 }
|
|
|
631 TParent::RenderOverlay2(p_updaterect,p_dc);
|
|
|
632 }
|
|
|
633
|
|
|
634 void CListControlWithSelectionBase::SetDropMark(size_t mark, bool inside) {
|
|
|
635 if (mark != m_dropMark || inside != m_dropMarkInside) {
|
|
|
636 CRgn updateRgn; updateRgn.CreateRectRgn(0, 0, 0, 0);
|
|
|
637 AddDropMarkToUpdateRgn(updateRgn, m_dropMark, m_dropMarkInside);
|
|
|
638 m_dropMark = mark;
|
|
|
639 m_dropMarkInside = inside;
|
|
|
640 AddDropMarkToUpdateRgn(updateRgn, m_dropMark, m_dropMarkInside);
|
|
|
641 RedrawWindow(NULL, updateRgn);
|
|
|
642 }
|
|
|
643 }
|
|
|
644
|
|
|
645 static int transformDDScroll(int p_value,int p_width, int p_dpi) {
|
|
|
646 if (p_dpi <= 0) p_dpi = 96;
|
|
|
647 const double dpiMul = 96.0 / (double) p_dpi;
|
|
|
648 double val = (double)(p_width - p_value);
|
|
|
649 val *= dpiMul;
|
|
|
650 val = pow(val,1.1) * 0.33;
|
|
|
651 val /= dpiMul;
|
|
|
652 return pfc::rint32(val);
|
|
|
653 }
|
|
|
654
|
|
|
655 void CListControlWithSelectionBase::HandleDDScroll() {
|
|
|
656 CPoint position;
|
|
|
657 if (m_ddScroll.m_timerActive && GetCursorPos(&position)) {
|
|
|
658 CRect client = GetClientRectHook();
|
|
|
659 CPoint delta (0,0);
|
|
|
660 if (ClientToScreen(client)) {
|
|
|
661 const CSize DPI = QueryScreenDPIEx();
|
|
|
662 const int scrollZoneWidthBase = GetItemHeight() * 2;
|
|
|
663 const int scrollZoneHeight = pfc::min_t<int>( scrollZoneWidthBase, client.Height() / 4 );
|
|
|
664 const int scrollZoneWidth = pfc::min_t<int>( scrollZoneWidthBase, client.Width() / 4 );
|
|
|
665
|
|
|
666 if (position.y >= client.top && position.y < client.top + scrollZoneHeight) {
|
|
|
667 delta.y -= transformDDScroll(position.y - client.top, scrollZoneHeight, DPI.cy);
|
|
|
668 } else if (position.y >= client.bottom - scrollZoneHeight && position.y < client.bottom) {
|
|
|
669 delta.y += transformDDScroll(client.bottom - position.y, scrollZoneHeight, DPI.cy);
|
|
|
670 }
|
|
|
671
|
|
|
672 if (position.x >= client.left && position.x < client.left + scrollZoneWidth) {
|
|
|
673 delta.x -= transformDDScroll(position.x - client.left, scrollZoneWidth, DPI.cx);
|
|
|
674 } else if (position.x >= client.right - scrollZoneWidth && position.x < client.right) {
|
|
|
675 delta.x += transformDDScroll(client.right - position.x, scrollZoneWidth, DPI.cx);
|
|
|
676 }
|
|
|
677 }
|
|
|
678
|
|
|
679 if (delta != CPoint(0,0)) MoveViewOriginDelta(delta);
|
|
|
680 }
|
|
|
681 }
|
|
|
682
|
|
|
683 void CListControlWithSelectionBase::ToggleDDScroll(bool p_state) {
|
|
|
684 if (p_state != m_ddScroll.m_timerActive) {
|
|
|
685 if (p_state) {
|
|
|
686 SetTimer(m_ddScroll.KTimerID,m_ddScroll.KTimerPeriod);
|
|
|
687 } else {
|
|
|
688 KillTimer(m_ddScroll.KTimerID);
|
|
|
689 }
|
|
|
690 m_ddScroll.m_timerActive = p_state;
|
|
|
691 }
|
|
|
692 }
|
|
|
693
|
|
|
694 void CListControlWithSelectionBase::PrepareDragDrop(const CPoint & p_point,bool p_isRightClick) {
|
|
|
695 m_prepareDragDropMode = true;
|
|
|
696 m_prepareDragDropOrigin = p_point;
|
|
|
697 m_prepareDragDropModeRightClick = p_isRightClick;
|
|
|
698 SetCapture();
|
|
|
699 }
|
|
|
700 void CListControlWithSelectionBase::AbortPrepareDragDropMode(bool p_lostCapture) {
|
|
|
701 if (m_prepareDragDropMode) {
|
|
|
702 m_prepareDragDropMode = false;
|
|
|
703 if (!p_lostCapture) ::SetCapture(NULL);
|
|
|
704 }
|
|
|
705 }
|
|
|
706
|
|
|
707
|
|
|
708 void CListControlWithSelectionBase::RenderItem(t_size p_item,const CRect & p_itemRect,const CRect & p_updateRect,CDCHandle p_dc) {
|
|
|
709 //console::formatter() << "RenderItem: " << p_item;
|
|
|
710 const bool weHaveFocus = ::GetFocus() == m_hWnd;
|
|
|
711 const bool isSelected = this->IsItemSelected(p_item);
|
|
|
712
|
|
|
713 const t_uint32 bkColor = GetSysColorHook(colorBackground);
|
|
|
714 const t_uint32 hlColor = GetSysColorHook(colorSelection);
|
|
|
715 const t_uint32 bkColorUsed = isSelected ? (weHaveFocus ? hlColor : PaintUtils::BlendColor(hlColor,bkColor)) : bkColor;
|
|
|
716
|
|
|
717 bool alternateTextColor = false, dtt = false;
|
|
|
718 auto & m_theme = theme();
|
|
|
719 CRect rcSelection = p_itemRect;
|
|
|
720 this->AdjustSelectionRect(p_item, rcSelection);
|
|
|
721 if (m_theme != NULL && isSelected && hlColor == GetSysColor(COLOR_HIGHLIGHT) && /*bkColor == GetSysColor(COLOR_WINDOW) && */ IsThemePartDefined(m_theme, LVP_LISTITEM, 0)) {
|
|
|
722 //PaintUtils::RenderItemBackground(p_dc,p_itemRect,p_item+GetItemGroup(p_item),bkColor);
|
|
|
723 DrawThemeBackground(m_theme, p_dc, LVP_LISTITEM, weHaveFocus ? LISS_SELECTED : LISS_SELECTEDNOTFOCUS, rcSelection, p_updateRect);
|
|
|
724 // drawthemetext is acting whacky with dark mode
|
|
|
725 if (!this->GetDarkMode()) dtt = true;
|
|
|
726 } else {
|
|
|
727 this->RenderItemBackground(p_dc, rcSelection, p_item, bkColorUsed );
|
|
|
728 // PaintUtils::RenderItemBackground(p_dc,p_itemRect,p_item+GetItemGroup(p_item),bkColorUsed);
|
|
|
729 if (isSelected) alternateTextColor = true;
|
|
|
730 }
|
|
|
731
|
|
|
732 {
|
|
|
733 DCStateScope backup(p_dc);
|
|
|
734 p_dc.SetBkMode(TRANSPARENT);
|
|
|
735 p_dc.SetBkColor(bkColorUsed);
|
|
|
736 p_dc.SetTextColor(alternateTextColor ? PaintUtils::DetermineTextColor(bkColorUsed) : this->GetSysColorHook(colorText));
|
|
|
737 pfc::vartoggle_t<bool> toggle(m_drawThemeText, dtt);
|
|
|
738 RenderItemText(p_item,p_itemRect,p_updateRect,p_dc, !alternateTextColor);
|
|
|
739 }
|
|
|
740
|
|
|
741 if (IsItemFocused(p_item) && weHaveFocus) {
|
|
|
742 PaintUtils::FocusRect2(p_dc,rcSelection, bkColorUsed);
|
|
|
743 }
|
|
|
744 }
|
|
|
745
|
|
|
746 void CListControlWithSelectionBase::RenderSubItemText(t_size item, t_size subItem,const CRect & subItemRect,const CRect & updateRect,CDCHandle dc, bool allowColors) {
|
|
|
747 #if 0
|
|
|
748 auto ct = GetCellType(item, subItem);
|
|
|
749 if ( ct == nullptr ) return;
|
|
|
750
|
|
|
751 if (m_drawThemeText && ct->AllowDrawThemeText() && !this->IsSubItemGrayed(item, subItem)) for(;;) {
|
|
|
752 pfc::string_formatter label;
|
|
|
753 if (!GetSubItemText(item,subItem,label)) return;
|
|
|
754 const bool weHaveFocus = ::GetFocus() == m_hWnd;
|
|
|
755 // const bool isSelected = this->IsItemSelected(item);
|
|
|
756 pfc::stringcvt::string_os_from_utf8 cvt(label);
|
|
|
757 if (PaintUtils::TextContainsCodes(cvt)) break;
|
|
|
758 CRect clip = GetItemTextRect(subItemRect);
|
|
|
759 const t_uint32 format = PaintUtils::DrawText_TranslateHeaderAlignment(GetColumnFormat(subItem));
|
|
|
760 DrawThemeText(theme(), dc, LVP_LISTITEM, weHaveFocus ? LISS_SELECTED : LISS_SELECTEDNOTFOCUS, cvt, (int)cvt.length(), DT_NOPREFIX | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER | format, 0, clip);
|
|
|
761 return;
|
|
|
762 }
|
|
|
763 #endif
|
|
|
764 __super::RenderSubItemText(item, subItem, subItemRect, updateRect, dc, allowColors);
|
|
|
765 }
|
|
|
766
|
|
|
767 void CListControlWithSelectionBase::RenderGroupHeader2(size_t baseItem,const CRect & p_headerRect,const CRect & p_updateRect,CDCHandle p_dc) {
|
|
|
768 TParent::RenderGroupHeader2(baseItem,p_headerRect,p_updateRect,p_dc);
|
|
|
769 if (IsGroupHeaderFocused2(baseItem)) {
|
|
|
770 PaintUtils::FocusRect(p_dc,p_headerRect);
|
|
|
771 }
|
|
|
772 }
|
|
|
773
|
|
|
774 CRect CListControlWithSelectionBase::DropMarkerUpdateRect(t_size index,bool bInside) const {
|
|
|
775 if (index != SIZE_MAX) {
|
|
|
776 CRect rect;
|
|
|
777 if (bInside) {
|
|
|
778 rect = GetItemRect(index);
|
|
|
779 rect.InflateRect(DropMarkerMargin());
|
|
|
780 } else {
|
|
|
781 rect = DropMarkerRect(DropMarkerOffset(index));
|
|
|
782 }
|
|
|
783 return rect;
|
|
|
784 } else {
|
|
|
785 return CRect(0,0,0,0);
|
|
|
786 }
|
|
|
787 }
|
|
|
788 void CListControlWithSelectionBase::AddDropMarkToUpdateRgn(HRGN p_rgn, t_size p_index, bool bInside) const {
|
|
|
789 CRect rect = DropMarkerUpdateRect(p_index,bInside);
|
|
|
790 if (!rect.IsRectEmpty()) PaintUtils::AddRectToRgn(p_rgn,rect);
|
|
|
791 }
|
|
|
792
|
|
|
793 CRect CListControlWithSelectionBase::DropMarkerRect(int offset) const {
|
|
|
794 const int delta = MulDiv(5,m_dpi.cy,96);
|
|
|
795 CRect rc(0,offset - delta,GetViewAreaWidth(), offset + delta);
|
|
|
796 rc.InflateRect(DropMarkerMargin());
|
|
|
797 return rc;
|
|
|
798 }
|
|
|
799
|
|
|
800 int CListControlWithSelectionBase::DropMarkerOffset(t_size marker) const {
|
|
|
801
|
|
|
802 return (marker > 0 ? this->GetItemBottomOffsetAbs(marker-1): 0) - GetViewOffset().y;
|
|
|
803 }
|
|
|
804
|
|
|
805 bool CListControlWithSelectionBase::RenderDropMarkerClipped2(CDCHandle dc, const CRect & update, t_size item, bool bInside) {
|
|
|
806 CRect markerRect = DropMarkerUpdateRect(item,bInside);
|
|
|
807 CRect affected;
|
|
|
808 if (affected.IntersectRect(markerRect,update)) {
|
|
|
809 DCStateScope state(dc);
|
|
|
810 if (dc.IntersectClipRect(affected)) {
|
|
|
811 RenderDropMarker2(dc,item,bInside);
|
|
|
812 return true;
|
|
|
813 }
|
|
|
814 }
|
|
|
815 return false;
|
|
|
816 }
|
|
|
817 void CListControlWithSelectionBase::RenderDropMarker2(CDCHandle dc, t_size item, bool bInside) {
|
|
|
818 if (item != SIZE_MAX) {
|
|
|
819 if (bInside) {
|
|
|
820 CPen pen; MakeDropMarkerPen(pen);
|
|
|
821 SelectObjectScope penScope(dc,pen);
|
|
|
822 const CRect rc = GetItemRect(item);
|
|
|
823 dc.MoveTo(rc.left,rc.top);
|
|
|
824 dc.LineTo(rc.right,rc.top);
|
|
|
825 dc.LineTo(rc.right,rc.bottom);
|
|
|
826 dc.LineTo(rc.left,rc.bottom);
|
|
|
827 dc.LineTo(rc.left,rc.top);
|
|
|
828 } else {
|
|
|
829 RenderDropMarkerByOffset2(DropMarkerOffset(item),dc);
|
|
|
830 }
|
|
|
831 }
|
|
|
832 }
|
|
|
833
|
|
|
834 SIZE CListControlWithSelectionBase::DropMarkerMargin() const {
|
|
|
835 const int penDeltaX = MulDiv(5 /* we don't know how to translate CreatePen units... */,m_dpi.cx,96);
|
|
|
836 const int penDeltaY = MulDiv(5 /* we don't know how to translate CreatePen units... */,m_dpi.cy,96);
|
|
|
837 SIZE s = {penDeltaX,penDeltaY};
|
|
|
838 return s;
|
|
|
839 }
|
|
|
840 void CListControlWithSelectionBase::MakeDropMarkerPen(CPen & out) const {
|
|
|
841 WIN32_OP_D( out.CreatePen(PS_SOLID,3,GetSysColorHook(colorText)) != NULL );
|
|
|
842 }
|
|
|
843
|
|
|
844 void CListControlWithSelectionBase::RenderDropMarkerByOffset2(int offset,CDCHandle p_dc) {
|
|
|
845 CPen pen; MakeDropMarkerPen(pen);
|
|
|
846 const int delta = MulDiv(5,m_dpi.cy,96);
|
|
|
847 SelectObjectScope penScope(p_dc,pen);
|
|
|
848 const int width = GetViewAreaWidth();
|
|
|
849 if (width > 0) {
|
|
|
850 const int vx = this->GetViewOffset().x;
|
|
|
851 const int left = -vx;
|
|
|
852 const int right = width - 1 - vx;
|
|
|
853 p_dc.MoveTo(left,offset);
|
|
|
854 p_dc.LineTo(right,offset);
|
|
|
855 p_dc.MoveTo(left,offset-delta);
|
|
|
856 p_dc.LineTo(left,offset+delta);
|
|
|
857 p_dc.MoveTo(right,offset-delta);
|
|
|
858 p_dc.LineTo(right,offset+delta);
|
|
|
859 }
|
|
|
860 }
|
|
|
861
|
|
|
862 void CListControlWithSelectionBase::FocusToUpdateRgn(HRGN rgn) {
|
|
|
863 size_t focusItem = GetFocusItem();
|
|
|
864 if (focusItem != SIZE_MAX) AddItemToUpdateRgn(rgn,focusItem);
|
|
|
865 size_t focusGroup = GetGroupFocus2();
|
|
|
866 if (focusGroup != SIZE_MAX) AddGroupHeaderToUpdateRgn2(rgn,focusGroup);
|
|
|
867 }
|
|
|
868
|
|
|
869 void CListControlWithSelectionImpl::ReloadData() {
|
|
|
870 if ( GetItemCount() != m_selection.get_size() ) {
|
|
|
871 this->SelHandleReset();
|
|
|
872 }
|
|
|
873 __super::ReloadData();
|
|
|
874 }
|
|
|
875
|
|
|
876 size_t CListControlWithSelectionImpl::GetGroupFocus2() const {
|
|
|
877 return (m_groupFocus && m_focus < GetItemCount()) ? m_focus : SIZE_MAX;
|
|
|
878 }
|
|
|
879
|
|
|
880 void CListControlWithSelectionImpl::SetSelectionImpl(pfc::bit_array const & affected, pfc::bit_array const & status) {
|
|
|
881 const t_size total = m_selection.get_size();
|
|
|
882 pfc::bit_array_flatIndexList toUpdate;
|
|
|
883
|
|
|
884 // Only fire UpdateItems for stuff that's both on-screen and actually changed
|
|
|
885 // Firing for whole affected mask will repaint everything when selecting one item
|
|
|
886 t_size base, count;
|
|
|
887 if (!GetItemRangeAbs(GetVisibleRectAbs(), base, count)) { base = count = 0; }
|
|
|
888
|
|
|
889 affected.walk( total, [&] (size_t idx) {
|
|
|
890 if ( m_selection[idx] != status[idx] && this->CanSelectItem(idx) ) {
|
|
|
891 m_selection[idx] = status[idx];
|
|
|
892 if ( idx >= base && idx < base+count ) toUpdate.add(idx);
|
|
|
893 }
|
|
|
894 } );
|
|
|
895
|
|
|
896 if ( toUpdate.get_count() > 0 ) {
|
|
|
897 UpdateItems(toUpdate);
|
|
|
898 }
|
|
|
899
|
|
|
900
|
|
|
901 // Fire subclassable method ONLY WITH ITEMS THAT CHANGED
|
|
|
902 // We provide no other means for them to know old state
|
|
|
903 this->OnSelectionChanged(toUpdate, status );
|
|
|
904 }
|
|
|
905
|
|
|
906 void CListControlWithSelectionImpl::SetSelection(pfc::bit_array const & affected, pfc::bit_array const & status) {
|
|
|
907 RefreshSelectionSize();
|
|
|
908
|
|
|
909
|
|
|
910 if ( m_selectionSupport == selectionSupportNone ) return;
|
|
|
911
|
|
|
912 if ( m_selectionSupport == selectionSupportSingle ) {
|
|
|
913 size_t single = SIZE_MAX;
|
|
|
914 bool selNone = true;
|
|
|
915 const size_t total = m_selection.get_size();
|
|
|
916 for( size_t walk = 0; walk < total; ++ walk ) {
|
|
|
917 if ( affected.get(walk) ) {
|
|
|
918 if ( status.get(walk) && single == SIZE_MAX ) {
|
|
|
919 single = walk;
|
|
|
920 }
|
|
|
921 } else if ( IsItemSelected( walk ) ) {
|
|
|
922 selNone = false;
|
|
|
923 }
|
|
|
924 }
|
|
|
925 if ( single < total ) {
|
|
|
926 SetSelectionImpl( pfc::bit_array_true(), pfc::bit_array_one( single ) );
|
|
|
927 } else if ( selNone ) {
|
|
|
928 this->SetSelectionImpl( pfc::bit_array_true(), pfc::bit_array_false() );
|
|
|
929 }
|
|
|
930 } else {
|
|
|
931 SetSelectionImpl( affected, status );
|
|
|
932 }
|
|
|
933
|
|
|
934 }
|
|
|
935
|
|
|
936 void CListControlWithSelectionImpl::RefreshSelectionSize() {
|
|
|
937 RefreshSelectionSize(GetItemCount());
|
|
|
938 }
|
|
|
939 void CListControlWithSelectionImpl::RefreshSelectionSize(t_size total) {
|
|
|
940 const t_size oldSize = m_selection.get_size();
|
|
|
941 if (total != oldSize) {
|
|
|
942 m_selection.set_size(total);
|
|
|
943 for(t_size walk = oldSize; walk < total; ++walk) m_selection[walk] = false;
|
|
|
944 }
|
|
|
945 }
|
|
|
946
|
|
|
947 void CListControlWithSelectionImpl::SetGroupFocusByItem(t_size item) {
|
|
|
948 CRgn update; update.CreateRectRgn(0,0,0,0);
|
|
|
949 FocusToUpdateRgn(update);
|
|
|
950 m_groupFocus = true; m_focus = item;
|
|
|
951 FocusToUpdateRgn(update);
|
|
|
952 InvalidateRgn(update);
|
|
|
953
|
|
|
954
|
|
|
955 CRect header;
|
|
|
956 if (GetGroupHeaderRectAbs2(item,header)) EnsureVisibleRectAbs(header);
|
|
|
957
|
|
|
958 this->OnFocusChangedGroup2( item );
|
|
|
959 }
|
|
|
960
|
|
|
961 void CListControlWithSelectionImpl::SetFocusItem(t_size index) {
|
|
|
962 CRgn update; update.CreateRectRgn(0,0,0,0);
|
|
|
963 FocusToUpdateRgn(update);
|
|
|
964 size_t oldFocus = m_focus;
|
|
|
965 m_groupFocus = false; m_focus = index;
|
|
|
966 FocusToUpdateRgn(update);
|
|
|
967 InvalidateRgn(update);
|
|
|
968
|
|
|
969 if ( index != SIZE_MAX ) {
|
|
|
970 EnsureVisibleRectAbs(GetItemRectAbs(index));
|
|
|
971 }
|
|
|
972
|
|
|
973 SetSelectionStart(index);
|
|
|
974
|
|
|
975 this->OnFocusChanged(oldFocus, index);
|
|
|
976 }
|
|
|
977
|
|
|
978 static void UpdateIndexOnReorder(t_size & index, const t_size * order, t_size count) {
|
|
|
979 index = pfc::permutation_find_reverse(order,count,index);
|
|
|
980 }
|
|
|
981 static void UpdateIndexOnRemoval(t_size & index, const pfc::bit_array & mask, t_size oldCount, t_size newCount) {
|
|
|
982 if (index >= oldCount || newCount == 0) {index = SIZE_MAX; return;}
|
|
|
983 for(t_size walk = mask.find_first(true,0,oldCount); walk < newCount; ++walk) {
|
|
|
984 if (walk < index) --index;
|
|
|
985 else break;
|
|
|
986 }
|
|
|
987 if (index >= newCount) index = newCount - 1;
|
|
|
988 }
|
|
|
989
|
|
|
990 static void UpdateIndexOnInsert(size_t& index, pfc::bit_array const& mask, size_t newCount) {
|
|
|
991 if (index == SIZE_MAX) return;
|
|
|
992 for (size_t walk = 0; walk < index; ) {
|
|
|
993 size_t delta = mask.calc_count(true, walk, index - walk);
|
|
|
994 if (delta == 0) break;
|
|
|
995 walk = index; index += delta;
|
|
|
996 }
|
|
|
997 }
|
|
|
998
|
|
|
999 static void UpdateIndexOnInsert(t_size & index, t_size base, t_size count) {
|
|
|
1000 if (index != SIZE_MAX && index >= base) index += count;
|
|
|
1001 }
|
|
|
1002
|
|
|
1003 void CListControlWithSelectionImpl::SelHandleReorder(const t_size * order, t_size count) {
|
|
|
1004 RefreshSelectionSize();
|
|
|
1005 UpdateIndexOnReorder(m_focus,order,count);
|
|
|
1006 UpdateIndexOnReorder(m_selectionStart,order,count);
|
|
|
1007 pfc::array_t<bool> newSel; newSel.set_size(m_selection.get_size());
|
|
|
1008 for(t_size walk = 0; walk < m_selection.get_size(); ++walk) newSel[walk] = m_selection[order[walk]];
|
|
|
1009 m_selection = newSel;
|
|
|
1010 }
|
|
|
1011
|
|
|
1012 void CListControlWithSelectionImpl::SelHandleReset() {
|
|
|
1013 RefreshSelectionSize(GetItemCount());
|
|
|
1014 for(t_size walk = 0; walk < m_selection.get_size(); walk++) m_selection[walk] = false;
|
|
|
1015 m_focus = SIZE_MAX;
|
|
|
1016 m_groupFocus = false;
|
|
|
1017
|
|
|
1018 }
|
|
|
1019 void CListControlWithSelectionImpl::SelHandleRemoval(const pfc::bit_array & mask, t_size oldCount) {
|
|
|
1020 RefreshSelectionSize(oldCount);
|
|
|
1021 const t_size newCount = GetItemCount();
|
|
|
1022 UpdateIndexOnRemoval(m_focus,mask,oldCount,newCount);
|
|
|
1023 UpdateIndexOnRemoval(m_selectionStart,mask,oldCount,newCount);
|
|
|
1024 pfc::remove_mask_t(m_selection,mask);
|
|
|
1025 }
|
|
|
1026
|
|
|
1027 void CListControlWithSelectionImpl::SelHandleInsertion(pfc::bit_array const& mask, size_t oldCount, size_t newCount, bool select) {
|
|
|
1028 PFC_ASSERT(newCount == GetItemCount());
|
|
|
1029 PFC_ASSERT(oldCount <= newCount); (void)oldCount;
|
|
|
1030
|
|
|
1031 // To behave sanely in single-select mode, we'd have to alter selection of other items from here
|
|
|
1032 // Let caller worry and outright deny select requests in modes other than multisel
|
|
|
1033 if (m_selectionSupport != selectionSupportMulti) select = false;
|
|
|
1034
|
|
|
1035 UpdateIndexOnInsert(m_focus, mask, newCount);
|
|
|
1036 UpdateIndexOnInsert(m_selectionStart, mask, newCount);
|
|
|
1037 pfc::array_t<bool> newSel;
|
|
|
1038 newSel.resize(newCount);
|
|
|
1039
|
|
|
1040 size_t inWalk = 0;
|
|
|
1041 for (size_t walk = 0; walk < newCount; ++walk) {
|
|
|
1042 bool v = false;
|
|
|
1043 if (mask[walk]) {
|
|
|
1044 v = select;
|
|
|
1045 } else if (inWalk < m_selection.get_size()) {
|
|
|
1046 v = m_selection[inWalk++];
|
|
|
1047 }
|
|
|
1048 newSel[walk] = v;
|
|
|
1049 }
|
|
|
1050
|
|
|
1051 m_selection = std::move(newSel);
|
|
|
1052 }
|
|
|
1053
|
|
|
1054 void CListControlWithSelectionImpl::SelHandleInsertion(t_size base, t_size count, bool select) {
|
|
|
1055 PFC_ASSERT(base + count <= GetItemCount());
|
|
|
1056
|
|
|
1057 // To behave sanely in single-select mode, we'd have to alter selection of other items from here
|
|
|
1058 // Let caller worry and outright deny select requests in modes other than multisel
|
|
|
1059 if (m_selectionSupport != selectionSupportMulti) select = false;
|
|
|
1060
|
|
|
1061 UpdateIndexOnInsert(m_focus,base,count);
|
|
|
1062 UpdateIndexOnInsert(m_selectionStart,base,count);
|
|
|
1063 RefreshSelectionSize(GetItemCount() - count);
|
|
|
1064
|
|
|
1065
|
|
|
1066 m_selection.insert_multi(select,base,count);
|
|
|
1067 }
|
|
|
1068
|
|
|
1069
|
|
|
1070 LRESULT CListControlWithSelectionBase::OnGetDlgCode(UINT,WPARAM,LPARAM p_lp,BOOL& bHandled) {
|
|
|
1071 if (p_lp == 0) {
|
|
|
1072 return DLGC_WANTALLKEYS | DLGC_WANTCHARS | DLGC_WANTARROWS;
|
|
|
1073 } else {
|
|
|
1074 const MSG * pmsg = reinterpret_cast<const MSG *>(p_lp);
|
|
|
1075 switch(pmsg->message) {
|
|
|
1076 case WM_KEYDOWN:
|
|
|
1077 case WM_KEYUP:
|
|
|
1078 switch(pmsg->wParam) {
|
|
|
1079 case VK_ESCAPE:
|
|
|
1080 case VK_TAB:
|
|
|
1081 bHandled = FALSE;
|
|
|
1082 return 0;
|
|
|
1083 default:
|
|
|
1084 return DLGC_WANTMESSAGE;
|
|
|
1085 }
|
|
|
1086 case WM_CHAR:
|
|
|
1087 return DLGC_WANTMESSAGE;
|
|
|
1088 default:
|
|
|
1089 bHandled = FALSE;
|
|
|
1090 return 0;
|
|
|
1091 }
|
|
|
1092 }
|
|
|
1093 }
|
|
|
1094
|
|
|
1095 bool CListControlWithSelectionBase::GetFocusRect(CRect & p_rect) {
|
|
|
1096 CRect temp;
|
|
|
1097 if (!GetFocusRectAbs(temp)) return false;
|
|
|
1098 p_rect = RectClientToAbs(temp);
|
|
|
1099 return true;
|
|
|
1100 }
|
|
|
1101
|
|
|
1102 bool CListControlWithSelectionBase::GetFocusRectAbs(CRect & p_rect) {
|
|
|
1103 size_t item = this->GetFocusItem();
|
|
|
1104 if (item != SIZE_MAX) {
|
|
|
1105 p_rect = this->GetItemRectAbs(item);
|
|
|
1106 return true;
|
|
|
1107 }
|
|
|
1108
|
|
|
1109 item = this->GetGroupFocus2();
|
|
|
1110 if (item != SIZE_MAX) {
|
|
|
1111 return this->GetGroupHeaderRectAbs2(item, p_rect);
|
|
|
1112 }
|
|
|
1113
|
|
|
1114 return false;
|
|
|
1115 }
|
|
|
1116
|
|
|
1117 bool CListControlWithSelectionBase::GetContextMenuPoint2(CPoint& ptInOut) {
|
|
|
1118 CPoint ptInvalid(-1,-1);
|
|
|
1119 if (ptInOut == ptInvalid) {
|
|
|
1120 ptInOut = GetContextMenuPointDefault();
|
|
|
1121 return ptInOut != ptInvalid;
|
|
|
1122 } else {
|
|
|
1123 CRect rc = this->GetClientRectHook();
|
|
|
1124 WIN32_OP_D( ClientToScreen(rc) );
|
|
|
1125 return !!rc.PtInRect(ptInOut);
|
|
|
1126 }
|
|
|
1127 }
|
|
|
1128
|
|
|
1129 CPoint CListControlWithSelectionBase::GetContextMenuPointDefault() {
|
|
|
1130 CRect rect;
|
|
|
1131 if (!GetFocusRectAbs(rect)) return CPoint(-1,-1);
|
|
|
1132 EnsureVisibleRectAbs(rect);
|
|
|
1133 CPoint pt = rect.CenterPoint() - GetViewOffset();
|
|
|
1134 ClientToScreen(&pt);
|
|
|
1135 return pt;
|
|
|
1136 }
|
|
|
1137
|
|
|
1138 CPoint CListControlWithSelectionBase::GetContextMenuPoint(CPoint ptGot) {
|
|
|
1139 CPoint pt;
|
|
|
1140 if (ptGot.x == -1 && ptGot.y == -1) {
|
|
|
1141 pt = GetContextMenuPointDefault();
|
|
|
1142 } else {
|
|
|
1143 pt = ptGot;
|
|
|
1144 }
|
|
|
1145 return pt;
|
|
|
1146 }
|
|
|
1147
|
|
|
1148 CPoint CListControlWithSelectionBase::GetContextMenuPoint(LPARAM lp) {
|
|
|
1149 CPoint pt;
|
|
|
1150 if (lp == -1) {
|
|
|
1151 pt = GetContextMenuPointDefault();
|
|
|
1152 } else {
|
|
|
1153 pt = lp;
|
|
|
1154 }
|
|
|
1155 return pt;
|
|
|
1156 }
|
|
|
1157
|
|
|
1158 bool CListControlWithSelectionBase::MakeDropReorderPermutation(pfc::array_t<t_size> & out, CPoint ptDrop) const {
|
|
|
1159 t_size insertMark = InsertIndexFromPoint(ptDrop);
|
|
|
1160 /*if (insertMark != this->GetFocusItem())*/ {
|
|
|
1161 const t_size count = GetItemCount();
|
|
|
1162 if (insertMark > count) insertMark = count;
|
|
|
1163 {
|
|
|
1164 t_size selBefore = 0;
|
|
|
1165 for(t_size walk = 0; walk < insertMark; ++walk) {
|
|
|
1166 if (IsItemSelected(walk)) selBefore++;
|
|
|
1167 }
|
|
|
1168 insertMark -= selBefore;
|
|
|
1169 }
|
|
|
1170 {
|
|
|
1171 pfc::array_t<t_size> permutation, selected, nonselected;
|
|
|
1172 const t_size selcount = this->GetSelectedCount();
|
|
|
1173 selected.set_size(selcount); nonselected.set_size(count - selcount);
|
|
|
1174 permutation.set_size(count);
|
|
|
1175 if (insertMark > nonselected.get_size()) insertMark = nonselected.get_size();
|
|
|
1176 for(t_size walk = 0, swalk = 0, nwalk = 0; walk < count; ++walk) {
|
|
|
1177 if (IsItemSelected(walk)) {
|
|
|
1178 selected[swalk++] = walk;
|
|
|
1179 } else {
|
|
|
1180 nonselected[nwalk++] = walk;
|
|
|
1181 }
|
|
|
1182 }
|
|
|
1183 for(t_size walk = 0; walk < insertMark; ++walk) {
|
|
|
1184 permutation[walk] = nonselected[walk];
|
|
|
1185 }
|
|
|
1186 for(t_size walk = 0; walk < selected.get_size(); ++walk) {
|
|
|
1187 permutation[insertMark + walk] = selected[walk];
|
|
|
1188 }
|
|
|
1189 for(t_size walk = insertMark; walk < nonselected.get_size(); ++walk) {
|
|
|
1190 permutation[selected.get_size() + walk] = nonselected[walk];
|
|
|
1191 }
|
|
|
1192 for(t_size walk = 0; walk < permutation.get_size(); ++walk) {
|
|
|
1193 if (permutation[walk] != walk) {
|
|
|
1194 out = permutation;
|
|
|
1195 return true;
|
|
|
1196 }
|
|
|
1197 }
|
|
|
1198 }
|
|
|
1199 }
|
|
|
1200 return false;
|
|
|
1201 }
|
|
|
1202
|
|
|
1203 void CListControlWithSelectionBase::EnsureVisibleRectAbs(const CRect & p_rect) {
|
|
|
1204 if (!m_noEnsureVisible) TParent::EnsureVisibleRectAbs(p_rect);
|
|
|
1205 }
|
|
|
1206
|
|
|
1207 bool CListControlWithSelectionBase::TypeFindCheck(DWORD ts) const {
|
|
|
1208 if (m_typeFindTS == 0) return false;
|
|
|
1209 return ts - m_typeFindTS < 1000;
|
|
|
1210 }
|
|
|
1211
|
|
|
1212 void CListControlWithSelectionBase::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
|
|
1213 (void)nRepCnt; (void)nFlags;
|
|
|
1214 if (nChar < 32) {
|
|
|
1215 m_typeFindTS = 0;
|
|
|
1216 return;
|
|
|
1217 }
|
|
|
1218
|
|
|
1219 const DWORD ts = GetTickCount();
|
|
|
1220 if (!TypeFindCheck(ts)) m_typeFind.reset();
|
|
|
1221
|
|
|
1222 if (nChar == ' ' && m_typeFind.is_empty()) {
|
|
|
1223 m_typeFindTS = 0;
|
|
|
1224 return;
|
|
|
1225 }
|
|
|
1226
|
|
|
1227 m_typeFindTS = ts;
|
|
|
1228 if (m_typeFindTS == 0) m_typeFindTS = UINT32_MAX;
|
|
|
1229 char temp[10] = {};
|
|
|
1230 pfc::utf8_encode_char(nChar, temp);
|
|
|
1231 m_typeFind += temp;
|
|
|
1232 RunTypeFind();
|
|
|
1233 }
|
|
|
1234
|
|
|
1235 static unsigned detectRepetition( pfc::string8 const & str ) {
|
|
|
1236 size_t count = 0;
|
|
|
1237 size_t walk = 0;
|
|
|
1238 uint32_t first = 0;
|
|
|
1239
|
|
|
1240 while( walk < str.length() ) {
|
|
|
1241 uint32_t current;
|
|
|
1242 auto delta = pfc::utf8_decode_char( str.c_str() + walk, current, str.length() - walk );
|
|
|
1243 if ( delta == 0 ) break;
|
|
|
1244 walk += delta;
|
|
|
1245
|
|
|
1246 if ( count == 0 ) first = current;
|
|
|
1247 else if ( first != current ) return 0;
|
|
|
1248
|
|
|
1249 ++ count;
|
|
|
1250 }
|
|
|
1251
|
|
|
1252 if ( count > 1 ) return first;
|
|
|
1253 return 0;
|
|
|
1254 }
|
|
|
1255
|
|
|
1256 size_t CListControlWithSelectionBase::EvalTypeFind() {
|
|
|
1257 if ( GetItemCount() == 0 ) return SIZE_MAX;
|
|
|
1258
|
|
|
1259 static SmartStrStr tool;
|
|
|
1260
|
|
|
1261 const size_t itemCount = GetItemCount();
|
|
|
1262 const size_t colCount = GetColumnCount();
|
|
|
1263 pfc::string_formatter temp; temp.prealloc(1024);
|
|
|
1264 t_size searchBase = this->GetFocusItem();
|
|
|
1265 if (searchBase >= itemCount) searchBase = 0;
|
|
|
1266
|
|
|
1267 size_t partial = SIZE_MAX;
|
|
|
1268 size_t repetition = SIZE_MAX;
|
|
|
1269 bool useRepetition = false;
|
|
|
1270 pfc::string8 strRepetitionChar;
|
|
|
1271 unsigned repChar = detectRepetition( m_typeFind );
|
|
|
1272 if ( repChar != 0 ) {
|
|
|
1273 useRepetition = true;
|
|
|
1274 strRepetitionChar.add_char( repChar );
|
|
|
1275 }
|
|
|
1276
|
|
|
1277 for(t_size walk = 0; walk < itemCount; ++walk) {
|
|
|
1278 t_size index = (walk + searchBase) % itemCount;
|
|
|
1279 for(size_t cWalk = 0; cWalk < colCount; ++cWalk) {
|
|
|
1280
|
|
|
1281 temp.reset();
|
|
|
1282
|
|
|
1283 if (AllowTypeFindInCell( index, cWalk )) {
|
|
|
1284 this->GetSubItemText(index, cWalk, temp);
|
|
|
1285 }
|
|
|
1286
|
|
|
1287 if ( temp.length() == 0 ) {
|
|
|
1288 continue;
|
|
|
1289 }
|
|
|
1290 if (partial == SIZE_MAX) {
|
|
|
1291 size_t matchAt;
|
|
|
1292 if (tool.strStrEnd( temp, m_typeFind, & matchAt ) != nullptr) {
|
|
|
1293 if ( matchAt == 0 ) return index;
|
|
|
1294 partial = index;
|
|
|
1295 }
|
|
|
1296 } else {
|
|
|
1297 if ( tool.matchHere( temp, m_typeFind ) ) return index;
|
|
|
1298 }
|
|
|
1299 if (useRepetition && index != searchBase) {
|
|
|
1300 if ( tool.matchHere( temp, strRepetitionChar ) ) {
|
|
|
1301 useRepetition = false;
|
|
|
1302 repetition = index;
|
|
|
1303 }
|
|
|
1304 }
|
|
|
1305 }
|
|
|
1306 }
|
|
|
1307 if (partial < itemCount) return partial;
|
|
|
1308 if (repetition < itemCount) return repetition;
|
|
|
1309 return SIZE_MAX;
|
|
|
1310 }
|
|
|
1311
|
|
|
1312 void CListControlWithSelectionBase::RunTypeFind() {
|
|
|
1313 size_t index = EvalTypeFind();
|
|
|
1314 if (index < GetItemCount() ) {
|
|
|
1315 this->SetFocusItem( index );
|
|
|
1316 this->SetSelection(pfc::bit_array_true(), pfc::bit_array_one(index) );
|
|
|
1317 } else {
|
|
|
1318 MessageBeep(0);
|
|
|
1319 }
|
|
|
1320 }
|
|
|
1321
|
|
|
1322 size_t CListControlWithSelectionBase::GetFirstSelected() const {
|
|
|
1323 const size_t count = GetItemCount();
|
|
|
1324 for( size_t w = 0; w < count; ++w ) {
|
|
|
1325 if ( IsItemSelected(w) ) return w;
|
|
|
1326 }
|
|
|
1327 return SIZE_MAX;
|
|
|
1328 }
|
|
|
1329
|
|
|
1330 size_t CListControlWithSelectionBase::GetLastSelected() const {
|
|
|
1331 const size_t count = GetItemCount();
|
|
|
1332 for( size_t w = count - 1; (t_ssize) w >= 0; --w ) {
|
|
|
1333 if ( IsItemSelected(w) ) return w;
|
|
|
1334 }
|
|
|
1335 return SIZE_MAX;
|
|
|
1336 }
|
|
|
1337
|
|
|
1338 namespace {
|
|
|
1339 class CDropTargetImpl : public ImplementCOMRefCounter<IDropTarget> {
|
|
|
1340 public:
|
|
|
1341 COM_QI_BEGIN()
|
|
|
1342 COM_QI_ENTRY(IUnknown)
|
|
|
1343 COM_QI_ENTRY(IDropTarget)
|
|
|
1344 COM_QI_END()
|
|
|
1345
|
|
|
1346 bool valid = true;
|
|
|
1347 std::function<void(CPoint pt) > Track;
|
|
|
1348 std::function<DWORD (IDataObject*)> HookAccept;
|
|
|
1349 std::function<void (IDataObject*, CPoint pt)> HookDrop;
|
|
|
1350 std::function<void ()> HookLeave;
|
|
|
1351
|
|
|
1352 DWORD m_effect = DROPEFFECT_NONE;
|
|
|
1353
|
|
|
1354 HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
|
|
|
1355 (void)grfKeyState; (void)pt;
|
|
|
1356 if (pDataObj == NULL || pdwEffect == NULL) return E_INVALIDARG;
|
|
|
1357 if (!valid) return E_FAIL;
|
|
|
1358 if ( HookAccept ) {
|
|
|
1359 m_effect = HookAccept(pDataObj);
|
|
|
1360 } else {
|
|
|
1361 m_effect = DROPEFFECT_MOVE;
|
|
|
1362 }
|
|
|
1363 *pdwEffect = m_effect;
|
|
|
1364 return S_OK;
|
|
|
1365 }
|
|
|
1366 HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
|
|
|
1367 (void)grfKeyState;
|
|
|
1368 if (pdwEffect == NULL) return E_INVALIDARG;
|
|
|
1369 if (!valid) return E_FAIL;
|
|
|
1370 if ( m_effect != DROPEFFECT_NONE ) Track(CPoint(pt.x, pt.y));
|
|
|
1371 *pdwEffect = m_effect;
|
|
|
1372 return S_OK;
|
|
|
1373 }
|
|
|
1374 HRESULT STDMETHODCALLTYPE DragLeave() {
|
|
|
1375 if (HookLeave) HookLeave();
|
|
|
1376 return S_OK;
|
|
|
1377 }
|
|
|
1378 HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
|
|
|
1379 (void)grfKeyState; (void)pdwEffect;
|
|
|
1380 if ( HookDrop && m_effect != DROPEFFECT_NONE ) {
|
|
|
1381 HookDrop( pDataObj, CPoint(pt.x, pt.y) );
|
|
|
1382 }
|
|
|
1383 return S_OK;
|
|
|
1384 }
|
|
|
1385 };
|
|
|
1386
|
|
|
1387 class CDropSourceImpl : public ImplementCOMRefCounter<IDropSource> {
|
|
|
1388 public:
|
|
|
1389 CPoint droppedAt;
|
|
|
1390 bool droppedAtValid = false;
|
|
|
1391 bool allowReorder = false;
|
|
|
1392
|
|
|
1393 bool allowDragOutside = false;
|
|
|
1394 CWindow wndOrigin;
|
|
|
1395
|
|
|
1396 COM_QI_BEGIN()
|
|
|
1397 COM_QI_ENTRY(IUnknown)
|
|
|
1398 COM_QI_ENTRY(IDropSource)
|
|
|
1399 COM_QI_END()
|
|
|
1400
|
|
|
1401 HRESULT STDMETHODCALLTYPE GiveFeedback(DWORD dwEffect) {
|
|
|
1402 m_effect = dwEffect;
|
|
|
1403 return DRAGDROP_S_USEDEFAULTCURSORS;
|
|
|
1404 }
|
|
|
1405
|
|
|
1406 HRESULT STDMETHODCALLTYPE QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) {
|
|
|
1407
|
|
|
1408 if (fEscapePressed || (grfKeyState & MK_RBUTTON) != 0) {
|
|
|
1409 return DRAGDROP_S_CANCEL;
|
|
|
1410 } else if (!(grfKeyState & MK_LBUTTON)) {
|
|
|
1411 if (m_effect == DROPEFFECT_NONE) return DRAGDROP_S_CANCEL;
|
|
|
1412
|
|
|
1413 CPoint pt;
|
|
|
1414 if (!GetCursorPos(&pt)) return DRAGDROP_S_CANCEL;
|
|
|
1415 bool bInside = false;
|
|
|
1416 if (wndOrigin) {
|
|
|
1417 CRect rc;
|
|
|
1418 WIN32_OP_D(wndOrigin.GetWindowRect(rc));
|
|
|
1419 bInside = rc.PtInRect(pt);
|
|
|
1420 }
|
|
|
1421 if (!allowDragOutside && !bInside) return DRAGDROP_S_CANCEL;
|
|
|
1422
|
|
|
1423 if ( allowReorder && bInside) {
|
|
|
1424 droppedAt = pt;
|
|
|
1425 droppedAtValid = true;
|
|
|
1426 return DRAGDROP_S_CANCEL;
|
|
|
1427 }
|
|
|
1428 return DRAGDROP_S_DROP;
|
|
|
1429
|
|
|
1430 } else {
|
|
|
1431 return S_OK;
|
|
|
1432 }
|
|
|
1433 }
|
|
|
1434 private:
|
|
|
1435 DWORD m_effect = 0;
|
|
|
1436 };
|
|
|
1437 }
|
|
|
1438
|
|
|
1439 void CListControlWithSelectionBase::RunDragDrop(const CPoint & p_origin, bool p_isRightClick) {
|
|
|
1440
|
|
|
1441 uint32_t flags = this->QueryDragDropTypes();
|
|
|
1442 if ( flags == 0 ) {
|
|
|
1443 PFC_ASSERT(!"How did we get here?");
|
|
|
1444 return;
|
|
|
1445 }
|
|
|
1446 if ( flags == dragDrop_reorder ) {
|
|
|
1447 if ( p_isRightClick ) return;
|
|
|
1448 CPoint ptDrop;
|
|
|
1449 if ( RunReorderDragDrop( p_origin, ptDrop ) ) {
|
|
|
1450 pfc::array_t<size_t> order;
|
|
|
1451 if (MakeDropReorderPermutation(order, ptDrop)) {
|
|
|
1452 this->RequestReorder(order.get_ptr(), order.get_size());
|
|
|
1453 }
|
|
|
1454 }
|
|
|
1455 return;
|
|
|
1456 }
|
|
|
1457
|
|
|
1458 auto obj = this->MakeDataObject();
|
|
|
1459 if (obj.is_empty()) {
|
|
|
1460 PFC_ASSERT(!"How did we get here? No IDataObject");
|
|
|
1461 return;
|
|
|
1462 }
|
|
|
1463
|
|
|
1464 pfc::com_ptr_t<CDropSourceImpl> source = new CDropSourceImpl();
|
|
|
1465 source->wndOrigin = m_hWnd;
|
|
|
1466 source->allowDragOutside = true;
|
|
|
1467 source->allowReorder = (flags & dragDrop_reorder) != 0;
|
|
|
1468
|
|
|
1469 DWORD outEffect = DROPEFFECT_NONE;
|
|
|
1470 HRESULT status = DoDragDrop(obj.get_ptr(), source.get_ptr(), DragDropSourceEffects() , &outEffect);
|
|
|
1471
|
|
|
1472 if ( source->droppedAtValid ) {
|
|
|
1473 CPoint ptDrop = source->droppedAt;
|
|
|
1474 WIN32_OP_D(this->ScreenToClient(&ptDrop));
|
|
|
1475 pfc::array_t<size_t> order;
|
|
|
1476 if (MakeDropReorderPermutation(order, ptDrop)) {
|
|
|
1477 this->RequestReorder(order.get_ptr(), order.get_size());
|
|
|
1478 }
|
|
|
1479 } else if (status == DRAGDROP_S_DROP) {
|
|
|
1480 DragDropSourceSucceeded(outEffect);
|
|
|
1481 }
|
|
|
1482 }
|
|
|
1483
|
|
|
1484 pfc::com_ptr_t<IDataObject> CListControlWithSelectionBase::MakeDataObject() {
|
|
|
1485 // return dummy IDataObject, presume derived transmits drag and drop payload by other means
|
|
|
1486 using namespace IDataObjectUtils;
|
|
|
1487 return new ImplementCOMRefCounter< CDataObjectBase >();
|
|
|
1488 }
|
|
|
1489
|
|
|
1490 bool CListControlWithSelectionBase::RunReorderDragDrop(CPoint ptOrigin, CPoint & ptDrop) {
|
|
|
1491 (void)ptOrigin;
|
|
|
1492 pfc::com_ptr_t<CDropSourceImpl> source = new CDropSourceImpl();
|
|
|
1493 pfc::com_ptr_t<CDropTargetImpl> target = new CDropTargetImpl();
|
|
|
1494
|
|
|
1495 source->wndOrigin = m_hWnd;
|
|
|
1496 source->allowDragOutside = false;
|
|
|
1497 source->allowReorder = true;
|
|
|
1498
|
|
|
1499 target->Track = [this](CPoint pt) {
|
|
|
1500 WIN32_OP_D(this->ScreenToClient(&pt));
|
|
|
1501 size_t idx = this->InsertIndexFromPoint(pt);
|
|
|
1502 this->SetDropMark(idx, false);
|
|
|
1503 };
|
|
|
1504
|
|
|
1505 if ( FAILED(RegisterDragDrop(*this, target.get_ptr())) ) {
|
|
|
1506 // OleInitialize not called?
|
|
|
1507 PFC_ASSERT( !"Should not get here" );
|
|
|
1508 return false;
|
|
|
1509 }
|
|
|
1510
|
|
|
1511 pfc::onLeaving scope([=] { target->valid = false; RevokeDragDrop(*this); });
|
|
|
1512
|
|
|
1513 using namespace IDataObjectUtils;
|
|
|
1514 pfc::com_ptr_t<IDataObject> dataobject = new ImplementCOMRefCounter< CDataObjectBase >();
|
|
|
1515 DWORD outeffect = 0;
|
|
|
1516
|
|
|
1517 ToggleDDScroll(true);
|
|
|
1518 DoDragDrop(dataobject.get_ptr(), source.get_ptr(), DROPEFFECT_MOVE, &outeffect);
|
|
|
1519 ClearDropMark();
|
|
|
1520 ToggleDDScroll(false);
|
|
|
1521 if (source->droppedAtValid ) {
|
|
|
1522 CPoint pt = source->droppedAt;
|
|
|
1523 WIN32_OP_D( this->ScreenToClient( &pt ) );
|
|
|
1524 ptDrop = pt;
|
|
|
1525 return true;
|
|
|
1526 }
|
|
|
1527 return false;
|
|
|
1528 }
|
|
|
1529
|
|
|
1530 int CListControlWithSelectionBase::OnCreatePassThru(LPCREATESTRUCT) {
|
|
|
1531 const uint32_t flags = this->QueryDragDropTypes();
|
|
|
1532 if ( flags & dragDrop_external ) {
|
|
|
1533
|
|
|
1534 pfc::com_ptr_t<CDropTargetImpl> target = new CDropTargetImpl();
|
|
|
1535
|
|
|
1536 auto dda = std::make_shared<dragDropAccept_t>();
|
|
|
1537
|
|
|
1538 target->HookAccept = [this, flags, dda] ( IDataObject * obj ) {
|
|
|
1539 if (this->m_ownDDActive && (flags & dragDrop_reorder) != 0) {
|
|
|
1540 // Do not generate OnDrop for reorderings
|
|
|
1541 dda->showDropMark = true;
|
|
|
1542 dda->dwEFfect = DROPEFFECT_MOVE;
|
|
|
1543 } else {
|
|
|
1544 *dda = this->DragDropAccept2(obj);
|
|
|
1545 }
|
|
|
1546 return dda->dwEFfect;
|
|
|
1547 };
|
|
|
1548 target->HookDrop = [this, flags] ( IDataObject * obj, CPoint pt ) {
|
|
|
1549 this->ToggleDDScroll(false);
|
|
|
1550 this->ClearDropMark();
|
|
|
1551 if ( this->m_ownDDActive ) {
|
|
|
1552 // Do not generate OnDrop for reorderings
|
|
|
1553 if ( flags & dragDrop_reorder ) return;
|
|
|
1554 }
|
|
|
1555 this->OnDrop( obj, pt );
|
|
|
1556 };
|
|
|
1557 target->HookLeave = [this] {
|
|
|
1558 this->ClearDropMark();
|
|
|
1559 this->ToggleDDScroll(false);
|
|
|
1560 };
|
|
|
1561
|
|
|
1562 target->Track = [this, dda](CPoint pt) {
|
|
|
1563 this->ToggleDDScroll(true);
|
|
|
1564 if ( dda->showDropMark ) {
|
|
|
1565 WIN32_OP_D(this->ScreenToClient(&pt));
|
|
|
1566 size_t idx = this->InsertIndexFromPoint(pt);
|
|
|
1567 if (dda->dropOnItem) {
|
|
|
1568 if (idx < this->GetItemCount()) {
|
|
|
1569 this->SetDropMark(idx, true);
|
|
|
1570 } else {
|
|
|
1571 this->ClearDropMark();
|
|
|
1572 }
|
|
|
1573 } else {
|
|
|
1574 this->SetDropMark(idx, false);
|
|
|
1575 }
|
|
|
1576
|
|
|
1577 } else {
|
|
|
1578 this->ClearDropMark();
|
|
|
1579 }
|
|
|
1580 };
|
|
|
1581
|
|
|
1582 RegisterDragDrop(*this, target.get_ptr() );
|
|
|
1583 }
|
|
|
1584 SetMsgHandled(FALSE); return 0;
|
|
|
1585 }
|
|
|
1586
|
|
|
1587 void CListControlWithSelectionBase::OnDestroyPassThru() {
|
|
|
1588 AbortSelectDragMode();
|
|
|
1589 ToggleDDScroll(false);
|
|
|
1590 RevokeDragDrop(*this);
|
|
|
1591 SetMsgHandled(FALSE);
|
|
|
1592 }
|
|
|
1593
|
|
|
1594 size_t CListControlWithSelectionBase::GetPasteTarget(const CPoint * ptPaste) const {
|
|
|
1595 size_t target = SIZE_MAX;
|
|
|
1596 if (ptPaste != nullptr) {
|
|
|
1597 CPoint pt(*ptPaste); WIN32_OP_D(ScreenToClient(&pt));
|
|
|
1598 size_t groupBase;
|
|
|
1599 if (GroupHeaderFromPoint2(pt, groupBase)) {
|
|
|
1600 target = groupBase;
|
|
|
1601 } else if (ItemFromPoint(pt, target)) {
|
|
|
1602 auto rc = GetItemRect(target);
|
|
|
1603 auto height = rc.Height();
|
|
|
1604 if (height > 0) {
|
|
|
1605 double posInItem = (double)(pt.y - rc.top) / (double)height;
|
|
|
1606 if (posInItem >= 0.5) ++target;
|
|
|
1607 }
|
|
|
1608 }
|
|
|
1609 } else if (GroupFocusActive()) {
|
|
|
1610 target = GetGroupFocus2();
|
|
|
1611 } else {
|
|
|
1612 target = GetFocusItem();
|
|
|
1613 }
|
|
|
1614 return target;
|
|
|
1615 }
|
|
|
1616
|
|
|
1617
|
|
|
1618 pfc::bit_array_table CListControlWithSelectionImpl::GetSelectionMaskRef() const {
|
|
|
1619 return pfc::bit_array_table(m_selection.get_ptr(), m_selection.get_size());
|
|
|
1620 }
|
|
|
1621 pfc::bit_array_bittable CListControlWithSelectionImpl::GetSelectionMask() const {
|
|
|
1622 pfc::bit_array_bittable ret;
|
|
|
1623 const auto count = GetItemCount();
|
|
|
1624 ret.resize( GetItemCount() );
|
|
|
1625 for( size_t walk = 0; walk < count; ++ walk ) {
|
|
|
1626 ret.set(walk, IsItemSelected(walk));
|
|
|
1627 }
|
|
|
1628 return ret;
|
|
|
1629 }
|
|
|
1630
|
|
|
1631 void CListControlWithSelectionImpl::OnItemsReordered( const size_t * order, size_t count) {
|
|
|
1632 PFC_ASSERT( count == GetItemCount() );
|
|
|
1633
|
|
|
1634 SelHandleReorder( order, count );
|
|
|
1635 __super::OnItemsReordered(order, count);
|
|
|
1636 }
|
|
|
1637
|
|
|
1638 void CListControlWithSelectionImpl::OnItemsRemoved( pfc::bit_array const & mask, size_t oldCount) {
|
|
|
1639 SelHandleRemoval(mask, oldCount);
|
|
|
1640 __super::OnItemsRemoved( mask, oldCount );
|
|
|
1641 }
|
|
|
1642
|
|
|
1643 void CListControlWithSelectionImpl::OnItemsInsertedEx(pfc::bit_array const& mask, size_t oldCount, size_t newCount, bool bSelect) {
|
|
|
1644 SelHandleInsertion( mask, oldCount, newCount, bSelect);
|
|
|
1645 __super::OnItemsInsertedEx( mask, oldCount, newCount, bSelect );
|
|
|
1646 }
|
|
|
1647
|
|
|
1648 bool CListControlWithSelectionImpl::SelectAll() {
|
|
|
1649 if ( m_selectionSupport != selectionSupportMulti ) return false;
|
|
|
1650 return __super::SelectAll();
|
|
|
1651 }
|
|
|
1652
|
|
|
1653 DWORD CListControlWithSelectionBase::DragDropAccept(IDataObject* obj, bool& showDropMark) {
|
|
|
1654 (void)obj;
|
|
|
1655 showDropMark = false; return DROPEFFECT_NONE;
|
|
|
1656 }
|
|
|
1657
|
|
|
1658 CListControlWithSelectionBase::dragDropAccept_t CListControlWithSelectionBase::DragDropAccept2(IDataObject* obj) {
|
|
|
1659 dragDropAccept_t ret;
|
|
|
1660 ret.dwEFfect = this->DragDropAccept(obj, ret.showDropMark);
|
|
|
1661 return ret;
|
|
|
1662 }
|