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