xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/PanelItems.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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 1 // 0 : for debug local icons loading
587   if (!Is_Slow_Icon_Folder() || _showRealFileIcons)
588     _folder.QueryInterface(IID_IFolderGetSystemIconIndex, &folderGetSystemIconIndex);
589 #endif
590 
591   const bool isFSDrivesFolder = IsFSDrivesFolder();
592   const bool isArcFolder = IsArcFolder();
593 
594   if (!IsFSFolder())
595   {
596     CMyComPtr<IGetFolderArcProps> getFolderArcProps;
597     _folder.QueryInterface(IID_IGetFolderArcProps, &getFolderArcProps);
598     _thereAreDeletedItems = false;
599     if (getFolderArcProps)
600     {
601       CMyComPtr<IFolderArcProps> arcProps;
602       getFolderArcProps->GetFolderArcProps(&arcProps);
603       if (arcProps)
604       {
605         UInt32 numLevels;
606         if (arcProps->GetArcNumLevels(&numLevels) != S_OK)
607           numLevels = 0;
608         NCOM::CPropVariant prop;
609         if (arcProps->GetArcProp(numLevels - 1, kpidIsDeleted, &prop) == S_OK)
610           if (prop.vt == VT_BOOL && VARIANT_BOOLToBool(prop.boolVal))
611             _thereAreDeletedItems = true;
612       }
613     }
614   }
615 
616   _thereAre_ListView_Items = true;
617 
618   // OutputDebugStringA("\n\n");
619 
620   Print_OnNotify("===== Before Load");
621 
622   // #define USE_EMBED_ITEM
623 
624   if (showDots)
625   {
626     const UString itemName ("..");
627     item.iItem = listViewItemCount;
628     if (itemName == state.FocusedName)
629       cursorIndex = listViewItemCount;
630     item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
631     int subItem = 0;
632     item.iSubItem = subItem++;
633     item.lParam = (LPARAM)(int)kParentIndex;
634     #ifdef USE_EMBED_ITEM
635     item.pszText = const_cast<wchar_t *>((const wchar_t *)itemName);
636     #else
637     item.pszText = LPSTR_TEXTCALLBACKW;
638     #endif
639     // const UInt32 attrib = FILE_ATTRIBUTE_DIRECTORY;
640     item.iImage = g_Ext_to_Icon_Map.GetIconIndex_DIR();
641         // g_Ext_to_Icon_Map.GetIconIndex(attrib, itemName);
642     if (item.iImage < 0)
643         item.iImage = 0;
644     if (_listView.InsertItem(&item) == -1)
645       return E_FAIL;
646     listViewItemCount++;
647   }
648 
649   // OutputDebugStringA("S1\n");
650 
651   UString correctedName;
652   UString itemName;
653   UString relPath;
654 
655   for (UInt32 i = 0; i < numItems; i++)
656   {
657     const wchar_t *name = NULL;
658     unsigned nameLen = 0;
659 
660     if (_folderGetItemName)
661       _folderGetItemName->GetItemName(i, &name, &nameLen);
662     if (!name)
663     {
664       GetItemName(i, itemName);
665       name = itemName;
666       nameLen = itemName.Len();
667     }
668 
669     bool selected = false;
670 
671     if (state.FocusedName_Defined || !state.SelectedNames.IsEmpty())
672     {
673       relPath.Empty();
674       // relPath += GetItemPrefix(i);
675       if (_flatMode)
676       {
677         const wchar_t *prefix = NULL;
678         if (_folderGetItemName)
679         {
680           unsigned prefixLen = 0;
681           _folderGetItemName->GetItemPrefix(i, &prefix, &prefixLen);
682           if (prefix)
683             relPath = prefix;
684         }
685         if (!prefix)
686         {
687           NCOM::CPropVariant prop;
688           if (_folder->GetProperty(i, kpidPrefix, &prop) != S_OK)
689             throw 2723400;
690           if (prop.vt == VT_BSTR)
691             relPath.SetFromBstr(prop.bstrVal);
692         }
693       }
694       relPath += name;
695       if (relPath == state.FocusedName)
696         cursorIndex = listViewItemCount;
697       if (state.SelectedNames.FindInSorted(relPath) != -1)
698         selected = true;
699     }
700 
701     _selectedStatusVector.AddInReserved(selected);
702 
703     item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
704 
705     if (!_mySelectMode)
706       if (selected)
707       {
708         item.mask |= LVIF_STATE;
709         item.state = LVIS_SELECTED;
710       }
711 
712     int subItem = 0;
713     item.iItem = listViewItemCount;
714 
715     item.iSubItem = subItem++;
716     item.lParam = (LPARAM)i;
717 
718     /*
719     int finish = nameLen - 4;
720     int j;
721     for (j = 0; j < finish; j++)
722     {
723       if (name[j    ] == ' ' &&
724           name[j + 1] == ' ' &&
725           name[j + 2] == ' ' &&
726           name[j + 3] == ' ' &&
727           name[j + 4] == ' ')
728         break;
729     }
730     if (j < finish)
731     {
732       correctedName.Empty();
733       correctedName = "virus";
734       int pos = 0;
735       for (;;)
736       {
737         int posNew = itemName.Find(L"     ", pos);
738         if (posNew < 0)
739         {
740           correctedName += itemName.Ptr(pos);
741           break;
742         }
743         correctedName += itemName.Mid(pos, posNew - pos);
744         correctedName += " ... ";
745         pos = posNew;
746         while (itemName[++pos] == ' ');
747       }
748       item.pszText = const_cast<wchar_t *>((const wchar_t *)correctedName);
749     }
750     else
751     */
752     {
753       #ifdef USE_EMBED_ITEM
754       item.pszText = const_cast<wchar_t *>((const wchar_t *)name);
755       #else
756       item.pszText = LPSTR_TEXTCALLBACKW;
757       #endif
758       /* LPSTR_TEXTCALLBACKW works, but in some cases there are problems,
759       since we block notify handler.
760       LPSTR_TEXTCALLBACKW can be 2-3 times faster for loading in this loop. */
761     }
762 
763     bool defined = false;
764     item.iImage = -1;
765 
766     if (folderGetSystemIconIndex)
767     {
768       const HRESULT res = folderGetSystemIconIndex->GetSystemIconIndex(i, &item.iImage);
769       if (res == S_OK)
770       {
771         // item.iImage = -1; // for debug
772         defined = (item.iImage > 0);
773 #if 0 // 0: can be slower: 2 attempts for some paths.
774       // 1: faster, but we can get default icon for some cases (where non default icon is possible)
775 
776         if (item.iImage == 0)
777         {
778           // (item.iImage == 0) means default icon.
779           // But (item.iImage == 0) also can be returned for exe/ico files,
780           // if filePath is LONG PATH (path_len() >= MAX_PATH).
781           // Also we want to show split icon (.001) for any split extension: 001 002 003.
782           // Are there another cases for (item.iImage == 0) for files with known extensions?
783           // We don't want to do second attempt to request icon,
784           // if it also will return (item.iImage == 0).
785 
786           int dotPos = -1;
787           for (unsigned k = 0;; k++)
788           {
789             const wchar_t c = name[k];
790             if (c == 0)
791               break;
792             if (c == '.')
793               dotPos = (int)i;
794             // we don't need IS_PATH_SEPAR check, because we have only (fileName) doesn't include path prefix.
795             // if (IS_PATH_SEPAR(c) || c == ':') dotPos = -1;
796           }
797           defined = true;
798           if (dotPos >= 0)
799           {
800 #if 0
801             const wchar_t *ext = name + dotPos;
802             if (StringsAreEqualNoCase_Ascii(ext, ".exe") ||
803                 StringsAreEqualNoCase_Ascii(ext, ".ico"))
804 #endif
805               defined = false;
806           }
807         }
808 #endif
809       }
810     }
811 
812     if (!defined)
813     {
814       UInt32 attrib = 0;
815       {
816         NCOM::CPropVariant prop;
817         RINOK(_folder->GetProperty(i, kpidAttrib, &prop))
818         if (prop.vt == VT_UI4)
819         {
820           attrib = prop.ulVal;
821           if (isArcFolder)
822           {
823             // if attrib (high 16-bits) is supposed from posix,
824             // we keep only low bits (basic Windows attrib flags):
825             if (attrib & 0xF0000000)
826               attrib &= 0x3FFF;
827           }
828         }
829       }
830       if (IsItem_Folder(i))
831         attrib |= FILE_ATTRIBUTE_DIRECTORY;
832       else
833         attrib &= ~(UInt32)FILE_ATTRIBUTE_DIRECTORY;
834 
835       item.iImage = -1;
836       if (isFSDrivesFolder)
837       {
838         FString fs (us2fs((UString)name));
839         fs.Add_PathSepar();
840         item.iImage = Shell_GetFileInfo_SysIconIndex_for_Path(fs, attrib);
841         // item.iImage = 0; // for debug
842       }
843       if (item.iImage < 0) // <= 0 check?
844         item.iImage = g_Ext_to_Icon_Map.GetIconIndex(attrib, name);
845     }
846 
847     // item.iImage = -1; // for debug
848     if (item.iImage < 0)
849         item.iImage = 0; // default image
850     if (_listView.InsertItem(&item) == -1)
851       return E_FAIL;
852     listViewItemCount++;
853   }
854 
855   /*
856     xp-64: there is different order when Windows calls CPanel::OnNotify for _listView modes:
857     Details      : after whole code
858     List         : 2 times:
859                         1) - ListView.SotRedraw()
860                         2) - after whole code
861     Small Icons  :
862     Large icons  : 2 times:
863                         1) - ListView.Sort()
864                         2) - after whole code (calls with reverse order of items)
865 
866     So we need to allow Notify(), when windows requests names during the following code.
867   */
868 
869   Print_OnNotify("after Load");
870 
871   disableNotify.SetMemMode_Enable();
872   disableNotify.Restore();
873 
874   if (_listView.GetItemCount() > 0 && cursorIndex >= 0)
875     SetFocusedSelectedItem(cursorIndex, state.SelectFocused);
876 
877   Print_OnNotify("after SetFocusedSelectedItem");
878 
879   SetSortRawStatus();
880   _listView.SortItems(CompareItems, (LPARAM)this);
881 
882   Print_OnNotify("after  Sort");
883 
884   if (cursorIndex < 0 && _listView.GetItemCount() > 0)
885   {
886     if (focusedPos >= _listView.GetItemCount())
887       focusedPos = _listView.GetItemCount() - 1;
888     // we select item only in showDots mode.
889     SetFocusedSelectedItem(focusedPos, showDots && (focusedPos == 0));
890   }
891 
892   // m_RedrawEnabled = true;
893 
894   Print_OnNotify("after  SetFocusedSelectedItem2");
895 
896   _listView.EnsureVisible(_listView.GetFocusedItem(), false);
897 
898   // disableNotify.SetMemMode_Enable();
899   // disableNotify.Restore();
900 
901   Print_OnNotify("after  EnsureVisible");
902 
903   _listView.SetRedraw(true);
904 
905   Print_OnNotify("after  SetRedraw");
906 
907   _listView.InvalidateRect(NULL, true);
908 
909   Print_OnNotify("after InvalidateRect");
910   /*
911   _listView.UpdateWindow();
912   */
913   Refresh_StatusBar();
914   /*
915   char s[256];
916   sprintf(s,
917       // "attribMap = %5d, extMap = %5d, "
918       "delete = %5d, load = %5d, list = %5d, sort = %5d, end = %5d",
919       // g_Ext_to_Icon_Map._attribMap.Size(),
920       // g_Ext_to_Icon_Map._extMap.Size(),
921       tickCount1 - tickCount0,
922       tickCount2 - tickCount1,
923       tickCount3 - tickCount2,
924       tickCount4 - tickCount3,
925       tickCount5 - tickCount4
926       );
927   sprintf(s,
928       "5 = %5d, 6 = %5d, 7 = %5d, 8 = %5d, 9 = %5d",
929       tickCount5 - tickCount4,
930       tickCount6 - tickCount5,
931       tickCount7 - tickCount6,
932       tickCount8 - tickCount7,
933       tickCount9 - tickCount8
934       );
935   OutputDebugStringA(s);
936   */
937   return S_OK;
938 }
939 
940 
Get_ItemIndices_Selected(CRecordVector<UInt32> & indices) const941 void CPanel::Get_ItemIndices_Selected(CRecordVector<UInt32> &indices) const
942 {
943   indices.Clear();
944   /*
945   int itemIndex = -1;
946   while ((itemIndex = _listView.GetNextSelectedItem(itemIndex)) != -1)
947   {
948     LPARAM param;
949     if (_listView.GetItemParam(itemIndex, param))
950       indices.Add(param);
951   }
952   HeapSort(&indices.Front(), indices.Size());
953   */
954   const bool *v = _selectedStatusVector.ConstData();
955   const unsigned size = _selectedStatusVector.Size();
956   for (unsigned i = 0; i < size; i++)
957     if (v[i])
958       indices.Add(i);
959 }
960 
961 
Get_ItemIndices_Operated(CRecordVector<UInt32> & indices) const962 void CPanel::Get_ItemIndices_Operated(CRecordVector<UInt32> &indices) const
963 {
964   Get_ItemIndices_Selected(indices);
965   if (!indices.IsEmpty())
966     return;
967   if (_listView.GetSelectedCount() == 0)
968     return;
969   const int focusedItem = _listView.GetFocusedItem();
970   if (focusedItem >= 0)
971   {
972     if (_listView.IsItemSelected(focusedItem))
973     {
974       const unsigned realIndex = GetRealItemIndex(focusedItem);
975       if (realIndex != kParentIndex)
976         indices.Add(realIndex);
977     }
978   }
979 }
980 
Get_ItemIndices_All(CRecordVector<UInt32> & indices) const981 void CPanel::Get_ItemIndices_All(CRecordVector<UInt32> &indices) const
982 {
983   indices.Clear();
984   UInt32 numItems;
985   if (_folder->GetNumberOfItems(&numItems) != S_OK)
986     return;
987   indices.ClearAndSetSize(numItems);
988   UInt32 *vec = indices.NonConstData();
989   for (UInt32 i = 0; i < numItems; i++)
990     vec[i] = i;
991 }
992 
Get_ItemIndices_OperSmart(CRecordVector<UInt32> & indices) const993 void CPanel::Get_ItemIndices_OperSmart(CRecordVector<UInt32> &indices) const
994 {
995   Get_ItemIndices_Operated(indices);
996   if (indices.IsEmpty() || (indices.Size() == 1 && indices[0] == (UInt32)(Int32)-1))
997     Get_ItemIndices_All(indices);
998 }
999 
1000 /*
1001 void CPanel::GetOperatedListViewIndices(CRecordVector<UInt32> &indices) const
1002 {
1003   indices.Clear();
1004   int numItems = _listView.GetItemCount();
1005   for (int i = 0; i < numItems; i++)
1006   {
1007     const unsigned realIndex = GetRealItemIndex(i);
1008     if (realIndex >= 0)
1009       if (_selectedStatusVector[realIndex])
1010         indices.Add(i);
1011   }
1012   if (indices.IsEmpty())
1013   {
1014     const int focusedItem = _listView.GetFocusedItem();
1015       if (focusedItem >= 0)
1016         indices.Add(focusedItem);
1017   }
1018 }
1019 */
1020 
EditItem(bool useEditor)1021 void CPanel::EditItem(bool useEditor)
1022 {
1023   if (!useEditor)
1024   {
1025     CMyComPtr<IFolderCalcItemFullSize> calcItemFullSize;
1026     _folder.QueryInterface(IID_IFolderCalcItemFullSize, &calcItemFullSize);
1027     if (calcItemFullSize)
1028     {
1029       bool needRefresh = false;
1030       CRecordVector<UInt32> indices;
1031       Get_ItemIndices_Operated(indices);
1032       FOR_VECTOR (i, indices)
1033       {
1034         UInt32 index = indices[i];
1035         if (IsItem_Folder(index))
1036         {
1037           calcItemFullSize->CalcItemFullSize(index, NULL);
1038           needRefresh = true;
1039         }
1040       }
1041       if (needRefresh)
1042       {
1043         // _listView.RedrawItem(0);
1044         // _listView.RedrawAllItems();
1045         InvalidateList();
1046         return;
1047       }
1048     }
1049   }
1050 
1051 
1052   const int focusedItem = _listView.GetFocusedItem();
1053   if (focusedItem < 0)
1054     return;
1055   const unsigned realIndex = GetRealItemIndex(focusedItem);
1056   if (realIndex == kParentIndex)
1057     return;
1058   if (!IsItem_Folder(realIndex))
1059     EditItem(realIndex, useEditor);
1060 }
1061 
OpenFocusedItemAsInternal(const wchar_t * type)1062 void CPanel::OpenFocusedItemAsInternal(const wchar_t *type)
1063 {
1064   const int focusedItem = _listView.GetFocusedItem();
1065   if (focusedItem < 0)
1066     return;
1067   const unsigned realIndex = GetRealItemIndex(focusedItem);
1068   if (IsItem_Folder(realIndex))
1069     OpenFolder(realIndex);
1070   else
1071     OpenItem(realIndex, true, false, type);
1072 }
1073 
OpenSelectedItems(bool tryInternal)1074 void CPanel::OpenSelectedItems(bool tryInternal)
1075 {
1076   CRecordVector<UInt32> indices;
1077   Get_ItemIndices_Operated(indices);
1078   if (indices.Size() > 20)
1079   {
1080     MessageBox_Error_LangID(IDS_TOO_MANY_ITEMS);
1081     return;
1082   }
1083 
1084   const int focusedItem = _listView.GetFocusedItem();
1085   if (focusedItem >= 0)
1086   {
1087     const unsigned realIndex = GetRealItemIndex(focusedItem);
1088     if (realIndex == kParentIndex && (tryInternal || indices.Size() == 0) && _listView.IsItemSelected(focusedItem))
1089       indices.Insert(0, realIndex);
1090   }
1091 
1092   bool dirIsStarted = false;
1093   FOR_VECTOR (i, indices)
1094   {
1095     UInt32 index = indices[i];
1096     // CFileInfo &aFile = m_Files[index];
1097     if (IsItem_Folder(index))
1098     {
1099       if (!dirIsStarted)
1100       {
1101         if (tryInternal)
1102         {
1103           OpenFolder(index);
1104           dirIsStarted = true;
1105           break;
1106         }
1107         else
1108           OpenFolderExternal(index);
1109       }
1110     }
1111     else
1112       OpenItem(index, (tryInternal && indices.Size() == 1), true);
1113   }
1114 }
1115 
GetItemName(unsigned itemIndex) const1116 UString CPanel::GetItemName(unsigned itemIndex) const
1117 {
1118   if (itemIndex == kParentIndex)
1119     return L"..";
1120   NCOM::CPropVariant prop;
1121   if (_folder->GetProperty(itemIndex, kpidName, &prop) != S_OK)
1122     throw 2723400;
1123   if (prop.vt != VT_BSTR)
1124     throw 2723401;
1125   return prop.bstrVal;
1126 }
1127 
GetItemName_for_Copy(unsigned itemIndex) const1128 UString CPanel::GetItemName_for_Copy(unsigned itemIndex) const
1129 {
1130   if (itemIndex == kParentIndex)
1131     return L"..";
1132   UString s;
1133   {
1134     NCOM::CPropVariant prop;
1135     if (_folder->GetProperty(itemIndex, kpidOutName, &prop) == S_OK)
1136     {
1137       if (prop.vt == VT_BSTR)
1138         s = prop.bstrVal;
1139       else if (prop.vt != VT_EMPTY)
1140         throw 2723401;
1141     }
1142     if (s.IsEmpty())
1143       s = GetItemName(itemIndex);
1144   }
1145   return Get_Correct_FsFile_Name(s);
1146 }
1147 
GetItemName(unsigned itemIndex,UString & s) const1148 void CPanel::GetItemName(unsigned itemIndex, UString &s) const
1149 {
1150   if (itemIndex == kParentIndex)
1151   {
1152     s = "..";
1153     return;
1154   }
1155   NCOM::CPropVariant prop;
1156   if (_folder->GetProperty(itemIndex, kpidName, &prop) != S_OK)
1157     throw 2723400;
1158   if (prop.vt != VT_BSTR)
1159     throw 2723401;
1160   s.SetFromBstr(prop.bstrVal);
1161 }
1162 
GetItemPrefix(unsigned itemIndex) const1163 UString CPanel::GetItemPrefix(unsigned itemIndex) const
1164 {
1165   if (itemIndex == kParentIndex)
1166     return UString();
1167   NCOM::CPropVariant prop;
1168   if (_folder->GetProperty(itemIndex, kpidPrefix, &prop) != S_OK)
1169     throw 2723400;
1170   UString prefix;
1171   if (prop.vt == VT_BSTR)
1172     prefix.SetFromBstr(prop.bstrVal);
1173   return prefix;
1174 }
1175 
GetItemRelPath(unsigned itemIndex) const1176 UString CPanel::GetItemRelPath(unsigned itemIndex) const
1177 {
1178   return GetItemPrefix(itemIndex) + GetItemName(itemIndex);
1179 }
1180 
GetItemRelPath2(unsigned itemIndex) const1181 UString CPanel::GetItemRelPath2(unsigned itemIndex) const
1182 {
1183   UString s = GetItemRelPath(itemIndex);
1184   #if defined(_WIN32) && !defined(UNDER_CE)
1185   if (s.Len() == 2 && NFile::NName::IsDrivePath2(s))
1186   {
1187     if (IsFSDrivesFolder() && !IsDeviceDrivesPrefix())
1188       s.Add_PathSepar();
1189   }
1190   #endif
1191   return s;
1192 }
1193 
1194 
Add_ItemRelPath2_To_String(unsigned itemIndex,UString & s) const1195 void CPanel::Add_ItemRelPath2_To_String(unsigned itemIndex, UString &s) const
1196 {
1197   if (itemIndex == kParentIndex)
1198   {
1199     s += "..";
1200     return;
1201   }
1202 
1203   const unsigned start = s.Len();
1204   NCOM::CPropVariant prop;
1205   if (_folder->GetProperty(itemIndex, kpidPrefix, &prop) != S_OK)
1206     throw 2723400;
1207   if (prop.vt == VT_BSTR)
1208     s += prop.bstrVal;
1209 
1210   const wchar_t *name = NULL;
1211   unsigned nameLen = 0;
1212 
1213   if (_folderGetItemName)
1214     _folderGetItemName->GetItemName(itemIndex, &name, &nameLen);
1215   if (name)
1216     s += name;
1217   else
1218   {
1219     prop.Clear();
1220     if (_folder->GetProperty(itemIndex, kpidName, &prop) != S_OK)
1221       throw 2723400;
1222     if (prop.vt != VT_BSTR)
1223       throw 2723401;
1224     s += prop.bstrVal;
1225   }
1226 
1227   #if defined(_WIN32) && !defined(UNDER_CE)
1228     if (s.Len() - start == 2 && NFile::NName::IsDrivePath2(s.Ptr(start)))
1229     {
1230       if (IsFSDrivesFolder() && !IsDeviceDrivesPrefix())
1231         s.Add_PathSepar();
1232     }
1233   #endif
1234 }
1235 
1236 
GetItemFullPath(unsigned itemIndex) const1237 UString CPanel::GetItemFullPath(unsigned itemIndex) const
1238 {
1239   return GetFsPath() + GetItemRelPath2(itemIndex);
1240 }
1241 
GetItem_BoolProp(UInt32 itemIndex,PROPID propID) const1242 bool CPanel::GetItem_BoolProp(UInt32 itemIndex, PROPID propID) const
1243 {
1244   NCOM::CPropVariant prop;
1245   if (_folder->GetProperty(itemIndex, propID, &prop) != S_OK)
1246     throw 2723400;
1247   if (prop.vt == VT_BOOL)
1248     return VARIANT_BOOLToBool(prop.boolVal);
1249   if (prop.vt == VT_EMPTY)
1250     return false;
1251   throw 2723401;
1252 }
1253 
IsItem_Deleted(unsigned itemIndex) const1254 bool CPanel::IsItem_Deleted(unsigned itemIndex) const
1255 {
1256   if (itemIndex == kParentIndex)
1257     return false;
1258   return GetItem_BoolProp(itemIndex, kpidIsDeleted);
1259 }
1260 
IsItem_Folder(unsigned itemIndex) const1261 bool CPanel::IsItem_Folder(unsigned itemIndex) const
1262 {
1263   if (itemIndex == kParentIndex)
1264     return true;
1265   if (itemIndex < _isDirVector.Size())
1266     return _isDirVector[itemIndex];
1267   return GetItem_BoolProp(itemIndex, kpidIsDir);
1268 }
1269 
IsItem_AltStream(unsigned itemIndex) const1270 bool CPanel::IsItem_AltStream(unsigned itemIndex) const
1271 {
1272   if (itemIndex == kParentIndex)
1273     return false;
1274   return GetItem_BoolProp(itemIndex, kpidIsAltStream);
1275 }
1276 
GetItem_UInt64Prop(unsigned itemIndex,PROPID propID) const1277 UInt64 CPanel::GetItem_UInt64Prop(unsigned itemIndex, PROPID propID) const
1278 {
1279   if (itemIndex == kParentIndex)
1280     return 0;
1281   NCOM::CPropVariant prop;
1282   if (_folder->GetProperty(itemIndex, propID, &prop) != S_OK)
1283     throw 2723400;
1284   UInt64 val = 0;
1285   if (ConvertPropVariantToUInt64(prop, val))
1286     return val;
1287   return 0;
1288 }
1289 
GetItemSize(unsigned itemIndex) const1290 UInt64 CPanel::GetItemSize(unsigned itemIndex) const
1291 {
1292   if (itemIndex == kParentIndex)
1293     return 0;
1294   if (_folderGetItemName)
1295     return _folderGetItemName->GetItemSize(itemIndex);
1296   return GetItem_UInt64Prop(itemIndex, kpidSize);
1297 }
1298 
SaveListViewInfo()1299 void CPanel::SaveListViewInfo()
1300 {
1301   if (!_needSaveInfo)
1302     return;
1303 
1304   unsigned i;
1305 
1306   for (i = 0; i < _visibleColumns.Size(); i++)
1307   {
1308     CPropColumn &prop = _visibleColumns[i];
1309     LVCOLUMN winColumnInfo;
1310     winColumnInfo.mask = LVCF_ORDER | LVCF_WIDTH;
1311     if (!_listView.GetColumn(i, &winColumnInfo))
1312       throw 1;
1313     prop.Order = winColumnInfo.iOrder;
1314     prop.Width = (UInt32)(Int32)winColumnInfo.cx;
1315   }
1316 
1317   CListViewInfo viewInfo;
1318 
1319   // PROPID sortPropID = _columns[_sortIndex].ID;
1320   const PROPID sortPropID = _sortID;
1321 
1322   // we save columns as "sorted by order" to registry
1323 
1324   CPropColumns sortedProperties = _visibleColumns;
1325 
1326   sortedProperties.Sort();
1327 
1328   for (i = 0; i < sortedProperties.Size(); i++)
1329   {
1330     const CPropColumn &prop = sortedProperties[i];
1331     CColumnInfo columnInfo;
1332     columnInfo.IsVisible = prop.IsVisible;
1333     columnInfo.PropID = prop.ID;
1334     columnInfo.Width = prop.Width;
1335     viewInfo.Columns.Add(columnInfo);
1336   }
1337 
1338   for (i = 0; i < _columns.Size(); i++)
1339   {
1340     const CPropColumn &prop = _columns[i];
1341     if (sortedProperties.FindItem_for_PropID(prop.ID) < 0)
1342     {
1343       CColumnInfo columnInfo;
1344       columnInfo.IsVisible = false;
1345       columnInfo.PropID = prop.ID;
1346       columnInfo.Width = prop.Width;
1347       viewInfo.Columns.Add(columnInfo);
1348     }
1349   }
1350 
1351   viewInfo.SortID = sortPropID;
1352   viewInfo.Ascending = _ascending;
1353   viewInfo.IsLoaded = true;
1354   if (!_listViewInfo.IsEqual(viewInfo))
1355   {
1356     viewInfo.Save(_typeIDString);
1357     _listViewInfo = viewInfo;
1358   }
1359 }
1360 
1361 
OnRightClick(MY_NMLISTVIEW_NMITEMACTIVATE * itemActivate,LRESULT & result)1362 bool CPanel::OnRightClick(MY_NMLISTVIEW_NMITEMACTIVATE *itemActivate, LRESULT &result)
1363 {
1364   if (itemActivate->hdr.hwndFrom == HWND(_listView))
1365     return false;
1366   POINT point;
1367   ::GetCursorPos(&point);
1368   ShowColumnsContextMenu(point.x, point.y);
1369   result = TRUE;
1370   return true;
1371 }
1372 
ShowColumnsContextMenu(int x,int y)1373 void CPanel::ShowColumnsContextMenu(int x, int y)
1374 {
1375   CMenu menu;
1376   CMenuDestroyer menuDestroyer(menu);
1377 
1378   menu.CreatePopup();
1379 
1380   const int kCommandStart = 100;
1381   FOR_VECTOR (i, _columns)
1382   {
1383     const CPropColumn &prop = _columns[i];
1384     UINT flags =  MF_STRING;
1385     if (prop.IsVisible)
1386       flags |= MF_CHECKED;
1387     if (i == 0)
1388       flags |= MF_GRAYED;
1389     menu.AppendItem(flags, kCommandStart + i, prop.Name);
1390   }
1391 
1392   const int menuResult = menu.Track(TPM_LEFTALIGN | TPM_RETURNCMD | TPM_NONOTIFY, x, y, _listView);
1393 
1394   if (menuResult >= kCommandStart && menuResult <= kCommandStart + (int)_columns.Size())
1395   {
1396     const unsigned index = (unsigned)(menuResult - kCommandStart);
1397     CPropColumn &prop = _columns[index];
1398     prop.IsVisible = !prop.IsVisible;
1399 
1400     if (prop.IsVisible)
1401     {
1402       prop.Order = (int)_visibleColumns.Size();
1403       AddColumn(prop);
1404     }
1405     else
1406     {
1407       const int visibleIndex = _visibleColumns.FindItem_for_PropID(prop.ID);
1408       if (visibleIndex >= 0)
1409       {
1410         /*
1411         if (_sortIndex == index)
1412         {
1413         _sortIndex = 0;
1414         _ascending = true;
1415         }
1416         */
1417         if (_sortID == prop.ID)
1418         {
1419           _sortID = kpidName;
1420           _ascending = true;
1421         }
1422         DeleteColumn((unsigned)visibleIndex);
1423       }
1424     }
1425   }
1426 }
1427 
OnReload(bool onTimer)1428 void CPanel::OnReload(bool onTimer)
1429 {
1430   const HRESULT res = RefreshListCtrl_SaveFocused(onTimer);
1431   if (res != S_OK)
1432     MessageBox_Error_HRESULT(res);
1433 }
1434 
OnTimer()1435 void CPanel::OnTimer()
1436 {
1437   if (!_processTimer)
1438     return;
1439   if (!AutoRefresh_Mode)
1440     return;
1441   if (!_folder) // it's unexpected case, but we use it as additional protection.
1442     return;
1443   {
1444     CMyComPtr<IFolderWasChanged> folderWasChanged;
1445     _folder.QueryInterface(IID_IFolderWasChanged, &folderWasChanged);
1446     if (!folderWasChanged)
1447       return;
1448     Int32 wasChanged;
1449     if (folderWasChanged->WasChanged(&wasChanged) != S_OK || wasChanged == 0)
1450       return;
1451   }
1452   OnReload(true); // onTimer
1453 }
1454