1 // PanelItems.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../../C/Sort.h"
6
7 #include "../../../Windows/FileName.h"
8 #include "../../../Windows/Menu.h"
9 #include "../../../Windows/PropVariant.h"
10 #include "../../../Windows/PropVariantConv.h"
11
12 #include "../../PropID.h"
13
14 #include "../Common/ExtractingFilePath.h"
15
16 #include "resource.h"
17
18 #include "LangUtils.h"
19 #include "Panel.h"
20 #include "PropertyName.h"
21 #include "RootFolder.h"
22
23 using namespace NWindows;
24
GetColumnVisible(PROPID propID,bool isFsFolder)25 static bool GetColumnVisible(PROPID propID, bool isFsFolder)
26 {
27 if (isFsFolder)
28 {
29 switch (propID)
30 {
31 case kpidATime:
32 case kpidChangeTime:
33 case kpidAttrib:
34 case kpidPackSize:
35 case kpidINode:
36 case kpidLinks:
37 case kpidNtReparse:
38 return false;
39 }
40 }
41 return true;
42 }
43
GetColumnWidth(PROPID propID,VARTYPE)44 static unsigned GetColumnWidth(PROPID propID, VARTYPE /* varType */)
45 {
46 switch (propID)
47 {
48 case kpidName: return 160;
49 }
50 return 100;
51 }
52
GetColumnAlign(PROPID propID,VARTYPE varType)53 static int GetColumnAlign(PROPID propID, VARTYPE varType)
54 {
55 switch (propID)
56 {
57 case kpidCTime:
58 case kpidATime:
59 case kpidMTime:
60 case kpidChangeTime:
61 return LVCFMT_LEFT;
62 }
63
64 switch (varType)
65 {
66 case VT_UI1:
67 case VT_I2:
68 case VT_UI2:
69 case VT_I4:
70 case VT_INT:
71 case VT_UI4:
72 case VT_UINT:
73 case VT_I8:
74 case VT_UI8:
75 case VT_BOOL:
76 return LVCFMT_RIGHT;
77
78 case VT_EMPTY:
79 case VT_I1:
80 case VT_FILETIME:
81 case VT_BSTR:
82 return LVCFMT_LEFT;
83
84 default:
85 return LVCFMT_CENTER;
86 }
87 }
88
89
ItemProperty_Compare_NameFirst(void * const * a1,void * const * a2,void *)90 static int ItemProperty_Compare_NameFirst(void *const *a1, void *const *a2, void * /* param */)
91 {
92 return (*(*((const CPropColumn *const *)a1))).Compare_NameFirst
93 (*(*((const CPropColumn *const *)a2)));
94 }
95
InitColumns()96 HRESULT CPanel::InitColumns()
97 {
98 SaveListViewInfo();
99
100 // DeleteListItems();
101 _selectedStatusVector.Clear();
102
103 {
104 // ReadListViewInfo();
105 const UString oldType = _typeIDString;
106 _typeIDString = GetFolderTypeID();
107 // an empty _typeIDString is allowed.
108
109 // we read registry only for new FolderTypeID
110 if (!_needSaveInfo || _typeIDString != oldType)
111 _listViewInfo.Read(_typeIDString);
112
113 // folders with same FolderTypeID can have different columns
114 // so we still read columns for that case.
115 // if (_needSaveInfo && _typeIDString == oldType) return S_OK;
116 }
117
118 // PROPID sortID;
119 /*
120 if (_listViewInfo.SortIndex >= 0)
121 sortID = _listViewInfo.Columns[_listViewInfo.SortIndex].PropID;
122 */
123 // sortID = _listViewInfo.SortID;
124
125 _ascending = _listViewInfo.Ascending;
126
127 _columns.Clear();
128
129 const bool isFsFolder = IsFSFolder() || IsAltStreamsFolder();
130
131 {
132 UInt32 numProps;
133 _folder->GetNumberOfProperties(&numProps);
134
135 for (UInt32 i = 0; i < numProps; i++)
136 {
137 CMyComBSTR name;
138 PROPID propID;
139 VARTYPE varType;
140 HRESULT res = _folder->GetPropertyInfo(i, &name, &propID, &varType);
141
142 if (res != S_OK)
143 {
144 /* We can return ERROR, but in that case, other code will not be called,
145 and user can see empty window without error message. So we just ignore that field */
146 continue;
147 }
148 if (propID == kpidIsDir)
149 continue;
150 CPropColumn prop;
151 prop.Type = varType;
152 prop.ID = propID;
153 prop.Name = GetNameOfProperty(propID, name);
154 prop.Order = -1;
155 prop.IsVisible = GetColumnVisible(propID, isFsFolder);
156 prop.Width = GetColumnWidth(propID, varType);
157 prop.IsRawProp = false;
158 _columns.Add(prop);
159 }
160
161 /*
162 {
163 // debug column
164 CPropColumn prop;
165 prop.Type = VT_BSTR;
166 prop.ID = 2000;
167 prop.Name = "Debug";
168 prop.Order = -1;
169 prop.IsVisible = true;
170 prop.Width = 300;
171 prop.IsRawProp = false;
172 _columns.Add(prop);
173 }
174 */
175 }
176
177 if (_folderRawProps)
178 {
179 UInt32 numProps;
180 _folderRawProps->GetNumRawProps(&numProps);
181
182 for (UInt32 i = 0; i < numProps; i++)
183 {
184 CMyComBSTR name;
185 PROPID propID;
186 const HRESULT res = _folderRawProps->GetRawPropInfo(i, &name, &propID);
187 if (res != S_OK)
188 continue;
189 CPropColumn prop;
190 prop.Type = VT_EMPTY;
191 prop.ID = propID;
192 prop.Name = GetNameOfProperty(propID, name);
193 prop.Order = -1;
194 prop.IsVisible = GetColumnVisible(propID, isFsFolder);
195 prop.Width = GetColumnWidth(propID, VT_BSTR);
196 prop.IsRawProp = true;
197 _columns.Add(prop);
198 }
199 }
200
201 unsigned order = 0;
202 unsigned i;
203
204 for (i = 0; i < _listViewInfo.Columns.Size(); i++)
205 {
206 const CColumnInfo &columnInfo = _listViewInfo.Columns[i];
207 const int index = _columns.FindItem_for_PropID(columnInfo.PropID);
208 if (index >= 0)
209 {
210 CPropColumn &item = _columns[index];
211 if (item.Order >= 0)
212 continue; // we ignore duplicated items
213 bool isVisible = columnInfo.IsVisible;
214 // we enable kpidName, if it was disabled by some incorrect code
215 if (columnInfo.PropID == kpidName)
216 isVisible = true;
217 item.IsVisible = isVisible;
218 item.Width = columnInfo.Width;
219 if (isVisible)
220 item.Order = (int)(order++);
221 continue;
222 }
223 }
224
225 for (i = 0; i < _columns.Size(); i++)
226 {
227 CPropColumn &item = _columns[i];
228 if (item.IsVisible && item.Order < 0)
229 item.Order = (int)(order++);
230 }
231
232 for (i = 0; i < _columns.Size(); i++)
233 {
234 CPropColumn &item = _columns[i];
235 if (item.Order < 0)
236 item.Order = (int)(order++);
237 }
238
239 CPropColumns newColumns;
240
241 for (i = 0; i < _columns.Size(); i++)
242 {
243 const CPropColumn &prop = _columns[i];
244 if (prop.IsVisible)
245 newColumns.Add(prop);
246 }
247
248
249 /*
250 _sortIndex = 0;
251 if (_listViewInfo.SortIndex >= 0)
252 {
253 int sortIndex = _columns.FindItem_for_PropID(sortID);
254 if (sortIndex >= 0)
255 _sortIndex = sortIndex;
256 }
257 */
258
259 if (_listViewInfo.IsLoaded)
260 _sortID = _listViewInfo.SortID;
261 else
262 {
263 _sortID = 0;
264 if (IsFSFolder() || IsAltStreamsFolder() || IsArcFolder())
265 _sortID = kpidName;
266 }
267
268 /* There are restrictions in ListView control:
269 1) main column (kpidName) must have (LV_COLUMNW::iSubItem = 0)
270 So we need special sorting for columns.
271 2) when we add new column, LV_COLUMNW::iOrder cannot be larger than already inserted columns)
272 So we set column order after all columns are added.
273 */
274 newColumns.Sort(ItemProperty_Compare_NameFirst, NULL);
275
276 if (newColumns.IsEqualTo(_visibleColumns))
277 return S_OK;
278
279 CIntArr columns(newColumns.Size());
280 for (i = 0; i < newColumns.Size(); i++)
281 columns[i] = -1;
282
283 bool orderError = false;
284
285 for (i = 0; i < newColumns.Size(); i++)
286 {
287 const CPropColumn &prop = newColumns[i];
288 if (prop.Order < (int)newColumns.Size() && columns[prop.Order] == -1)
289 columns[prop.Order] = (int)i;
290 else
291 orderError = true;
292 }
293
294 for (;;)
295 {
296 const unsigned numColumns = _visibleColumns.Size();
297 if (numColumns == 0)
298 break;
299 DeleteColumn(numColumns - 1);
300 }
301
302 for (i = 0; i < newColumns.Size(); i++)
303 AddColumn(newColumns[i]);
304
305 // columns[0], columns[1], .... should be displayed from left to right:
306 if (!orderError)
307 _listView.SetColumnOrderArray(_visibleColumns.Size(), columns);
308
309 _needSaveInfo = true;
310
311 return S_OK;
312 }
313
314
DeleteColumn(unsigned index)315 void CPanel::DeleteColumn(unsigned index)
316 {
317 _visibleColumns.Delete(index);
318 _listView.DeleteColumn(index);
319 }
320
AddColumn(const CPropColumn & prop)321 void CPanel::AddColumn(const CPropColumn &prop)
322 {
323 const unsigned index = _visibleColumns.Size();
324
325 LV_COLUMNW column;
326 column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER;
327 column.cx = (int)prop.Width;
328 column.fmt = GetColumnAlign(prop.ID, prop.Type);
329 column.iOrder = (int)index; // must be <= _listView.ItemCount
330 column.iSubItem = (int)index; // must be <= _listView.ItemCount
331 column.pszText = const_cast<wchar_t *>((const wchar_t *)prop.Name);
332
333 _visibleColumns.Add(prop);
334 _listView.InsertColumn(index, &column);
335 }
336
337
RefreshListCtrl()338 HRESULT CPanel::RefreshListCtrl()
339 {
340 CSelectedState state;
341 return RefreshListCtrl(state);
342 }
343
344 int CALLBACK CompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData);
345
346
GetSelectedNames(UStringVector & selectedNames)347 void CPanel::GetSelectedNames(UStringVector &selectedNames)
348 {
349 CRecordVector<UInt32> indices;
350 Get_ItemIndices_Selected(indices);
351 selectedNames.ClearAndReserve(indices.Size());
352 FOR_VECTOR (i, indices)
353 selectedNames.AddInReserved(GetItemRelPath(indices[i]));
354
355 /*
356 for (int i = 0; i < _listView.GetItemCount(); i++)
357 {
358 const int kSize = 1024;
359 WCHAR name[kSize + 1];
360 LVITEMW item;
361 item.iItem = i;
362 item.pszText = name;
363 item.cchTextMax = kSize;
364 item.iSubItem = 0;
365 item.mask = LVIF_TEXT | LVIF_PARAM;
366 if (!_listView.GetItem(&item))
367 continue;
368 const unsigned realIndex = GetRealIndex(item);
369 if (realIndex == kParentIndex)
370 continue;
371 if (_selectedStatusVector[realIndex])
372 selectedNames.Add(item.pszText);
373 }
374 */
375 selectedNames.Sort();
376 }
377
SaveSelectedState(CSelectedState & s)378 void CPanel::SaveSelectedState(CSelectedState &s)
379 {
380 s.FocusedName_Defined = false;
381 s.FocusedName.Empty();
382 s.SelectFocused = true; // false;
383 s.SelectedNames.Clear();
384 s.FocusedItem = _listView.GetFocusedItem();
385 {
386 if (s.FocusedItem >= 0)
387 {
388 const unsigned realIndex = GetRealItemIndex(s.FocusedItem);
389 if (realIndex != kParentIndex)
390 {
391 s.FocusedName = GetItemRelPath(realIndex);
392 s.FocusedName_Defined = true;
393
394 s.SelectFocused = _listView.IsItemSelected(s.FocusedItem);
395
396 /*
397 const int kSize = 1024;
398 WCHAR name[kSize + 1];
399 LVITEMW item;
400 item.iItem = focusedItem;
401 item.pszText = name;
402 item.cchTextMax = kSize;
403 item.iSubItem = 0;
404 item.mask = LVIF_TEXT;
405 if (_listView.GetItem(&item))
406 focusedName = item.pszText;
407 */
408 }
409 }
410 }
411 GetSelectedNames(s.SelectedNames);
412 }
413
414 /*
415 HRESULT CPanel::RefreshListCtrl(const CSelectedState &s)
416 {
417 bool selectFocused = s.SelectFocused;
418 if (_mySelectMode)
419 selectFocused = true;
420 return RefreshListCtrl2(
421 s.FocusedItem >= 0, // allowEmptyFocusedName
422 s.FocusedName, s.FocusedItem, selectFocused, s.SelectedNames);
423 }
424 */
425
RefreshListCtrl_SaveFocused(bool onTimer)426 HRESULT CPanel::RefreshListCtrl_SaveFocused(bool onTimer)
427 {
428 CSelectedState state;
429 SaveSelectedState(state);
430 state.CalledFromTimer = onTimer;
431 return RefreshListCtrl(state);
432 }
433
SetFocusedSelectedItem(int index,bool select)434 void CPanel::SetFocusedSelectedItem(int index, bool select)
435 {
436 UINT state = LVIS_FOCUSED;
437 if (select)
438 state |= LVIS_SELECTED;
439 _listView.SetItemState(index, state, state);
440 if (!_mySelectMode && select)
441 {
442 const unsigned realIndex = GetRealItemIndex(index);
443 if (realIndex != kParentIndex)
444 _selectedStatusVector[realIndex] = true;
445 }
446 }
447
448 // #define PRINT_STAT
449
450 #ifdef PRINT_STAT
451 void Print_OnNotify(const char *name);
452 #else
453 #define Print_OnNotify(x)
454 #endif
455
456
457
458 /*
459
460 extern UInt32 g_NumGroups;
461 extern DWORD g_start_tick;
462 extern DWORD g_prev_tick;
463 extern DWORD g_Num_SetItemText;
464 extern UInt32 g_NumMessages;
465 */
466
RefreshListCtrl(const CSelectedState & state)467 HRESULT CPanel::RefreshListCtrl(const CSelectedState &state)
468 {
469 m_DropHighlighted_SelectionIndex = -1;
470 m_DropHighlighted_SubFolderName.Empty();
471
472 if (!_folder)
473 return S_OK;
474
475 /*
476 g_start_tick = GetTickCount();
477 g_Num_SetItemText = 0;
478 g_NumMessages = 0;
479 */
480
481 _dontShowMode = false;
482 if (!state.CalledFromTimer)
483 LoadFullPathAndShow();
484 // OutputDebugStringA("=======\n");
485 // OutputDebugStringA("s1 \n");
486 CDisableTimerProcessing timerProcessing(*this);
487 CDisableNotify disableNotify(*this);
488
489 int focusedPos = state.FocusedItem;
490 if (focusedPos < 0)
491 focusedPos = 0;
492
493 _listView.SetRedraw(false);
494 // m_RedrawEnabled = false;
495
496 LVITEMW item;
497 ZeroMemory(&item, sizeof(item));
498
499 // DWORD tickCount0 = GetTickCount();
500
501 // _enableItemChangeNotify = false;
502 DeleteListItems();
503 _enableItemChangeNotify = true;
504
505 int listViewItemCount = 0;
506
507 _selectedStatusVector.Clear();
508 // _realIndices.Clear();
509 _startGroupSelect = 0;
510
511 _selectionIsDefined = false;
512
513 // m_Files.Clear();
514
515 /*
516 if (!_folder)
517 {
518 // throw 1;
519 SetToRootFolder();
520 }
521 */
522
523 _headerToolBar.EnableButton(kParentFolderID, !IsRootFolder());
524
525 {
526 CMyComPtr<IFolderSetFlatMode> folderSetFlatMode;
527 _folder.QueryInterface(IID_IFolderSetFlatMode, &folderSetFlatMode);
528 if (folderSetFlatMode)
529 folderSetFlatMode->SetFlatMode(BoolToInt(_flatMode));
530 }
531
532 /*
533 {
534 CMyComPtr<IFolderSetShowNtfsStreamsMode> setShow;
535 _folder.QueryInterface(IID_IFolderSetShowNtfsStreamsMode, &setShow);
536 if (setShow)
537 setShow->SetShowNtfsStreamsMode(BoolToInt(_showNtfsStrems_Mode));
538 }
539 */
540
541 _isDirVector.Clear();
542 // DWORD tickCount1 = GetTickCount();
543 IFolderFolder *folder = _folder;
544 RINOK(_folder->LoadItems())
545 // DWORD tickCount2 = GetTickCount();
546 // OutputDebugString(TEXT("Start Dir\n"));
547 RINOK(InitColumns())
548
549 UInt32 numItems;
550 _folder->GetNumberOfItems(&numItems);
551 {
552 NCOM::CPropVariant prop;
553 _isDirVector.ClearAndSetSize(numItems);
554 bool *vec = _isDirVector.NonConstData();
555 HRESULT hres = S_OK;
556 unsigned i;
557 for (i = 0; i < numItems; i++)
558 {
559 hres = folder->GetProperty(i, kpidIsDir, &prop);
560 if (hres != S_OK)
561 break;
562 bool v = false;
563 if (prop.vt == VT_BOOL)
564 v = VARIANT_BOOLToBool(prop.boolVal);
565 else if (prop.vt != VT_EMPTY)
566 break;
567 vec[i] = v;
568 }
569 if (i != numItems)
570 {
571 _isDirVector.Clear();
572 if (hres == S_OK)
573 hres = E_FAIL;
574 }
575 RINOK(hres)
576 }
577
578 const bool showDots = _showDots && !IsRootFolder();
579
580 _listView.SetItemCount(numItems + (showDots ? 1 : 0));
581
582 _selectedStatusVector.ClearAndReserve(numItems);
583 int cursorIndex = -1;
584
585 CMyComPtr<IFolderGetSystemIconIndex> folderGetSystemIconIndex;
586 if (!Is_Slow_Icon_Folder() || _showRealFileIcons)
587 _folder.QueryInterface(IID_IFolderGetSystemIconIndex, &folderGetSystemIconIndex);
588
589 if (!IsFSFolder())
590 {
591 CMyComPtr<IGetFolderArcProps> getFolderArcProps;
592 _folder.QueryInterface(IID_IGetFolderArcProps, &getFolderArcProps);
593 _thereAreDeletedItems = false;
594 if (getFolderArcProps)
595 {
596 CMyComPtr<IFolderArcProps> arcProps;
597 getFolderArcProps->GetFolderArcProps(&arcProps);
598 if (arcProps)
599 {
600 UInt32 numLevels;
601 if (arcProps->GetArcNumLevels(&numLevels) != S_OK)
602 numLevels = 0;
603 NCOM::CPropVariant prop;
604 if (arcProps->GetArcProp(numLevels - 1, kpidIsDeleted, &prop) == S_OK)
605 if (prop.vt == VT_BOOL && VARIANT_BOOLToBool(prop.boolVal))
606 _thereAreDeletedItems = true;
607 }
608 }
609 }
610
611 _thereAre_ListView_Items = true;
612
613 // OutputDebugStringA("\n\n");
614
615 Print_OnNotify("===== Before Load");
616
617 // #define USE_EMBED_ITEM
618
619 if (showDots)
620 {
621 const UString itemName ("..");
622 item.iItem = listViewItemCount;
623 if (itemName == state.FocusedName)
624 cursorIndex = listViewItemCount;
625 item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
626 int subItem = 0;
627 item.iSubItem = subItem++;
628 item.lParam = (LPARAM)(int)kParentIndex;
629 #ifdef USE_EMBED_ITEM
630 item.pszText = const_cast<wchar_t *>((const wchar_t *)itemName);
631 #else
632 item.pszText = LPSTR_TEXTCALLBACKW;
633 #endif
634 const UInt32 attrib = FILE_ATTRIBUTE_DIRECTORY;
635 item.iImage = _extToIconMap.GetIconIndex(attrib, itemName);
636 if (item.iImage < 0)
637 item.iImage = 0;
638 if (_listView.InsertItem(&item) == -1)
639 return E_FAIL;
640 listViewItemCount++;
641 }
642
643 // OutputDebugStringA("S1\n");
644
645 UString correctedName;
646 UString itemName;
647 UString relPath;
648
649 for (UInt32 i = 0; i < numItems; i++)
650 {
651 const wchar_t *name = NULL;
652 unsigned nameLen = 0;
653
654 if (_folderGetItemName)
655 _folderGetItemName->GetItemName(i, &name, &nameLen);
656 if (!name)
657 {
658 GetItemName(i, itemName);
659 name = itemName;
660 nameLen = itemName.Len();
661 }
662
663 bool selected = false;
664
665 if (state.FocusedName_Defined || !state.SelectedNames.IsEmpty())
666 {
667 relPath.Empty();
668 // relPath += GetItemPrefix(i);
669 if (_flatMode)
670 {
671 const wchar_t *prefix = NULL;
672 if (_folderGetItemName)
673 {
674 unsigned prefixLen = 0;
675 _folderGetItemName->GetItemPrefix(i, &prefix, &prefixLen);
676 if (prefix)
677 relPath = prefix;
678 }
679 if (!prefix)
680 {
681 NCOM::CPropVariant prop;
682 if (_folder->GetProperty(i, kpidPrefix, &prop) != S_OK)
683 throw 2723400;
684 if (prop.vt == VT_BSTR)
685 relPath.SetFromBstr(prop.bstrVal);
686 }
687 }
688 relPath += name;
689 if (relPath == state.FocusedName)
690 cursorIndex = listViewItemCount;
691 if (state.SelectedNames.FindInSorted(relPath) != -1)
692 selected = true;
693 }
694
695 _selectedStatusVector.AddInReserved(selected);
696
697 item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
698
699 if (!_mySelectMode)
700 if (selected)
701 {
702 item.mask |= LVIF_STATE;
703 item.state = LVIS_SELECTED;
704 }
705
706 int subItem = 0;
707 item.iItem = listViewItemCount;
708
709 item.iSubItem = subItem++;
710 item.lParam = (LPARAM)i;
711
712 /*
713 int finish = nameLen - 4;
714 int j;
715 for (j = 0; j < finish; j++)
716 {
717 if (name[j ] == ' ' &&
718 name[j + 1] == ' ' &&
719 name[j + 2] == ' ' &&
720 name[j + 3] == ' ' &&
721 name[j + 4] == ' ')
722 break;
723 }
724 if (j < finish)
725 {
726 correctedName.Empty();
727 correctedName = "virus";
728 int pos = 0;
729 for (;;)
730 {
731 int posNew = itemName.Find(L" ", pos);
732 if (posNew < 0)
733 {
734 correctedName += itemName.Ptr(pos);
735 break;
736 }
737 correctedName += itemName.Mid(pos, posNew - pos);
738 correctedName += " ... ";
739 pos = posNew;
740 while (itemName[++pos] == ' ');
741 }
742 item.pszText = const_cast<wchar_t *>((const wchar_t *)correctedName);
743 }
744 else
745 */
746 {
747 #ifdef USE_EMBED_ITEM
748 item.pszText = const_cast<wchar_t *>((const wchar_t *)name);
749 #else
750 item.pszText = LPSTR_TEXTCALLBACKW;
751 #endif
752 /* LPSTR_TEXTCALLBACKW works, but in some cases there are problems,
753 since we block notify handler.
754 LPSTR_TEXTCALLBACKW can be 2-3 times faster for loading in this loop. */
755 }
756
757 bool defined = false;
758
759 if (folderGetSystemIconIndex)
760 {
761 folderGetSystemIconIndex->GetSystemIconIndex(i, &item.iImage);
762 defined = (item.iImage > 0);
763 }
764
765 if (!defined)
766 {
767 UInt32 attrib = 0;
768 {
769 NCOM::CPropVariant prop;
770 RINOK(_folder->GetProperty(i, kpidAttrib, &prop))
771 if (prop.vt == VT_UI4)
772 attrib = prop.ulVal;
773 }
774 if (IsItem_Folder(i))
775 attrib |= FILE_ATTRIBUTE_DIRECTORY;
776
777 if (_currentFolderPrefix.IsEmpty())
778 {
779 int iconIndexTemp;
780 GetRealIconIndex(us2fs((UString)name) + FCHAR_PATH_SEPARATOR, attrib, iconIndexTemp);
781 item.iImage = iconIndexTemp;
782 }
783 else
784 {
785 item.iImage = _extToIconMap.GetIconIndex(attrib, name);
786 }
787 }
788
789 if (item.iImage < 0)
790 item.iImage = 0;
791
792 if (_listView.InsertItem(&item) == -1)
793 return E_FAIL;
794 listViewItemCount++;
795 }
796
797 /*
798 xp-64: there is different order when Windows calls CPanel::OnNotify for _listView modes:
799 Details : after whole code
800 List : 2 times:
801 1) - ListView.SotRedraw()
802 2) - after whole code
803 Small Icons :
804 Large icons : 2 times:
805 1) - ListView.Sort()
806 2) - after whole code (calls with reverse order of items)
807
808 So we need to allow Notify(), when windows requests names during the following code.
809 */
810
811 Print_OnNotify("after Load");
812
813 disableNotify.SetMemMode_Enable();
814 disableNotify.Restore();
815
816 if (_listView.GetItemCount() > 0 && cursorIndex >= 0)
817 SetFocusedSelectedItem(cursorIndex, state.SelectFocused);
818
819 Print_OnNotify("after SetFocusedSelectedItem");
820
821 SetSortRawStatus();
822 _listView.SortItems(CompareItems, (LPARAM)this);
823
824 Print_OnNotify("after Sort");
825
826 if (cursorIndex < 0 && _listView.GetItemCount() > 0)
827 {
828 if (focusedPos >= _listView.GetItemCount())
829 focusedPos = _listView.GetItemCount() - 1;
830 // we select item only in showDots mode.
831 SetFocusedSelectedItem(focusedPos, showDots && (focusedPos == 0));
832 }
833
834 // m_RedrawEnabled = true;
835
836 Print_OnNotify("after SetFocusedSelectedItem2");
837
838 _listView.EnsureVisible(_listView.GetFocusedItem(), false);
839
840 // disableNotify.SetMemMode_Enable();
841 // disableNotify.Restore();
842
843 Print_OnNotify("after EnsureVisible");
844
845 _listView.SetRedraw(true);
846
847 Print_OnNotify("after SetRedraw");
848
849 _listView.InvalidateRect(NULL, true);
850
851 Print_OnNotify("after InvalidateRect");
852 /*
853 _listView.UpdateWindow();
854 */
855 Refresh_StatusBar();
856 /*
857 char s[256];
858 sprintf(s,
859 // "attribMap = %5d, extMap = %5d, "
860 "delete = %5d, load = %5d, list = %5d, sort = %5d, end = %5d",
861 // _extToIconMap._attribMap.Size(),
862 // _extToIconMap._extMap.Size(),
863 tickCount1 - tickCount0,
864 tickCount2 - tickCount1,
865 tickCount3 - tickCount2,
866 tickCount4 - tickCount3,
867 tickCount5 - tickCount4
868 );
869 sprintf(s,
870 "5 = %5d, 6 = %5d, 7 = %5d, 8 = %5d, 9 = %5d",
871 tickCount5 - tickCount4,
872 tickCount6 - tickCount5,
873 tickCount7 - tickCount6,
874 tickCount8 - tickCount7,
875 tickCount9 - tickCount8
876 );
877 OutputDebugStringA(s);
878 */
879 return S_OK;
880 }
881
882
Get_ItemIndices_Selected(CRecordVector<UInt32> & indices) const883 void CPanel::Get_ItemIndices_Selected(CRecordVector<UInt32> &indices) const
884 {
885 indices.Clear();
886 /*
887 int itemIndex = -1;
888 while ((itemIndex = _listView.GetNextSelectedItem(itemIndex)) != -1)
889 {
890 LPARAM param;
891 if (_listView.GetItemParam(itemIndex, param))
892 indices.Add(param);
893 }
894 HeapSort(&indices.Front(), indices.Size());
895 */
896 const bool *v = _selectedStatusVector.ConstData();
897 const unsigned size = _selectedStatusVector.Size();
898 for (unsigned i = 0; i < size; i++)
899 if (v[i])
900 indices.Add(i);
901 }
902
903
Get_ItemIndices_Operated(CRecordVector<UInt32> & indices) const904 void CPanel::Get_ItemIndices_Operated(CRecordVector<UInt32> &indices) const
905 {
906 Get_ItemIndices_Selected(indices);
907 if (!indices.IsEmpty())
908 return;
909 if (_listView.GetSelectedCount() == 0)
910 return;
911 const int focusedItem = _listView.GetFocusedItem();
912 if (focusedItem >= 0)
913 {
914 if (_listView.IsItemSelected(focusedItem))
915 {
916 const unsigned realIndex = GetRealItemIndex(focusedItem);
917 if (realIndex != kParentIndex)
918 indices.Add(realIndex);
919 }
920 }
921 }
922
Get_ItemIndices_All(CRecordVector<UInt32> & indices) const923 void CPanel::Get_ItemIndices_All(CRecordVector<UInt32> &indices) const
924 {
925 indices.Clear();
926 UInt32 numItems;
927 if (_folder->GetNumberOfItems(&numItems) != S_OK)
928 return;
929 indices.ClearAndSetSize(numItems);
930 UInt32 *vec = indices.NonConstData();
931 for (UInt32 i = 0; i < numItems; i++)
932 vec[i] = i;
933 }
934
Get_ItemIndices_OperSmart(CRecordVector<UInt32> & indices) const935 void CPanel::Get_ItemIndices_OperSmart(CRecordVector<UInt32> &indices) const
936 {
937 Get_ItemIndices_Operated(indices);
938 if (indices.IsEmpty() || (indices.Size() == 1 && indices[0] == (UInt32)(Int32)-1))
939 Get_ItemIndices_All(indices);
940 }
941
942 /*
943 void CPanel::GetOperatedListViewIndices(CRecordVector<UInt32> &indices) const
944 {
945 indices.Clear();
946 int numItems = _listView.GetItemCount();
947 for (int i = 0; i < numItems; i++)
948 {
949 const unsigned realIndex = GetRealItemIndex(i);
950 if (realIndex >= 0)
951 if (_selectedStatusVector[realIndex])
952 indices.Add(i);
953 }
954 if (indices.IsEmpty())
955 {
956 const int focusedItem = _listView.GetFocusedItem();
957 if (focusedItem >= 0)
958 indices.Add(focusedItem);
959 }
960 }
961 */
962
EditItem(bool useEditor)963 void CPanel::EditItem(bool useEditor)
964 {
965 if (!useEditor)
966 {
967 CMyComPtr<IFolderCalcItemFullSize> calcItemFullSize;
968 _folder.QueryInterface(IID_IFolderCalcItemFullSize, &calcItemFullSize);
969 if (calcItemFullSize)
970 {
971 bool needRefresh = false;
972 CRecordVector<UInt32> indices;
973 Get_ItemIndices_Operated(indices);
974 FOR_VECTOR (i, indices)
975 {
976 UInt32 index = indices[i];
977 if (IsItem_Folder(index))
978 {
979 calcItemFullSize->CalcItemFullSize(index, NULL);
980 needRefresh = true;
981 }
982 }
983 if (needRefresh)
984 {
985 // _listView.RedrawItem(0);
986 // _listView.RedrawAllItems();
987 InvalidateList();
988 return;
989 }
990 }
991 }
992
993
994 const int focusedItem = _listView.GetFocusedItem();
995 if (focusedItem < 0)
996 return;
997 const unsigned realIndex = GetRealItemIndex(focusedItem);
998 if (realIndex == kParentIndex)
999 return;
1000 if (!IsItem_Folder(realIndex))
1001 EditItem(realIndex, useEditor);
1002 }
1003
OpenFocusedItemAsInternal(const wchar_t * type)1004 void CPanel::OpenFocusedItemAsInternal(const wchar_t *type)
1005 {
1006 const int focusedItem = _listView.GetFocusedItem();
1007 if (focusedItem < 0)
1008 return;
1009 const unsigned realIndex = GetRealItemIndex(focusedItem);
1010 if (IsItem_Folder(realIndex))
1011 OpenFolder(realIndex);
1012 else
1013 OpenItem(realIndex, true, false, type);
1014 }
1015
OpenSelectedItems(bool tryInternal)1016 void CPanel::OpenSelectedItems(bool tryInternal)
1017 {
1018 CRecordVector<UInt32> indices;
1019 Get_ItemIndices_Operated(indices);
1020 if (indices.Size() > 20)
1021 {
1022 MessageBox_Error_LangID(IDS_TOO_MANY_ITEMS);
1023 return;
1024 }
1025
1026 const int focusedItem = _listView.GetFocusedItem();
1027 if (focusedItem >= 0)
1028 {
1029 const unsigned realIndex = GetRealItemIndex(focusedItem);
1030 if (realIndex == kParentIndex && (tryInternal || indices.Size() == 0) && _listView.IsItemSelected(focusedItem))
1031 indices.Insert(0, realIndex);
1032 }
1033
1034 bool dirIsStarted = false;
1035 FOR_VECTOR (i, indices)
1036 {
1037 UInt32 index = indices[i];
1038 // CFileInfo &aFile = m_Files[index];
1039 if (IsItem_Folder(index))
1040 {
1041 if (!dirIsStarted)
1042 {
1043 if (tryInternal)
1044 {
1045 OpenFolder(index);
1046 dirIsStarted = true;
1047 break;
1048 }
1049 else
1050 OpenFolderExternal(index);
1051 }
1052 }
1053 else
1054 OpenItem(index, (tryInternal && indices.Size() == 1), true);
1055 }
1056 }
1057
GetItemName(unsigned itemIndex) const1058 UString CPanel::GetItemName(unsigned itemIndex) const
1059 {
1060 if (itemIndex == kParentIndex)
1061 return L"..";
1062 NCOM::CPropVariant prop;
1063 if (_folder->GetProperty(itemIndex, kpidName, &prop) != S_OK)
1064 throw 2723400;
1065 if (prop.vt != VT_BSTR)
1066 throw 2723401;
1067 return prop.bstrVal;
1068 }
1069
GetItemName_for_Copy(unsigned itemIndex) const1070 UString CPanel::GetItemName_for_Copy(unsigned itemIndex) const
1071 {
1072 if (itemIndex == kParentIndex)
1073 return L"..";
1074 UString s;
1075 {
1076 NCOM::CPropVariant prop;
1077 if (_folder->GetProperty(itemIndex, kpidOutName, &prop) == S_OK)
1078 {
1079 if (prop.vt == VT_BSTR)
1080 s = prop.bstrVal;
1081 else if (prop.vt != VT_EMPTY)
1082 throw 2723401;
1083 }
1084 if (s.IsEmpty())
1085 s = GetItemName(itemIndex);
1086 }
1087 return Get_Correct_FsFile_Name(s);
1088 }
1089
GetItemName(unsigned itemIndex,UString & s) const1090 void CPanel::GetItemName(unsigned itemIndex, UString &s) const
1091 {
1092 if (itemIndex == kParentIndex)
1093 {
1094 s = "..";
1095 return;
1096 }
1097 NCOM::CPropVariant prop;
1098 if (_folder->GetProperty(itemIndex, kpidName, &prop) != S_OK)
1099 throw 2723400;
1100 if (prop.vt != VT_BSTR)
1101 throw 2723401;
1102 s.SetFromBstr(prop.bstrVal);
1103 }
1104
GetItemPrefix(unsigned itemIndex) const1105 UString CPanel::GetItemPrefix(unsigned itemIndex) const
1106 {
1107 if (itemIndex == kParentIndex)
1108 return UString();
1109 NCOM::CPropVariant prop;
1110 if (_folder->GetProperty(itemIndex, kpidPrefix, &prop) != S_OK)
1111 throw 2723400;
1112 UString prefix;
1113 if (prop.vt == VT_BSTR)
1114 prefix.SetFromBstr(prop.bstrVal);
1115 return prefix;
1116 }
1117
GetItemRelPath(unsigned itemIndex) const1118 UString CPanel::GetItemRelPath(unsigned itemIndex) const
1119 {
1120 return GetItemPrefix(itemIndex) + GetItemName(itemIndex);
1121 }
1122
GetItemRelPath2(unsigned itemIndex) const1123 UString CPanel::GetItemRelPath2(unsigned itemIndex) const
1124 {
1125 UString s = GetItemRelPath(itemIndex);
1126 #if defined(_WIN32) && !defined(UNDER_CE)
1127 if (s.Len() == 2 && NFile::NName::IsDrivePath2(s))
1128 {
1129 if (IsFSDrivesFolder() && !IsDeviceDrivesPrefix())
1130 s.Add_PathSepar();
1131 }
1132 #endif
1133 return s;
1134 }
1135
1136
Add_ItemRelPath2_To_String(unsigned itemIndex,UString & s) const1137 void CPanel::Add_ItemRelPath2_To_String(unsigned itemIndex, UString &s) const
1138 {
1139 if (itemIndex == kParentIndex)
1140 {
1141 s += "..";
1142 return;
1143 }
1144
1145 const unsigned start = s.Len();
1146 NCOM::CPropVariant prop;
1147 if (_folder->GetProperty(itemIndex, kpidPrefix, &prop) != S_OK)
1148 throw 2723400;
1149 if (prop.vt == VT_BSTR)
1150 s += prop.bstrVal;
1151
1152 const wchar_t *name = NULL;
1153 unsigned nameLen = 0;
1154
1155 if (_folderGetItemName)
1156 _folderGetItemName->GetItemName(itemIndex, &name, &nameLen);
1157 if (name)
1158 s += name;
1159 else
1160 {
1161 prop.Clear();
1162 if (_folder->GetProperty(itemIndex, kpidName, &prop) != S_OK)
1163 throw 2723400;
1164 if (prop.vt != VT_BSTR)
1165 throw 2723401;
1166 s += prop.bstrVal;
1167 }
1168
1169 #if defined(_WIN32) && !defined(UNDER_CE)
1170 if (s.Len() - start == 2 && NFile::NName::IsDrivePath2(s.Ptr(start)))
1171 {
1172 if (IsFSDrivesFolder() && !IsDeviceDrivesPrefix())
1173 s.Add_PathSepar();
1174 }
1175 #endif
1176 }
1177
1178
GetItemFullPath(unsigned itemIndex) const1179 UString CPanel::GetItemFullPath(unsigned itemIndex) const
1180 {
1181 return GetFsPath() + GetItemRelPath2(itemIndex);
1182 }
1183
GetItem_BoolProp(UInt32 itemIndex,PROPID propID) const1184 bool CPanel::GetItem_BoolProp(UInt32 itemIndex, PROPID propID) const
1185 {
1186 NCOM::CPropVariant prop;
1187 if (_folder->GetProperty(itemIndex, propID, &prop) != S_OK)
1188 throw 2723400;
1189 if (prop.vt == VT_BOOL)
1190 return VARIANT_BOOLToBool(prop.boolVal);
1191 if (prop.vt == VT_EMPTY)
1192 return false;
1193 throw 2723401;
1194 }
1195
IsItem_Deleted(unsigned itemIndex) const1196 bool CPanel::IsItem_Deleted(unsigned itemIndex) const
1197 {
1198 if (itemIndex == kParentIndex)
1199 return false;
1200 return GetItem_BoolProp(itemIndex, kpidIsDeleted);
1201 }
1202
IsItem_Folder(unsigned itemIndex) const1203 bool CPanel::IsItem_Folder(unsigned itemIndex) const
1204 {
1205 if (itemIndex == kParentIndex)
1206 return true;
1207 if (itemIndex < _isDirVector.Size())
1208 return _isDirVector[itemIndex];
1209 return GetItem_BoolProp(itemIndex, kpidIsDir);
1210 }
1211
IsItem_AltStream(unsigned itemIndex) const1212 bool CPanel::IsItem_AltStream(unsigned itemIndex) const
1213 {
1214 if (itemIndex == kParentIndex)
1215 return false;
1216 return GetItem_BoolProp(itemIndex, kpidIsAltStream);
1217 }
1218
GetItem_UInt64Prop(unsigned itemIndex,PROPID propID) const1219 UInt64 CPanel::GetItem_UInt64Prop(unsigned itemIndex, PROPID propID) const
1220 {
1221 if (itemIndex == kParentIndex)
1222 return 0;
1223 NCOM::CPropVariant prop;
1224 if (_folder->GetProperty(itemIndex, propID, &prop) != S_OK)
1225 throw 2723400;
1226 UInt64 val = 0;
1227 if (ConvertPropVariantToUInt64(prop, val))
1228 return val;
1229 return 0;
1230 }
1231
GetItemSize(unsigned itemIndex) const1232 UInt64 CPanel::GetItemSize(unsigned itemIndex) const
1233 {
1234 if (itemIndex == kParentIndex)
1235 return 0;
1236 if (_folderGetItemName)
1237 return _folderGetItemName->GetItemSize(itemIndex);
1238 return GetItem_UInt64Prop(itemIndex, kpidSize);
1239 }
1240
SaveListViewInfo()1241 void CPanel::SaveListViewInfo()
1242 {
1243 if (!_needSaveInfo)
1244 return;
1245
1246 unsigned i;
1247
1248 for (i = 0; i < _visibleColumns.Size(); i++)
1249 {
1250 CPropColumn &prop = _visibleColumns[i];
1251 LVCOLUMN winColumnInfo;
1252 winColumnInfo.mask = LVCF_ORDER | LVCF_WIDTH;
1253 if (!_listView.GetColumn(i, &winColumnInfo))
1254 throw 1;
1255 prop.Order = winColumnInfo.iOrder;
1256 prop.Width = (UInt32)(Int32)winColumnInfo.cx;
1257 }
1258
1259 CListViewInfo viewInfo;
1260
1261 // PROPID sortPropID = _columns[_sortIndex].ID;
1262 const PROPID sortPropID = _sortID;
1263
1264 // we save columns as "sorted by order" to registry
1265
1266 CPropColumns sortedProperties = _visibleColumns;
1267
1268 sortedProperties.Sort();
1269
1270 for (i = 0; i < sortedProperties.Size(); i++)
1271 {
1272 const CPropColumn &prop = sortedProperties[i];
1273 CColumnInfo columnInfo;
1274 columnInfo.IsVisible = prop.IsVisible;
1275 columnInfo.PropID = prop.ID;
1276 columnInfo.Width = prop.Width;
1277 viewInfo.Columns.Add(columnInfo);
1278 }
1279
1280 for (i = 0; i < _columns.Size(); i++)
1281 {
1282 const CPropColumn &prop = _columns[i];
1283 if (sortedProperties.FindItem_for_PropID(prop.ID) < 0)
1284 {
1285 CColumnInfo columnInfo;
1286 columnInfo.IsVisible = false;
1287 columnInfo.PropID = prop.ID;
1288 columnInfo.Width = prop.Width;
1289 viewInfo.Columns.Add(columnInfo);
1290 }
1291 }
1292
1293 viewInfo.SortID = sortPropID;
1294 viewInfo.Ascending = _ascending;
1295 viewInfo.IsLoaded = true;
1296 if (!_listViewInfo.IsEqual(viewInfo))
1297 {
1298 viewInfo.Save(_typeIDString);
1299 _listViewInfo = viewInfo;
1300 }
1301 }
1302
1303
OnRightClick(MY_NMLISTVIEW_NMITEMACTIVATE * itemActivate,LRESULT & result)1304 bool CPanel::OnRightClick(MY_NMLISTVIEW_NMITEMACTIVATE *itemActivate, LRESULT &result)
1305 {
1306 if (itemActivate->hdr.hwndFrom == HWND(_listView))
1307 return false;
1308 POINT point;
1309 ::GetCursorPos(&point);
1310 ShowColumnsContextMenu(point.x, point.y);
1311 result = TRUE;
1312 return true;
1313 }
1314
ShowColumnsContextMenu(int x,int y)1315 void CPanel::ShowColumnsContextMenu(int x, int y)
1316 {
1317 CMenu menu;
1318 CMenuDestroyer menuDestroyer(menu);
1319
1320 menu.CreatePopup();
1321
1322 const int kCommandStart = 100;
1323 FOR_VECTOR (i, _columns)
1324 {
1325 const CPropColumn &prop = _columns[i];
1326 UINT flags = MF_STRING;
1327 if (prop.IsVisible)
1328 flags |= MF_CHECKED;
1329 if (i == 0)
1330 flags |= MF_GRAYED;
1331 menu.AppendItem(flags, kCommandStart + i, prop.Name);
1332 }
1333
1334 const int menuResult = menu.Track(TPM_LEFTALIGN | TPM_RETURNCMD | TPM_NONOTIFY, x, y, _listView);
1335
1336 if (menuResult >= kCommandStart && menuResult <= kCommandStart + (int)_columns.Size())
1337 {
1338 const unsigned index = (unsigned)(menuResult - kCommandStart);
1339 CPropColumn &prop = _columns[index];
1340 prop.IsVisible = !prop.IsVisible;
1341
1342 if (prop.IsVisible)
1343 {
1344 prop.Order = (int)_visibleColumns.Size();
1345 AddColumn(prop);
1346 }
1347 else
1348 {
1349 const int visibleIndex = _visibleColumns.FindItem_for_PropID(prop.ID);
1350 if (visibleIndex >= 0)
1351 {
1352 /*
1353 if (_sortIndex == index)
1354 {
1355 _sortIndex = 0;
1356 _ascending = true;
1357 }
1358 */
1359 if (_sortID == prop.ID)
1360 {
1361 _sortID = kpidName;
1362 _ascending = true;
1363 }
1364 DeleteColumn((unsigned)visibleIndex);
1365 }
1366 }
1367 }
1368 }
1369
OnReload(bool onTimer)1370 void CPanel::OnReload(bool onTimer)
1371 {
1372 const HRESULT res = RefreshListCtrl_SaveFocused(onTimer);
1373 if (res != S_OK)
1374 MessageBox_Error_HRESULT(res);
1375 }
1376
OnTimer()1377 void CPanel::OnTimer()
1378 {
1379 if (!_processTimer)
1380 return;
1381 if (!AutoRefresh_Mode)
1382 return;
1383 CMyComPtr<IFolderWasChanged> folderWasChanged;
1384 if (_folder.QueryInterface(IID_IFolderWasChanged, &folderWasChanged) != S_OK)
1385 return;
1386 Int32 wasChanged;
1387 if (folderWasChanged->WasChanged(&wasChanged) != S_OK)
1388 return;
1389 if (wasChanged == 0)
1390 return;
1391 OnReload(true); // onTimer
1392 }
1393