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