• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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