| // PanelItems.cpp |
| |
| #include "StdAfx.h" |
| |
| #include "../../../../C/Sort.h" |
| |
| #include "../../../Windows/FileName.h" |
| #include "../../../Windows/Menu.h" |
| #include "../../../Windows/PropVariant.h" |
| #include "../../../Windows/PropVariantConv.h" |
| |
| #include "../../PropID.h" |
| |
| #include "../Common/ExtractingFilePath.h" |
| |
| #include "resource.h" |
| |
| #include "LangUtils.h" |
| #include "Panel.h" |
| #include "PropertyName.h" |
| #include "RootFolder.h" |
| |
| using namespace NWindows; |
| |
| static bool GetColumnVisible(PROPID propID, bool isFsFolder) |
| { |
| if (isFsFolder) |
| { |
| switch (propID) |
| { |
| case kpidATime: |
| case kpidChangeTime: |
| case kpidAttrib: |
| case kpidPackSize: |
| case kpidINode: |
| case kpidLinks: |
| case kpidNtReparse: |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static unsigned GetColumnWidth(PROPID propID, VARTYPE /* varType */) |
| { |
| switch (propID) |
| { |
| case kpidName: return 160; |
| } |
| return 100; |
| } |
| |
| static int GetColumnAlign(PROPID propID, VARTYPE varType) |
| { |
| switch (propID) |
| { |
| case kpidCTime: |
| case kpidATime: |
| case kpidMTime: |
| case kpidChangeTime: |
| return LVCFMT_LEFT; |
| } |
| |
| switch (varType) |
| { |
| case VT_UI1: |
| case VT_I2: |
| case VT_UI2: |
| case VT_I4: |
| case VT_INT: |
| case VT_UI4: |
| case VT_UINT: |
| case VT_I8: |
| case VT_UI8: |
| case VT_BOOL: |
| return LVCFMT_RIGHT; |
| |
| case VT_EMPTY: |
| case VT_I1: |
| case VT_FILETIME: |
| case VT_BSTR: |
| return LVCFMT_LEFT; |
| |
| default: |
| return LVCFMT_CENTER; |
| } |
| } |
| |
| |
| static int ItemProperty_Compare_NameFirst(void *const *a1, void *const *a2, void * /* param */) |
| { |
| return (*(*((const CPropColumn *const *)a1))).Compare_NameFirst(*(*((const CPropColumn *const *)a2))); |
| } |
| |
| HRESULT CPanel::InitColumns() |
| { |
| SaveListViewInfo(); |
| |
| // DeleteListItems(); |
| _selectedStatusVector.Clear(); |
| |
| { |
| // ReadListViewInfo(); |
| const UString oldType = _typeIDString; |
| _typeIDString = GetFolderTypeID(); |
| // an empty _typeIDString is allowed. |
| |
| // we read registry only for new FolderTypeID |
| if (!_needSaveInfo || _typeIDString != oldType) |
| _listViewInfo.Read(_typeIDString); |
| |
| // folders with same FolderTypeID can have different columns |
| // so we still read columns for that case. |
| // if (_needSaveInfo && _typeIDString == oldType) return S_OK; |
| } |
| |
| // PROPID sortID; |
| /* |
| if (_listViewInfo.SortIndex >= 0) |
| sortID = _listViewInfo.Columns[_listViewInfo.SortIndex].PropID; |
| */ |
| // sortID = _listViewInfo.SortID; |
| |
| _ascending = _listViewInfo.Ascending; |
| |
| _columns.Clear(); |
| |
| bool isFsFolder = IsFSFolder() || IsAltStreamsFolder(); |
| |
| { |
| UInt32 numProps; |
| _folder->GetNumberOfProperties(&numProps); |
| |
| for (UInt32 i = 0; i < numProps; i++) |
| { |
| CMyComBSTR name; |
| PROPID propID; |
| VARTYPE varType; |
| HRESULT res = _folder->GetPropertyInfo(i, &name, &propID, &varType); |
| |
| if (res != S_OK) |
| { |
| /* We can return ERROR, but in that case, other code will not be called, |
| and user can see empty window without error message. So we just ignore that field */ |
| continue; |
| } |
| if (propID == kpidIsDir) |
| continue; |
| CPropColumn prop; |
| prop.Type = varType; |
| prop.ID = propID; |
| prop.Name = GetNameOfProperty(propID, name); |
| prop.Order = -1; |
| prop.IsVisible = GetColumnVisible(propID, isFsFolder); |
| prop.Width = GetColumnWidth(propID, varType); |
| prop.IsRawProp = false; |
| _columns.Add(prop); |
| } |
| |
| /* |
| { |
| // debug column |
| CPropColumn prop; |
| prop.Type = VT_BSTR; |
| prop.ID = 2000; |
| prop.Name = "Debug"; |
| prop.Order = -1; |
| prop.IsVisible = true; |
| prop.Width = 300; |
| prop.IsRawProp = false; |
| _columns.Add(prop); |
| } |
| */ |
| } |
| |
| if (_folderRawProps) |
| { |
| UInt32 numProps; |
| _folderRawProps->GetNumRawProps(&numProps); |
| |
| for (UInt32 i = 0; i < numProps; i++) |
| { |
| CMyComBSTR name; |
| PROPID propID; |
| HRESULT res = _folderRawProps->GetRawPropInfo(i, &name, &propID); |
| if (res != S_OK) |
| continue; |
| CPropColumn prop; |
| prop.Type = VT_EMPTY; |
| prop.ID = propID; |
| prop.Name = GetNameOfProperty(propID, name); |
| prop.Order = -1; |
| prop.IsVisible = GetColumnVisible(propID, isFsFolder); |
| prop.Width = GetColumnWidth(propID, VT_BSTR); |
| prop.IsRawProp = true; |
| _columns.Add(prop); |
| } |
| } |
| |
| unsigned order = 0; |
| unsigned i; |
| |
| for (i = 0; i < _listViewInfo.Columns.Size(); i++) |
| { |
| const CColumnInfo &columnInfo = _listViewInfo.Columns[i]; |
| const int index = _columns.FindItem_for_PropID(columnInfo.PropID); |
| if (index >= 0) |
| { |
| CPropColumn &item = _columns[index]; |
| if (item.Order >= 0) |
| continue; // we ignore duplicated items |
| bool isVisible = columnInfo.IsVisible; |
| // we enable kpidName, if it was disabled by some incorrect code |
| if (columnInfo.PropID == kpidName) |
| isVisible = true; |
| item.IsVisible = isVisible; |
| item.Width = columnInfo.Width; |
| if (isVisible) |
| item.Order = (int)(order++); |
| continue; |
| } |
| } |
| |
| for (i = 0; i < _columns.Size(); i++) |
| { |
| CPropColumn &item = _columns[i]; |
| if (item.IsVisible && item.Order < 0) |
| item.Order = (int)(order++); |
| } |
| |
| for (i = 0; i < _columns.Size(); i++) |
| { |
| CPropColumn &item = _columns[i]; |
| if (item.Order < 0) |
| item.Order = (int)(order++); |
| } |
| |
| CPropColumns newColumns; |
| |
| for (i = 0; i < _columns.Size(); i++) |
| { |
| const CPropColumn &prop = _columns[i]; |
| if (prop.IsVisible) |
| newColumns.Add(prop); |
| } |
| |
| |
| /* |
| _sortIndex = 0; |
| if (_listViewInfo.SortIndex >= 0) |
| { |
| int sortIndex = _columns.FindItem_for_PropID(sortID); |
| if (sortIndex >= 0) |
| _sortIndex = sortIndex; |
| } |
| */ |
| |
| if (_listViewInfo.IsLoaded) |
| _sortID = _listViewInfo.SortID; |
| else |
| { |
| _sortID = 0; |
| if (IsFSFolder() || IsAltStreamsFolder() || IsArcFolder()) |
| _sortID = kpidName; |
| } |
| |
| /* There are restrictions in ListView control: |
| 1) main column (kpidName) must have (LV_COLUMNW::iSubItem = 0) |
| So we need special sorting for columns. |
| 2) when we add new column, LV_COLUMNW::iOrder cannot be larger than already inserted columns) |
| So we set column order after all columns are added. |
| */ |
| newColumns.Sort(ItemProperty_Compare_NameFirst, NULL); |
| |
| if (newColumns.IsEqualTo(_visibleColumns)) |
| return S_OK; |
| |
| CIntArr columns(newColumns.Size()); |
| for (i = 0; i < newColumns.Size(); i++) |
| columns[i] = -1; |
| |
| bool orderError = false; |
| |
| for (i = 0; i < newColumns.Size(); i++) |
| { |
| const CPropColumn &prop = newColumns[i]; |
| if (prop.Order < (int)newColumns.Size() && columns[prop.Order] == -1) |
| columns[prop.Order] = (int)i; |
| else |
| orderError = true; |
| } |
| |
| for (;;) |
| { |
| const unsigned numColumns = _visibleColumns.Size(); |
| if (numColumns == 0) |
| break; |
| DeleteColumn(numColumns - 1); |
| } |
| |
| for (i = 0; i < newColumns.Size(); i++) |
| AddColumn(newColumns[i]); |
| |
| // columns[0], columns[1], .... should be displayed from left to right: |
| if (!orderError) |
| _listView.SetColumnOrderArray(_visibleColumns.Size(), columns); |
| |
| _needSaveInfo = true; |
| |
| return S_OK; |
| } |
| |
| |
| void CPanel::DeleteColumn(unsigned index) |
| { |
| _visibleColumns.Delete(index); |
| _listView.DeleteColumn(index); |
| } |
| |
| void CPanel::AddColumn(const CPropColumn &prop) |
| { |
| const unsigned index = _visibleColumns.Size(); |
| |
| LV_COLUMNW column; |
| column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER; |
| column.cx = (int)prop.Width; |
| column.fmt = GetColumnAlign(prop.ID, prop.Type); |
| column.iOrder = (int)index; // must be <= _listView.ItemCount |
| column.iSubItem = (int)index; // must be <= _listView.ItemCount |
| column.pszText = const_cast<wchar_t *>((const wchar_t *)prop.Name); |
| |
| _visibleColumns.Add(prop); |
| _listView.InsertColumn(index, &column); |
| } |
| |
| |
| HRESULT CPanel::RefreshListCtrl() |
| { |
| CSelectedState state; |
| return RefreshListCtrl(state); |
| } |
| |
| int CALLBACK CompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData); |
| |
| |
| void CPanel::GetSelectedNames(UStringVector &selectedNames) |
| { |
| CRecordVector<UInt32> indices; |
| Get_ItemIndices_Selected(indices); |
| selectedNames.ClearAndReserve(indices.Size()); |
| FOR_VECTOR (i, indices) |
| selectedNames.AddInReserved(GetItemRelPath(indices[i])); |
| |
| /* |
| for (int i = 0; i < _listView.GetItemCount(); i++) |
| { |
| const int kSize = 1024; |
| WCHAR name[kSize + 1]; |
| LVITEMW item; |
| item.iItem = i; |
| item.pszText = name; |
| item.cchTextMax = kSize; |
| item.iSubItem = 0; |
| item.mask = LVIF_TEXT | LVIF_PARAM; |
| if (!_listView.GetItem(&item)) |
| continue; |
| const unsigned realIndex = GetRealIndex(item); |
| if (realIndex == kParentIndex) |
| continue; |
| if (_selectedStatusVector[realIndex]) |
| selectedNames.Add(item.pszText); |
| } |
| */ |
| selectedNames.Sort(); |
| } |
| |
| void CPanel::SaveSelectedState(CSelectedState &s) |
| { |
| s.FocusedName_Defined = false; |
| s.FocusedName.Empty(); |
| s.SelectFocused = true; // false; |
| s.SelectedNames.Clear(); |
| s.FocusedItem = _listView.GetFocusedItem(); |
| { |
| if (s.FocusedItem >= 0) |
| { |
| const unsigned realIndex = GetRealItemIndex(s.FocusedItem); |
| if (realIndex != kParentIndex) |
| { |
| s.FocusedName = GetItemRelPath(realIndex); |
| s.FocusedName_Defined = true; |
| |
| s.SelectFocused = _listView.IsItemSelected(s.FocusedItem); |
| |
| /* |
| const int kSize = 1024; |
| WCHAR name[kSize + 1]; |
| LVITEMW item; |
| item.iItem = focusedItem; |
| item.pszText = name; |
| item.cchTextMax = kSize; |
| item.iSubItem = 0; |
| item.mask = LVIF_TEXT; |
| if (_listView.GetItem(&item)) |
| focusedName = item.pszText; |
| */ |
| } |
| } |
| } |
| GetSelectedNames(s.SelectedNames); |
| } |
| |
| /* |
| HRESULT CPanel::RefreshListCtrl(const CSelectedState &s) |
| { |
| bool selectFocused = s.SelectFocused; |
| if (_mySelectMode) |
| selectFocused = true; |
| return RefreshListCtrl2( |
| s.FocusedItem >= 0, // allowEmptyFocusedName |
| s.FocusedName, s.FocusedItem, selectFocused, s.SelectedNames); |
| } |
| */ |
| |
| HRESULT CPanel::RefreshListCtrl_SaveFocused(bool onTimer) |
| { |
| CSelectedState state; |
| SaveSelectedState(state); |
| state.CalledFromTimer = onTimer; |
| return RefreshListCtrl(state); |
| } |
| |
| void CPanel::SetFocusedSelectedItem(int index, bool select) |
| { |
| UINT state = LVIS_FOCUSED; |
| if (select) |
| state |= LVIS_SELECTED; |
| _listView.SetItemState(index, state, state); |
| if (!_mySelectMode && select) |
| { |
| const unsigned realIndex = GetRealItemIndex(index); |
| if (realIndex != kParentIndex) |
| _selectedStatusVector[realIndex] = true; |
| } |
| } |
| |
| // #define PRINT_STAT |
| |
| #ifdef PRINT_STAT |
| void Print_OnNotify(const char *name); |
| #else |
| #define Print_OnNotify(x) |
| #endif |
| |
| |
| |
| /* |
| |
| extern UInt32 g_NumGroups; |
| extern DWORD g_start_tick; |
| extern DWORD g_prev_tick; |
| extern DWORD g_Num_SetItemText; |
| extern UInt32 g_NumMessages; |
| */ |
| |
| HRESULT CPanel::RefreshListCtrl(const CSelectedState &state) |
| { |
| m_DropHighlighted_SelectionIndex = -1; |
| m_DropHighlighted_SubFolderName.Empty(); |
| |
| if (!_folder) |
| return S_OK; |
| |
| /* |
| g_start_tick = GetTickCount(); |
| g_Num_SetItemText = 0; |
| g_NumMessages = 0; |
| */ |
| |
| _dontShowMode = false; |
| if (!state.CalledFromTimer) |
| LoadFullPathAndShow(); |
| // OutputDebugStringA("=======\n"); |
| // OutputDebugStringA("s1 \n"); |
| CDisableTimerProcessing timerProcessing(*this); |
| CDisableNotify disableNotify(*this); |
| |
| int focusedPos = state.FocusedItem; |
| if (focusedPos < 0) |
| focusedPos = 0; |
| |
| _listView.SetRedraw(false); |
| // m_RedrawEnabled = false; |
| |
| LVITEMW item; |
| ZeroMemory(&item, sizeof(item)); |
| |
| // DWORD tickCount0 = GetTickCount(); |
| |
| // _enableItemChangeNotify = false; |
| DeleteListItems(); |
| _enableItemChangeNotify = true; |
| |
| int listViewItemCount = 0; |
| |
| _selectedStatusVector.Clear(); |
| // _realIndices.Clear(); |
| _startGroupSelect = 0; |
| |
| _selectionIsDefined = false; |
| |
| // m_Files.Clear(); |
| |
| /* |
| if (!_folder) |
| { |
| // throw 1; |
| SetToRootFolder(); |
| } |
| */ |
| |
| _headerToolBar.EnableButton(kParentFolderID, !IsRootFolder()); |
| |
| { |
| CMyComPtr<IFolderSetFlatMode> folderSetFlatMode; |
| _folder.QueryInterface(IID_IFolderSetFlatMode, &folderSetFlatMode); |
| if (folderSetFlatMode) |
| folderSetFlatMode->SetFlatMode(BoolToInt(_flatMode)); |
| } |
| |
| /* |
| { |
| CMyComPtr<IFolderSetShowNtfsStreamsMode> setShow; |
| _folder.QueryInterface(IID_IFolderSetShowNtfsStreamsMode, &setShow); |
| if (setShow) |
| setShow->SetShowNtfsStreamsMode(BoolToInt(_showNtfsStrems_Mode)); |
| } |
| */ |
| |
| _isDirVector.Clear(); |
| // DWORD tickCount1 = GetTickCount(); |
| IFolderFolder *folder = _folder; |
| RINOK(_folder->LoadItems()) |
| // DWORD tickCount2 = GetTickCount(); |
| // OutputDebugString(TEXT("Start Dir\n")); |
| RINOK(InitColumns()) |
| |
| UInt32 numItems; |
| _folder->GetNumberOfItems(&numItems); |
| { |
| NCOM::CPropVariant prop; |
| _isDirVector.ClearAndSetSize(numItems); |
| bool *vec = (bool *)&_isDirVector.Front(); |
| HRESULT hres = S_OK; |
| unsigned i; |
| for (i = 0; i < numItems; i++) |
| { |
| hres = folder->GetProperty(i, kpidIsDir, &prop); |
| if (hres != S_OK) |
| break; |
| bool v = false; |
| if (prop.vt == VT_BOOL) |
| v = VARIANT_BOOLToBool(prop.boolVal); |
| else if (prop.vt != VT_EMPTY) |
| break; |
| vec[i] = v; |
| } |
| if (i != numItems) |
| { |
| _isDirVector.Clear(); |
| if (hres == S_OK) |
| hres = E_FAIL; |
| } |
| RINOK(hres) |
| } |
| |
| const bool showDots = _showDots && !IsRootFolder(); |
| |
| _listView.SetItemCount(numItems + (showDots ? 1 : 0)); |
| |
| _selectedStatusVector.ClearAndReserve(numItems); |
| int cursorIndex = -1; |
| |
| CMyComPtr<IFolderGetSystemIconIndex> folderGetSystemIconIndex; |
| if (!Is_Slow_Icon_Folder() || _showRealFileIcons) |
| _folder.QueryInterface(IID_IFolderGetSystemIconIndex, &folderGetSystemIconIndex); |
| |
| if (!IsFSFolder()) |
| { |
| CMyComPtr<IGetFolderArcProps> getFolderArcProps; |
| _folder.QueryInterface(IID_IGetFolderArcProps, &getFolderArcProps); |
| _thereAreDeletedItems = false; |
| if (getFolderArcProps) |
| { |
| CMyComPtr<IFolderArcProps> arcProps; |
| getFolderArcProps->GetFolderArcProps(&arcProps); |
| if (arcProps) |
| { |
| UInt32 numLevels; |
| if (arcProps->GetArcNumLevels(&numLevels) != S_OK) |
| numLevels = 0; |
| NCOM::CPropVariant prop; |
| if (arcProps->GetArcProp(numLevels - 1, kpidIsDeleted, &prop) == S_OK) |
| if (prop.vt == VT_BOOL && VARIANT_BOOLToBool(prop.boolVal)) |
| _thereAreDeletedItems = true; |
| } |
| } |
| } |
| |
| _thereAre_ListView_Items = true; |
| |
| // OutputDebugStringA("\n\n"); |
| |
| Print_OnNotify("===== Before Load"); |
| |
| // #define USE_EMBED_ITEM |
| |
| if (showDots) |
| { |
| const UString itemName (".."); |
| item.iItem = listViewItemCount; |
| if (itemName == state.FocusedName) |
| cursorIndex = listViewItemCount; |
| item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE; |
| int subItem = 0; |
| item.iSubItem = subItem++; |
| item.lParam = (LPARAM)(int)kParentIndex; |
| #ifdef USE_EMBED_ITEM |
| item.pszText = const_cast<wchar_t *>((const wchar_t *)itemName); |
| #else |
| item.pszText = LPSTR_TEXTCALLBACKW; |
| #endif |
| const UInt32 attrib = FILE_ATTRIBUTE_DIRECTORY; |
| item.iImage = _extToIconMap.GetIconIndex(attrib, itemName); |
| if (item.iImage < 0) |
| item.iImage = 0; |
| if (_listView.InsertItem(&item) == -1) |
| return E_FAIL; |
| listViewItemCount++; |
| } |
| |
| // OutputDebugStringA("S1\n"); |
| |
| UString correctedName; |
| UString itemName; |
| UString relPath; |
| |
| for (UInt32 i = 0; i < numItems; i++) |
| { |
| const wchar_t *name = NULL; |
| unsigned nameLen = 0; |
| |
| if (_folderGetItemName) |
| _folderGetItemName->GetItemName(i, &name, &nameLen); |
| if (!name) |
| { |
| GetItemName(i, itemName); |
| name = itemName; |
| nameLen = itemName.Len(); |
| } |
| |
| bool selected = false; |
| |
| if (state.FocusedName_Defined || !state.SelectedNames.IsEmpty()) |
| { |
| relPath.Empty(); |
| // relPath += GetItemPrefix(i); |
| if (_flatMode) |
| { |
| const wchar_t *prefix = NULL; |
| if (_folderGetItemName) |
| { |
| unsigned prefixLen = 0; |
| _folderGetItemName->GetItemPrefix(i, &prefix, &prefixLen); |
| if (prefix) |
| relPath = prefix; |
| } |
| if (!prefix) |
| { |
| NCOM::CPropVariant prop; |
| if (_folder->GetProperty(i, kpidPrefix, &prop) != S_OK) |
| throw 2723400; |
| if (prop.vt == VT_BSTR) |
| relPath.SetFromBstr(prop.bstrVal); |
| } |
| } |
| relPath += name; |
| if (relPath == state.FocusedName) |
| cursorIndex = listViewItemCount; |
| if (state.SelectedNames.FindInSorted(relPath) != -1) |
| selected = true; |
| } |
| |
| _selectedStatusVector.AddInReserved(selected); |
| |
| item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE; |
| |
| if (!_mySelectMode) |
| if (selected) |
| { |
| item.mask |= LVIF_STATE; |
| item.state = LVIS_SELECTED; |
| } |
| |
| int subItem = 0; |
| item.iItem = listViewItemCount; |
| |
| item.iSubItem = subItem++; |
| item.lParam = (LPARAM)i; |
| |
| /* |
| int finish = nameLen - 4; |
| int j; |
| for (j = 0; j < finish; j++) |
| { |
| if (name[j ] == ' ' && |
| name[j + 1] == ' ' && |
| name[j + 2] == ' ' && |
| name[j + 3] == ' ' && |
| name[j + 4] == ' ') |
| break; |
| } |
| if (j < finish) |
| { |
| correctedName.Empty(); |
| correctedName = "virus"; |
| int pos = 0; |
| for (;;) |
| { |
| int posNew = itemName.Find(L" ", pos); |
| if (posNew < 0) |
| { |
| correctedName += itemName.Ptr(pos); |
| break; |
| } |
| correctedName += itemName.Mid(pos, posNew - pos); |
| correctedName += " ... "; |
| pos = posNew; |
| while (itemName[++pos] == ' '); |
| } |
| item.pszText = const_cast<wchar_t *>((const wchar_t *)correctedName); |
| } |
| else |
| */ |
| { |
| #ifdef USE_EMBED_ITEM |
| item.pszText = const_cast<wchar_t *>((const wchar_t *)name); |
| #else |
| item.pszText = LPSTR_TEXTCALLBACKW; |
| #endif |
| /* LPSTR_TEXTCALLBACKW works, but in some cases there are problems, |
| since we block notify handler. |
| LPSTR_TEXTCALLBACKW can be 2-3 times faster for loading in this loop. */ |
| } |
| |
| bool defined = false; |
| |
| if (folderGetSystemIconIndex) |
| { |
| folderGetSystemIconIndex->GetSystemIconIndex(i, &item.iImage); |
| defined = (item.iImage > 0); |
| } |
| |
| if (!defined) |
| { |
| UInt32 attrib = 0; |
| { |
| NCOM::CPropVariant prop; |
| RINOK(_folder->GetProperty(i, kpidAttrib, &prop)) |
| if (prop.vt == VT_UI4) |
| attrib = prop.ulVal; |
| } |
| if (IsItem_Folder(i)) |
| attrib |= FILE_ATTRIBUTE_DIRECTORY; |
| |
| if (_currentFolderPrefix.IsEmpty()) |
| { |
| int iconIndexTemp; |
| GetRealIconIndex(us2fs((UString)name) + FCHAR_PATH_SEPARATOR, attrib, iconIndexTemp); |
| item.iImage = iconIndexTemp; |
| } |
| else |
| { |
| item.iImage = _extToIconMap.GetIconIndex(attrib, name); |
| } |
| } |
| |
| if (item.iImage < 0) |
| item.iImage = 0; |
| |
| if (_listView.InsertItem(&item) == -1) |
| return E_FAIL; |
| listViewItemCount++; |
| } |
| |
| /* |
| xp-64: there is different order when Windows calls CPanel::OnNotify for _listView modes: |
| Details : after whole code |
| List : 2 times: |
| 1) - ListView.SotRedraw() |
| 2) - after whole code |
| Small Icons : |
| Large icons : 2 times: |
| 1) - ListView.Sort() |
| 2) - after whole code (calls with reverse order of items) |
| |
| So we need to allow Notify(), when windows requests names during the following code. |
| */ |
| |
| Print_OnNotify("after Load"); |
| |
| disableNotify.SetMemMode_Enable(); |
| disableNotify.Restore(); |
| |
| if (_listView.GetItemCount() > 0 && cursorIndex >= 0) |
| SetFocusedSelectedItem(cursorIndex, state.SelectFocused); |
| |
| Print_OnNotify("after SetFocusedSelectedItem"); |
| |
| SetSortRawStatus(); |
| _listView.SortItems(CompareItems, (LPARAM)this); |
| |
| Print_OnNotify("after Sort"); |
| |
| if (cursorIndex < 0 && _listView.GetItemCount() > 0) |
| { |
| if (focusedPos >= _listView.GetItemCount()) |
| focusedPos = _listView.GetItemCount() - 1; |
| // we select item only in showDots mode. |
| SetFocusedSelectedItem(focusedPos, showDots && (focusedPos == 0)); |
| } |
| |
| // m_RedrawEnabled = true; |
| |
| Print_OnNotify("after SetFocusedSelectedItem2"); |
| |
| _listView.EnsureVisible(_listView.GetFocusedItem(), false); |
| |
| // disableNotify.SetMemMode_Enable(); |
| // disableNotify.Restore(); |
| |
| Print_OnNotify("after EnsureVisible"); |
| |
| _listView.SetRedraw(true); |
| |
| Print_OnNotify("after SetRedraw"); |
| |
| _listView.InvalidateRect(NULL, true); |
| |
| Print_OnNotify("after InvalidateRect"); |
| /* |
| _listView.UpdateWindow(); |
| */ |
| Refresh_StatusBar(); |
| /* |
| char s[256]; |
| sprintf(s, |
| // "attribMap = %5d, extMap = %5d, " |
| "delete = %5d, load = %5d, list = %5d, sort = %5d, end = %5d", |
| // _extToIconMap._attribMap.Size(), |
| // _extToIconMap._extMap.Size(), |
| tickCount1 - tickCount0, |
| tickCount2 - tickCount1, |
| tickCount3 - tickCount2, |
| tickCount4 - tickCount3, |
| tickCount5 - tickCount4 |
| ); |
| sprintf(s, |
| "5 = %5d, 6 = %5d, 7 = %5d, 8 = %5d, 9 = %5d", |
| tickCount5 - tickCount4, |
| tickCount6 - tickCount5, |
| tickCount7 - tickCount6, |
| tickCount8 - tickCount7, |
| tickCount9 - tickCount8 |
| ); |
| OutputDebugStringA(s); |
| */ |
| return S_OK; |
| } |
| |
| |
| void CPanel::Get_ItemIndices_Selected(CRecordVector<UInt32> &indices) const |
| { |
| indices.Clear(); |
| /* |
| int itemIndex = -1; |
| while ((itemIndex = _listView.GetNextSelectedItem(itemIndex)) != -1) |
| { |
| LPARAM param; |
| if (_listView.GetItemParam(itemIndex, param)) |
| indices.Add(param); |
| } |
| HeapSort(&indices.Front(), indices.Size()); |
| */ |
| const bool *v = &_selectedStatusVector.Front(); |
| const unsigned size = _selectedStatusVector.Size(); |
| for (unsigned i = 0; i < size; i++) |
| if (v[i]) |
| indices.Add(i); |
| } |
| |
| |
| void CPanel::Get_ItemIndices_Operated(CRecordVector<UInt32> &indices) const |
| { |
| Get_ItemIndices_Selected(indices); |
| if (!indices.IsEmpty()) |
| return; |
| if (_listView.GetSelectedCount() == 0) |
| return; |
| const int focusedItem = _listView.GetFocusedItem(); |
| if (focusedItem >= 0) |
| { |
| if (_listView.IsItemSelected(focusedItem)) |
| { |
| const unsigned realIndex = GetRealItemIndex(focusedItem); |
| if (realIndex != kParentIndex) |
| indices.Add(realIndex); |
| } |
| } |
| } |
| |
| void CPanel::Get_ItemIndices_All(CRecordVector<UInt32> &indices) const |
| { |
| indices.Clear(); |
| UInt32 numItems; |
| if (_folder->GetNumberOfItems(&numItems) != S_OK) |
| return; |
| indices.ClearAndSetSize(numItems); |
| UInt32 *vec = (UInt32 *)&indices.Front(); |
| for (UInt32 i = 0; i < numItems; i++) |
| vec[i] = i; |
| } |
| |
| void CPanel::Get_ItemIndices_OperSmart(CRecordVector<UInt32> &indices) const |
| { |
| Get_ItemIndices_Operated(indices); |
| if (indices.IsEmpty() || (indices.Size() == 1 && indices[0] == (UInt32)(Int32)-1)) |
| Get_ItemIndices_All(indices); |
| } |
| |
| /* |
| void CPanel::GetOperatedListViewIndices(CRecordVector<UInt32> &indices) const |
| { |
| indices.Clear(); |
| int numItems = _listView.GetItemCount(); |
| for (int i = 0; i < numItems; i++) |
| { |
| const unsigned realIndex = GetRealItemIndex(i); |
| if (realIndex >= 0) |
| if (_selectedStatusVector[realIndex]) |
| indices.Add(i); |
| } |
| if (indices.IsEmpty()) |
| { |
| const int focusedItem = _listView.GetFocusedItem(); |
| if (focusedItem >= 0) |
| indices.Add(focusedItem); |
| } |
| } |
| */ |
| |
| void CPanel::EditItem(bool useEditor) |
| { |
| if (!useEditor) |
| { |
| CMyComPtr<IFolderCalcItemFullSize> calcItemFullSize; |
| _folder.QueryInterface(IID_IFolderCalcItemFullSize, &calcItemFullSize); |
| if (calcItemFullSize) |
| { |
| bool needRefresh = false; |
| CRecordVector<UInt32> indices; |
| Get_ItemIndices_Operated(indices); |
| FOR_VECTOR (i, indices) |
| { |
| UInt32 index = indices[i]; |
| if (IsItem_Folder(index)) |
| { |
| calcItemFullSize->CalcItemFullSize(index, NULL); |
| needRefresh = true; |
| } |
| } |
| if (needRefresh) |
| { |
| // _listView.RedrawItem(0); |
| // _listView.RedrawAllItems(); |
| InvalidateList(); |
| return; |
| } |
| } |
| } |
| |
| |
| const int focusedItem = _listView.GetFocusedItem(); |
| if (focusedItem < 0) |
| return; |
| const unsigned realIndex = GetRealItemIndex(focusedItem); |
| if (realIndex == kParentIndex) |
| return; |
| if (!IsItem_Folder(realIndex)) |
| EditItem(realIndex, useEditor); |
| } |
| |
| void CPanel::OpenFocusedItemAsInternal(const wchar_t *type) |
| { |
| const int focusedItem = _listView.GetFocusedItem(); |
| if (focusedItem < 0) |
| return; |
| const unsigned realIndex = GetRealItemIndex(focusedItem); |
| if (IsItem_Folder(realIndex)) |
| OpenFolder(realIndex); |
| else |
| OpenItem(realIndex, true, false, type); |
| } |
| |
| void CPanel::OpenSelectedItems(bool tryInternal) |
| { |
| CRecordVector<UInt32> indices; |
| Get_ItemIndices_Operated(indices); |
| if (indices.Size() > 20) |
| { |
| MessageBox_Error_LangID(IDS_TOO_MANY_ITEMS); |
| return; |
| } |
| |
| const int focusedItem = _listView.GetFocusedItem(); |
| if (focusedItem >= 0) |
| { |
| const unsigned realIndex = GetRealItemIndex(focusedItem); |
| if (realIndex == kParentIndex && (tryInternal || indices.Size() == 0) && _listView.IsItemSelected(focusedItem)) |
| indices.Insert(0, realIndex); |
| } |
| |
| bool dirIsStarted = false; |
| FOR_VECTOR (i, indices) |
| { |
| UInt32 index = indices[i]; |
| // CFileInfo &aFile = m_Files[index]; |
| if (IsItem_Folder(index)) |
| { |
| if (!dirIsStarted) |
| { |
| if (tryInternal) |
| { |
| OpenFolder(index); |
| dirIsStarted = true; |
| break; |
| } |
| else |
| OpenFolderExternal(index); |
| } |
| } |
| else |
| OpenItem(index, (tryInternal && indices.Size() == 1), true); |
| } |
| } |
| |
| UString CPanel::GetItemName(unsigned itemIndex) const |
| { |
| if (itemIndex == kParentIndex) |
| return L".."; |
| NCOM::CPropVariant prop; |
| if (_folder->GetProperty(itemIndex, kpidName, &prop) != S_OK) |
| throw 2723400; |
| if (prop.vt != VT_BSTR) |
| throw 2723401; |
| return prop.bstrVal; |
| } |
| |
| UString CPanel::GetItemName_for_Copy(unsigned itemIndex) const |
| { |
| if (itemIndex == kParentIndex) |
| return L".."; |
| UString s; |
| { |
| NCOM::CPropVariant prop; |
| if (_folder->GetProperty(itemIndex, kpidOutName, &prop) == S_OK) |
| { |
| if (prop.vt == VT_BSTR) |
| s = prop.bstrVal; |
| else if (prop.vt != VT_EMPTY) |
| throw 2723401; |
| } |
| if (s.IsEmpty()) |
| s = GetItemName(itemIndex); |
| } |
| return Get_Correct_FsFile_Name(s); |
| } |
| |
| void CPanel::GetItemName(unsigned itemIndex, UString &s) const |
| { |
| if (itemIndex == kParentIndex) |
| { |
| s = ".."; |
| return; |
| } |
| NCOM::CPropVariant prop; |
| if (_folder->GetProperty(itemIndex, kpidName, &prop) != S_OK) |
| throw 2723400; |
| if (prop.vt != VT_BSTR) |
| throw 2723401; |
| s.SetFromBstr(prop.bstrVal); |
| } |
| |
| UString CPanel::GetItemPrefix(unsigned itemIndex) const |
| { |
| if (itemIndex == kParentIndex) |
| return UString(); |
| NCOM::CPropVariant prop; |
| if (_folder->GetProperty(itemIndex, kpidPrefix, &prop) != S_OK) |
| throw 2723400; |
| UString prefix; |
| if (prop.vt == VT_BSTR) |
| prefix.SetFromBstr(prop.bstrVal); |
| return prefix; |
| } |
| |
| UString CPanel::GetItemRelPath(unsigned itemIndex) const |
| { |
| return GetItemPrefix(itemIndex) + GetItemName(itemIndex); |
| } |
| |
| UString CPanel::GetItemRelPath2(unsigned itemIndex) const |
| { |
| UString s = GetItemRelPath(itemIndex); |
| #if defined(_WIN32) && !defined(UNDER_CE) |
| if (s.Len() == 2 && NFile::NName::IsDrivePath2(s)) |
| { |
| if (IsFSDrivesFolder() && !IsDeviceDrivesPrefix()) |
| s.Add_PathSepar(); |
| } |
| #endif |
| return s; |
| } |
| |
| |
| void CPanel::Add_ItemRelPath2_To_String(unsigned itemIndex, UString &s) const |
| { |
| if (itemIndex == kParentIndex) |
| { |
| s += ".."; |
| return; |
| } |
| |
| const unsigned start = s.Len(); |
| NCOM::CPropVariant prop; |
| if (_folder->GetProperty(itemIndex, kpidPrefix, &prop) != S_OK) |
| throw 2723400; |
| if (prop.vt == VT_BSTR) |
| s += prop.bstrVal; |
| |
| const wchar_t *name = NULL; |
| unsigned nameLen = 0; |
| |
| if (_folderGetItemName) |
| _folderGetItemName->GetItemName(itemIndex, &name, &nameLen); |
| if (name) |
| s += name; |
| else |
| { |
| prop.Clear(); |
| if (_folder->GetProperty(itemIndex, kpidName, &prop) != S_OK) |
| throw 2723400; |
| if (prop.vt != VT_BSTR) |
| throw 2723401; |
| s += prop.bstrVal; |
| } |
| |
| #if defined(_WIN32) && !defined(UNDER_CE) |
| if (s.Len() - start == 2 && NFile::NName::IsDrivePath2(s.Ptr(start))) |
| { |
| if (IsFSDrivesFolder() && !IsDeviceDrivesPrefix()) |
| s.Add_PathSepar(); |
| } |
| #endif |
| } |
| |
| |
| UString CPanel::GetItemFullPath(unsigned itemIndex) const |
| { |
| return GetFsPath() + GetItemRelPath2(itemIndex); |
| } |
| |
| bool CPanel::GetItem_BoolProp(UInt32 itemIndex, PROPID propID) const |
| { |
| NCOM::CPropVariant prop; |
| if (_folder->GetProperty(itemIndex, propID, &prop) != S_OK) |
| throw 2723400; |
| if (prop.vt == VT_BOOL) |
| return VARIANT_BOOLToBool(prop.boolVal); |
| if (prop.vt == VT_EMPTY) |
| return false; |
| throw 2723401; |
| } |
| |
| bool CPanel::IsItem_Deleted(unsigned itemIndex) const |
| { |
| if (itemIndex == kParentIndex) |
| return false; |
| return GetItem_BoolProp(itemIndex, kpidIsDeleted); |
| } |
| |
| bool CPanel::IsItem_Folder(unsigned itemIndex) const |
| { |
| if (itemIndex == kParentIndex) |
| return true; |
| if (itemIndex < _isDirVector.Size()) |
| return _isDirVector[itemIndex]; |
| return GetItem_BoolProp(itemIndex, kpidIsDir); |
| } |
| |
| bool CPanel::IsItem_AltStream(unsigned itemIndex) const |
| { |
| if (itemIndex == kParentIndex) |
| return false; |
| return GetItem_BoolProp(itemIndex, kpidIsAltStream); |
| } |
| |
| UInt64 CPanel::GetItem_UInt64Prop(unsigned itemIndex, PROPID propID) const |
| { |
| if (itemIndex == kParentIndex) |
| return 0; |
| NCOM::CPropVariant prop; |
| if (_folder->GetProperty(itemIndex, propID, &prop) != S_OK) |
| throw 2723400; |
| UInt64 val = 0; |
| if (ConvertPropVariantToUInt64(prop, val)) |
| return val; |
| return 0; |
| } |
| |
| UInt64 CPanel::GetItemSize(unsigned itemIndex) const |
| { |
| if (itemIndex == kParentIndex) |
| return 0; |
| if (_folderGetItemName) |
| return _folderGetItemName->GetItemSize(itemIndex); |
| return GetItem_UInt64Prop(itemIndex, kpidSize); |
| } |
| |
| void CPanel::SaveListViewInfo() |
| { |
| if (!_needSaveInfo) |
| return; |
| |
| unsigned i; |
| |
| for (i = 0; i < _visibleColumns.Size(); i++) |
| { |
| CPropColumn &prop = _visibleColumns[i]; |
| LVCOLUMN winColumnInfo; |
| winColumnInfo.mask = LVCF_ORDER | LVCF_WIDTH; |
| if (!_listView.GetColumn(i, &winColumnInfo)) |
| throw 1; |
| prop.Order = winColumnInfo.iOrder; |
| prop.Width = (UInt32)(Int32)winColumnInfo.cx; |
| } |
| |
| CListViewInfo viewInfo; |
| |
| // PROPID sortPropID = _columns[_sortIndex].ID; |
| PROPID sortPropID = _sortID; |
| |
| // we save columns as "sorted by order" to registry |
| |
| CPropColumns sortedProperties = _visibleColumns; |
| |
| sortedProperties.Sort(); |
| |
| for (i = 0; i < sortedProperties.Size(); i++) |
| { |
| const CPropColumn &prop = sortedProperties[i]; |
| CColumnInfo columnInfo; |
| columnInfo.IsVisible = prop.IsVisible; |
| columnInfo.PropID = prop.ID; |
| columnInfo.Width = prop.Width; |
| viewInfo.Columns.Add(columnInfo); |
| } |
| |
| for (i = 0; i < _columns.Size(); i++) |
| { |
| const CPropColumn &prop = _columns[i]; |
| if (sortedProperties.FindItem_for_PropID(prop.ID) < 0) |
| { |
| CColumnInfo columnInfo; |
| columnInfo.IsVisible = false; |
| columnInfo.PropID = prop.ID; |
| columnInfo.Width = prop.Width; |
| viewInfo.Columns.Add(columnInfo); |
| } |
| } |
| |
| viewInfo.SortID = sortPropID; |
| viewInfo.Ascending = _ascending; |
| viewInfo.IsLoaded = true; |
| if (!_listViewInfo.IsEqual(viewInfo)) |
| { |
| viewInfo.Save(_typeIDString); |
| _listViewInfo = viewInfo; |
| } |
| } |
| |
| |
| bool CPanel::OnRightClick(MY_NMLISTVIEW_NMITEMACTIVATE *itemActiveate, LRESULT &result) |
| { |
| if (itemActiveate->hdr.hwndFrom == HWND(_listView)) |
| return false; |
| POINT point; |
| ::GetCursorPos(&point); |
| ShowColumnsContextMenu(point.x, point.y); |
| result = TRUE; |
| return true; |
| } |
| |
| void CPanel::ShowColumnsContextMenu(int x, int y) |
| { |
| CMenu menu; |
| CMenuDestroyer menuDestroyer(menu); |
| |
| menu.CreatePopup(); |
| |
| const int kCommandStart = 100; |
| FOR_VECTOR (i, _columns) |
| { |
| const CPropColumn &prop = _columns[i]; |
| UINT flags = MF_STRING; |
| if (prop.IsVisible) |
| flags |= MF_CHECKED; |
| if (i == 0) |
| flags |= MF_GRAYED; |
| menu.AppendItem(flags, kCommandStart + i, prop.Name); |
| } |
| |
| const int menuResult = menu.Track(TPM_LEFTALIGN | TPM_RETURNCMD | TPM_NONOTIFY, x, y, _listView); |
| |
| if (menuResult >= kCommandStart && menuResult <= kCommandStart + (int)_columns.Size()) |
| { |
| const unsigned index = (unsigned)(menuResult - kCommandStart); |
| CPropColumn &prop = _columns[index]; |
| prop.IsVisible = !prop.IsVisible; |
| |
| if (prop.IsVisible) |
| { |
| prop.Order = (int)_visibleColumns.Size(); |
| AddColumn(prop); |
| } |
| else |
| { |
| const int visibleIndex = _visibleColumns.FindItem_for_PropID(prop.ID); |
| if (visibleIndex >= 0) |
| { |
| /* |
| if (_sortIndex == index) |
| { |
| _sortIndex = 0; |
| _ascending = true; |
| } |
| */ |
| if (_sortID == prop.ID) |
| { |
| _sortID = kpidName; |
| _ascending = true; |
| } |
| DeleteColumn((unsigned)visibleIndex); |
| } |
| } |
| } |
| } |
| |
| void CPanel::OnReload(bool onTimer) |
| { |
| const HRESULT res = RefreshListCtrl_SaveFocused(onTimer); |
| if (res != S_OK) |
| MessageBox_Error_HRESULT(res); |
| } |
| |
| void CPanel::OnTimer() |
| { |
| if (!_processTimer) |
| return; |
| if (!AutoRefresh_Mode) |
| return; |
| CMyComPtr<IFolderWasChanged> folderWasChanged; |
| if (_folder.QueryInterface(IID_IFolderWasChanged, &folderWasChanged) != S_OK) |
| return; |
| Int32 wasChanged; |
| if (folderWasChanged->WasChanged(&wasChanged) != S_OK) |
| return; |
| if (wasChanged == 0) |
| return; |
| OnReload(true); // onTimer |
| } |