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