michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsIFileView.h" michael@0: #include "nsITreeView.h" michael@0: #include "mozilla/ModuleUtils.h" michael@0: #include "nsITreeSelection.h" michael@0: #include "nsITreeColumns.h" michael@0: #include "nsITreeBoxObject.h" michael@0: #include "nsIFile.h" michael@0: #include "nsString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsCRT.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsIDateTimeFormat.h" michael@0: #include "nsDateTimeFormatCID.h" michael@0: #include "nsQuickSort.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsIAutoCompleteResult.h" michael@0: #include "nsIAutoCompleteSearch.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsIMutableArray.h" michael@0: #include "nsTArray.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include "nsWildCard.h" michael@0: michael@0: class nsIDOMDataTransfer; michael@0: michael@0: #define NS_FILECOMPLETE_CID { 0xcb60980e, 0x18a5, 0x4a77, \ michael@0: { 0x91, 0x10, 0x81, 0x46, 0x61, 0x4c, 0xa7, 0xf0 } } michael@0: #define NS_FILECOMPLETE_CONTRACTID "@mozilla.org/autocomplete/search;1?name=file" michael@0: michael@0: class nsFileResult MOZ_FINAL : public nsIAutoCompleteResult michael@0: { michael@0: public: michael@0: // aSearchString is the text typed into the autocomplete widget michael@0: // aSearchParam is the picker's currently displayed directory michael@0: nsFileResult(const nsAString& aSearchString, const nsAString& aSearchParam); michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIAUTOCOMPLETERESULT michael@0: michael@0: nsTArray mValues; michael@0: nsAutoString mSearchString; michael@0: uint16_t mSearchResult; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsFileResult, nsIAutoCompleteResult) michael@0: michael@0: nsFileResult::nsFileResult(const nsAString& aSearchString, michael@0: const nsAString& aSearchParam): michael@0: mSearchString(aSearchString) michael@0: { michael@0: if (aSearchString.IsEmpty()) michael@0: mSearchResult = RESULT_IGNORED; michael@0: else { michael@0: int32_t slashPos = mSearchString.RFindChar('/'); michael@0: mSearchResult = RESULT_FAILURE; michael@0: nsCOMPtr directory; michael@0: nsDependentSubstring parent(Substring(mSearchString, 0, slashPos + 1)); michael@0: if (!parent.IsEmpty() && parent.First() == '/') michael@0: NS_NewLocalFile(parent, true, getter_AddRefs(directory)); michael@0: if (!directory) { michael@0: if (NS_FAILED(NS_NewLocalFile(aSearchParam, true, getter_AddRefs(directory)))) michael@0: return; michael@0: if (slashPos > 0) michael@0: directory->AppendRelativePath(Substring(mSearchString, 0, slashPos)); michael@0: } michael@0: nsCOMPtr dirEntries; michael@0: if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(dirEntries)))) michael@0: return; michael@0: mSearchResult = RESULT_NOMATCH; michael@0: bool hasMore = false; michael@0: nsDependentSubstring prefix(Substring(mSearchString, slashPos + 1)); michael@0: while (NS_SUCCEEDED(dirEntries->HasMoreElements(&hasMore)) && hasMore) { michael@0: nsCOMPtr nextItem; michael@0: dirEntries->GetNext(getter_AddRefs(nextItem)); michael@0: nsCOMPtr nextFile(do_QueryInterface(nextItem)); michael@0: nsAutoString fileName; michael@0: nextFile->GetLeafName(fileName); michael@0: if (StringBeginsWith(fileName, prefix)) { michael@0: fileName.Insert(parent, 0); michael@0: if (mSearchResult == RESULT_NOMATCH && fileName.Equals(mSearchString)) michael@0: mSearchResult = RESULT_IGNORED; michael@0: else michael@0: mSearchResult = RESULT_SUCCESS; michael@0: bool isDirectory = false; michael@0: nextFile->IsDirectory(&isDirectory); michael@0: if (isDirectory) michael@0: fileName.Append('/'); michael@0: mValues.AppendElement(fileName); michael@0: } michael@0: } michael@0: mValues.Sort(); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::GetSearchString(nsAString & aSearchString) michael@0: { michael@0: aSearchString.Assign(mSearchString); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::GetSearchResult(uint16_t *aSearchResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aSearchResult); michael@0: *aSearchResult = mSearchResult; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::GetDefaultIndex(int32_t *aDefaultIndex) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aDefaultIndex); michael@0: *aDefaultIndex = -1; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::GetErrorDescription(nsAString & aErrorDescription) michael@0: { michael@0: aErrorDescription.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::GetMatchCount(uint32_t *aMatchCount) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aMatchCount); michael@0: *aMatchCount = mValues.Length(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::GetTypeAheadResult(bool *aTypeAheadResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aTypeAheadResult); michael@0: *aTypeAheadResult = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::GetValueAt(int32_t index, nsAString & aValue) michael@0: { michael@0: aValue = mValues[index]; michael@0: if (aValue.Last() == '/') michael@0: aValue.Truncate(aValue.Length() - 1); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::GetLabelAt(int32_t index, nsAString & aValue) michael@0: { michael@0: return GetValueAt(index, aValue); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::GetCommentAt(int32_t index, nsAString & aComment) michael@0: { michael@0: aComment.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::GetStyleAt(int32_t index, nsAString & aStyle) michael@0: { michael@0: if (mValues[index].Last() == '/') michael@0: aStyle.AssignLiteral("directory"); michael@0: else michael@0: aStyle.AssignLiteral("file"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::GetImageAt(int32_t index, nsAString & aImage) michael@0: { michael@0: aImage.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: NS_IMETHODIMP nsFileResult::GetFinalCompleteValueAt(int32_t index, michael@0: nsAString & aValue) michael@0: { michael@0: return GetValueAt(index, aValue); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFileResult::RemoveValueAt(int32_t rowIndex, bool removeFromDb) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: class nsFileComplete MOZ_FINAL : public nsIAutoCompleteSearch michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIAUTOCOMPLETESEARCH michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsFileComplete, nsIAutoCompleteSearch) michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileComplete::StartSearch(const nsAString& aSearchString, michael@0: const nsAString& aSearchParam, michael@0: nsIAutoCompleteResult *aPreviousResult, michael@0: nsIAutoCompleteObserver *aListener) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aListener); michael@0: nsRefPtr result = new nsFileResult(aSearchString, aSearchParam); michael@0: NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); michael@0: return aListener->OnSearchResult(this, result); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileComplete::StopSearch() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: #define NS_FILEVIEW_CID { 0xa5570462, 0x1dd1, 0x11b2, \ michael@0: { 0x9d, 0x19, 0xdf, 0x30, 0xa2, 0x7f, 0xbd, 0xc4 } } michael@0: michael@0: class nsFileView : public nsIFileView, michael@0: public nsITreeView michael@0: { michael@0: public: michael@0: nsFileView(); michael@0: nsresult Init(); michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIFILEVIEW michael@0: NS_DECL_NSITREEVIEW michael@0: michael@0: protected: michael@0: virtual ~nsFileView(); michael@0: michael@0: void FilterFiles(); michael@0: void ReverseArray(nsTArray >& aArray); michael@0: void SortArray(nsTArray >& aArray); michael@0: void SortInternal(); michael@0: michael@0: nsTArray > mFileList; michael@0: nsTArray > mDirList; michael@0: nsTArray > mFilteredFiles; michael@0: michael@0: nsCOMPtr mDirectoryPath; michael@0: nsCOMPtr mTree; michael@0: nsCOMPtr mSelection; michael@0: nsCOMPtr mDateFormatter; michael@0: michael@0: int16_t mSortType; michael@0: int32_t mTotalRows; michael@0: michael@0: nsTArray mCurrentFilters; michael@0: michael@0: bool mShowHiddenFiles; michael@0: bool mDirectoryFilter; michael@0: bool mReverseSort; michael@0: }; michael@0: michael@0: // Factory constructor michael@0: NS_GENERIC_FACTORY_CONSTRUCTOR(nsFileComplete) michael@0: NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFileView, Init) michael@0: NS_DEFINE_NAMED_CID(NS_FILECOMPLETE_CID); michael@0: NS_DEFINE_NAMED_CID(NS_FILEVIEW_CID); michael@0: michael@0: static const mozilla::Module::CIDEntry kFileViewCIDs[] = { michael@0: { &kNS_FILECOMPLETE_CID, false, nullptr, nsFileCompleteConstructor }, michael@0: { &kNS_FILEVIEW_CID, false, nullptr, nsFileViewConstructor }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module::ContractIDEntry kFileViewContracts[] = { michael@0: { NS_FILECOMPLETE_CONTRACTID, &kNS_FILECOMPLETE_CID }, michael@0: { NS_FILEVIEW_CONTRACTID, &kNS_FILEVIEW_CID }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module kFileViewModule = { michael@0: mozilla::Module::kVersion, michael@0: kFileViewCIDs, michael@0: kFileViewContracts michael@0: }; michael@0: michael@0: NSMODULE_DEFN(nsFileViewModule) = &kFileViewModule; michael@0: michael@0: nsFileView::nsFileView() : michael@0: mSortType(-1), michael@0: mTotalRows(0), michael@0: mShowHiddenFiles(false), michael@0: mDirectoryFilter(false), michael@0: mReverseSort(false) michael@0: { michael@0: } michael@0: michael@0: nsFileView::~nsFileView() michael@0: { michael@0: uint32_t count = mCurrentFilters.Length(); michael@0: for (uint32_t i = 0; i < count; ++i) michael@0: NS_Free(mCurrentFilters[i]); michael@0: } michael@0: michael@0: nsresult michael@0: nsFileView::Init() michael@0: { michael@0: mDateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID); michael@0: if (!mDateFormatter) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // nsISupports implementation michael@0: michael@0: NS_IMPL_ISUPPORTS(nsFileView, nsITreeView, nsIFileView) michael@0: michael@0: // nsIFileView implementation michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::SetShowHiddenFiles(bool aShowHidden) michael@0: { michael@0: if (aShowHidden != mShowHiddenFiles) { michael@0: mShowHiddenFiles = aShowHidden; michael@0: michael@0: // This could be better optimized, but since the hidden michael@0: // file functionality is not currently used, this will be fine. michael@0: SetDirectory(mDirectoryPath); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetShowHiddenFiles(bool* aShowHidden) michael@0: { michael@0: *aShowHidden = mShowHiddenFiles; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::SetShowOnlyDirectories(bool aOnlyDirs) michael@0: { michael@0: if (aOnlyDirs == mDirectoryFilter) michael@0: return NS_OK; michael@0: michael@0: mDirectoryFilter = aOnlyDirs; michael@0: uint32_t dirCount = mDirList.Length(); michael@0: if (mDirectoryFilter) { michael@0: int32_t rowDiff = mTotalRows - dirCount; michael@0: michael@0: mFilteredFiles.Clear(); michael@0: mTotalRows = dirCount; michael@0: if (mTree) michael@0: mTree->RowCountChanged(mTotalRows, -rowDiff); michael@0: } else { michael@0: // Run the filter again to get the file list back michael@0: FilterFiles(); michael@0: michael@0: SortArray(mFilteredFiles); michael@0: if (mReverseSort) michael@0: ReverseArray(mFilteredFiles); michael@0: michael@0: if (mTree) michael@0: mTree->RowCountChanged(dirCount, mTotalRows - dirCount); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetShowOnlyDirectories(bool* aOnlyDirs) michael@0: { michael@0: *aOnlyDirs = mDirectoryFilter; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetSortType(int16_t* aSortType) michael@0: { michael@0: *aSortType = mSortType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetReverseSort(bool* aReverseSort) michael@0: { michael@0: *aReverseSort = mReverseSort; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::Sort(int16_t aSortType, bool aReverseSort) michael@0: { michael@0: if (aSortType == mSortType) { michael@0: if (aReverseSort == mReverseSort) michael@0: return NS_OK; michael@0: michael@0: mReverseSort = aReverseSort; michael@0: ReverseArray(mDirList); michael@0: ReverseArray(mFilteredFiles); michael@0: } else { michael@0: mSortType = aSortType; michael@0: mReverseSort = aReverseSort; michael@0: SortInternal(); michael@0: } michael@0: michael@0: if (mTree) michael@0: mTree->Invalidate(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::SetDirectory(nsIFile* aDirectory) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aDirectory); michael@0: michael@0: nsCOMPtr dirEntries; michael@0: aDirectory->GetDirectoryEntries(getter_AddRefs(dirEntries)); michael@0: michael@0: if (!dirEntries) { michael@0: // Couldn't read in the directory, this can happen if the user does not michael@0: // have permission to list it. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mDirectoryPath = aDirectory; michael@0: mFileList.Clear(); michael@0: mDirList.Clear(); michael@0: michael@0: bool hasMore = false; michael@0: michael@0: while (NS_SUCCEEDED(dirEntries->HasMoreElements(&hasMore)) && hasMore) { michael@0: nsCOMPtr nextItem; michael@0: dirEntries->GetNext(getter_AddRefs(nextItem)); michael@0: nsCOMPtr theFile = do_QueryInterface(nextItem); michael@0: michael@0: bool isDirectory = false; michael@0: if (theFile) { michael@0: theFile->IsDirectory(&isDirectory); michael@0: michael@0: if (isDirectory) { michael@0: bool isHidden; michael@0: theFile->IsHidden(&isHidden); michael@0: if (mShowHiddenFiles || !isHidden) { michael@0: mDirList.AppendElement(theFile); michael@0: } michael@0: } michael@0: else { michael@0: mFileList.AppendElement(theFile); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (mTree) { michael@0: mTree->BeginUpdateBatch(); michael@0: mTree->RowCountChanged(0, -mTotalRows); michael@0: } michael@0: michael@0: FilterFiles(); michael@0: SortInternal(); michael@0: michael@0: if (mTree) { michael@0: mTree->EndUpdateBatch(); michael@0: mTree->ScrollToRow(0); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::SetFilter(const nsAString& aFilterString) michael@0: { michael@0: uint32_t filterCount = mCurrentFilters.Length(); michael@0: for (uint32_t i = 0; i < filterCount; ++i) michael@0: NS_Free(mCurrentFilters[i]); michael@0: mCurrentFilters.Clear(); michael@0: michael@0: nsAString::const_iterator start, iter, end; michael@0: aFilterString.BeginReading(iter); michael@0: aFilterString.EndReading(end); michael@0: michael@0: while (true) { michael@0: // skip over delimiters michael@0: while (iter != end && (*iter == ';' || *iter == ' ')) michael@0: ++iter; michael@0: michael@0: if (iter == end) michael@0: break; michael@0: michael@0: start = iter; // start of a filter michael@0: michael@0: // we know this is neither ';' nor ' ', skip to next char michael@0: ++iter; michael@0: michael@0: // find next delimiter or end of string michael@0: while (iter != end && (*iter != ';' && *iter != ' ')) michael@0: ++iter; michael@0: michael@0: char16_t* filter = ToNewUnicode(Substring(start, iter)); michael@0: if (!filter) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: if (!mCurrentFilters.AppendElement(filter)) { michael@0: NS_Free(filter); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: if (iter == end) michael@0: break; michael@0: michael@0: ++iter; // we know this is either ';' or ' ', skip to next char michael@0: } michael@0: michael@0: if (mTree) { michael@0: mTree->BeginUpdateBatch(); michael@0: uint32_t count = mDirList.Length(); michael@0: mTree->RowCountChanged(count, count - mTotalRows); michael@0: } michael@0: michael@0: mFilteredFiles.Clear(); michael@0: michael@0: FilterFiles(); michael@0: michael@0: SortArray(mFilteredFiles); michael@0: if (mReverseSort) michael@0: ReverseArray(mFilteredFiles); michael@0: michael@0: if (mTree) michael@0: mTree->EndUpdateBatch(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetSelectedFiles(nsIArray** aFiles) michael@0: { michael@0: *aFiles = nullptr; michael@0: if (!mSelection) michael@0: return NS_OK; michael@0: michael@0: int32_t numRanges; michael@0: mSelection->GetRangeCount(&numRanges); michael@0: michael@0: uint32_t dirCount = mDirList.Length(); michael@0: nsCOMPtr fileArray = michael@0: do_CreateInstance(NS_ARRAY_CONTRACTID); michael@0: NS_ENSURE_STATE(fileArray); michael@0: michael@0: for (int32_t range = 0; range < numRanges; ++range) { michael@0: int32_t rangeBegin, rangeEnd; michael@0: mSelection->GetRangeAt(range, &rangeBegin, &rangeEnd); michael@0: michael@0: for (int32_t itemIndex = rangeBegin; itemIndex <= rangeEnd; ++itemIndex) { michael@0: nsIFile* curFile = nullptr; michael@0: michael@0: if (itemIndex < (int32_t) dirCount) michael@0: curFile = mDirList[itemIndex]; michael@0: else { michael@0: if (itemIndex < mTotalRows) michael@0: curFile = mFilteredFiles[itemIndex - dirCount]; michael@0: } michael@0: michael@0: if (curFile) michael@0: fileArray->AppendElement(curFile, false); michael@0: } michael@0: } michael@0: michael@0: NS_ADDREF(*aFiles = fileArray); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // nsITreeView implementation michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetRowCount(int32_t* aRowCount) michael@0: { michael@0: *aRowCount = mTotalRows; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetSelection(nsITreeSelection** aSelection) michael@0: { michael@0: *aSelection = mSelection; michael@0: NS_IF_ADDREF(*aSelection); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::SetSelection(nsITreeSelection* aSelection) michael@0: { michael@0: mSelection = aSelection; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetRowProperties(int32_t aIndex, nsAString& aProps) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetCellProperties(int32_t aRow, nsITreeColumn* aCol, michael@0: nsAString& aProps) michael@0: { michael@0: uint32_t dirCount = mDirList.Length(); michael@0: michael@0: if (aRow < (int32_t) dirCount) michael@0: aProps.AppendLiteral("directory"); michael@0: else if (aRow < mTotalRows) michael@0: aProps.AppendLiteral("file"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetColumnProperties(nsITreeColumn* aCol, nsAString& aProps) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::IsContainer(int32_t aIndex, bool* aIsContainer) michael@0: { michael@0: *aIsContainer = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::IsContainerOpen(int32_t aIndex, bool* aIsOpen) michael@0: { michael@0: *aIsOpen = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::IsContainerEmpty(int32_t aIndex, bool* aIsEmpty) michael@0: { michael@0: *aIsEmpty = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::IsSeparator(int32_t aIndex, bool* aIsSeparator) michael@0: { michael@0: *aIsSeparator = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::IsSorted(bool* aIsSorted) michael@0: { michael@0: *aIsSorted = (mSortType >= 0); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::CanDrop(int32_t aIndex, int32_t aOrientation, michael@0: nsIDOMDataTransfer* dataTransfer, bool* aCanDrop) michael@0: { michael@0: *aCanDrop = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::Drop(int32_t aRow, int32_t aOrientation, nsIDOMDataTransfer* dataTransfer) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetParentIndex(int32_t aRowIndex, int32_t* aParentIndex) michael@0: { michael@0: *aParentIndex = -1; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex, michael@0: bool* aHasSibling) michael@0: { michael@0: *aHasSibling = (aAfterIndex < (mTotalRows - 1)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetLevel(int32_t aIndex, int32_t* aLevel) michael@0: { michael@0: *aLevel = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetImageSrc(int32_t aRow, nsITreeColumn* aCol, michael@0: nsAString& aImageSrc) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetProgressMode(int32_t aRow, nsITreeColumn* aCol, michael@0: int32_t* aProgressMode) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetCellValue(int32_t aRow, nsITreeColumn* aCol, michael@0: nsAString& aCellValue) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::GetCellText(int32_t aRow, nsITreeColumn* aCol, michael@0: nsAString& aCellText) michael@0: { michael@0: uint32_t dirCount = mDirList.Length(); michael@0: bool isDirectory; michael@0: nsIFile* curFile = nullptr; michael@0: michael@0: if (aRow < (int32_t) dirCount) { michael@0: isDirectory = true; michael@0: curFile = mDirList[aRow]; michael@0: } else if (aRow < mTotalRows) { michael@0: isDirectory = false; michael@0: curFile = mFilteredFiles[aRow - dirCount]; michael@0: } else { michael@0: // invalid row michael@0: aCellText.SetCapacity(0); michael@0: return NS_OK; michael@0: } michael@0: michael@0: const char16_t* colID; michael@0: aCol->GetIdConst(&colID); michael@0: if (NS_LITERAL_STRING("FilenameColumn").Equals(colID)) { michael@0: curFile->GetLeafName(aCellText); michael@0: } else if (NS_LITERAL_STRING("LastModifiedColumn").Equals(colID)) { michael@0: PRTime lastModTime; michael@0: curFile->GetLastModifiedTime(&lastModTime); michael@0: // XXX FormatPRTime could take an nsAString& michael@0: nsAutoString temp; michael@0: mDateFormatter->FormatPRTime(nullptr, kDateFormatShort, kTimeFormatSeconds, michael@0: lastModTime * 1000, temp); michael@0: aCellText = temp; michael@0: } else { michael@0: // file size michael@0: if (isDirectory) michael@0: aCellText.SetCapacity(0); michael@0: else { michael@0: int64_t fileSize; michael@0: curFile->GetFileSize(&fileSize); michael@0: CopyUTF8toUTF16(nsPrintfCString("%lld", fileSize), aCellText); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::SetTree(nsITreeBoxObject* aTree) michael@0: { michael@0: mTree = aTree; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::ToggleOpenState(int32_t aIndex) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::CycleHeader(nsITreeColumn* aCol) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::SelectionChanged() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::CycleCell(int32_t aRow, nsITreeColumn* aCol) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::IsEditable(int32_t aRow, nsITreeColumn* aCol, michael@0: bool* aIsEditable) michael@0: { michael@0: *aIsEditable = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::IsSelectable(int32_t aRow, nsITreeColumn* aCol, michael@0: bool* aIsSelectable) michael@0: { michael@0: *aIsSelectable = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::SetCellValue(int32_t aRow, nsITreeColumn* aCol, michael@0: const nsAString& aValue) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::SetCellText(int32_t aRow, nsITreeColumn* aCol, michael@0: const nsAString& aValue) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::PerformAction(const char16_t* aAction) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::PerformActionOnRow(const char16_t* aAction, int32_t aRow) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFileView::PerformActionOnCell(const char16_t* aAction, int32_t aRow, michael@0: nsITreeColumn* aCol) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Private methods michael@0: michael@0: void michael@0: nsFileView::FilterFiles() michael@0: { michael@0: uint32_t count = mDirList.Length(); michael@0: mTotalRows = count; michael@0: count = mFileList.Length(); michael@0: mFilteredFiles.Clear(); michael@0: uint32_t filterCount = mCurrentFilters.Length(); michael@0: michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: nsIFile* file = mFileList[i]; michael@0: bool isHidden = false; michael@0: if (!mShowHiddenFiles) michael@0: file->IsHidden(&isHidden); michael@0: michael@0: nsAutoString ucsLeafName; michael@0: if(NS_FAILED(file->GetLeafName(ucsLeafName))) { michael@0: // need to check return value for GetLeafName() michael@0: continue; michael@0: } michael@0: michael@0: if (!isHidden) { michael@0: for (uint32_t j = 0; j < filterCount; ++j) { michael@0: bool matched = false; michael@0: if (!nsCRT::strcmp(mCurrentFilters.ElementAt(j), michael@0: MOZ_UTF16("..apps"))) michael@0: { michael@0: file->IsExecutable(&matched); michael@0: } else michael@0: matched = (NS_WildCardMatch(ucsLeafName.get(), michael@0: mCurrentFilters.ElementAt(j), michael@0: true) == MATCH); michael@0: michael@0: if (matched) { michael@0: mFilteredFiles.AppendElement(file); michael@0: ++mTotalRows; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFileView::ReverseArray(nsTArray >& aArray) michael@0: { michael@0: uint32_t count = aArray.Length(); michael@0: for (uint32_t i = 0; i < count/2; ++i) { michael@0: // If we get references to the COMPtrs in the array, and then .swap() them michael@0: // we avoid AdRef() / Release() calls. michael@0: nsCOMPtr& element = aArray[i]; michael@0: nsCOMPtr& element2 = aArray[count - i - 1]; michael@0: element.swap(element2); michael@0: } michael@0: } michael@0: michael@0: static int michael@0: SortNameCallback(const void* aElement1, const void* aElement2, void* aContext) michael@0: { michael@0: nsIFile* file1 = *static_cast(aElement1); michael@0: nsIFile* file2 = *static_cast(aElement2); michael@0: michael@0: nsAutoString leafName1, leafName2; michael@0: file1->GetLeafName(leafName1); michael@0: file2->GetLeafName(leafName2); michael@0: michael@0: return Compare(leafName1, leafName2); michael@0: } michael@0: michael@0: static int michael@0: SortSizeCallback(const void* aElement1, const void* aElement2, void* aContext) michael@0: { michael@0: nsIFile* file1 = *static_cast(aElement1); michael@0: nsIFile* file2 = *static_cast(aElement2); michael@0: michael@0: int64_t size1, size2; michael@0: file1->GetFileSize(&size1); michael@0: file2->GetFileSize(&size2); michael@0: michael@0: if (size1 == size2) michael@0: return 0; michael@0: michael@0: return size1 < size2 ? -1 : 1; michael@0: } michael@0: michael@0: static int michael@0: SortDateCallback(const void* aElement1, const void* aElement2, void* aContext) michael@0: { michael@0: nsIFile* file1 = *static_cast(aElement1); michael@0: nsIFile* file2 = *static_cast(aElement2); michael@0: michael@0: PRTime time1, time2; michael@0: file1->GetLastModifiedTime(&time1); michael@0: file2->GetLastModifiedTime(&time2); michael@0: michael@0: if (time1 == time2) michael@0: return 0; michael@0: michael@0: return time1 < time2 ? -1 : 1; michael@0: } michael@0: michael@0: void michael@0: nsFileView::SortArray(nsTArray >& aArray) michael@0: { michael@0: // We assume the array to be in filesystem order, which michael@0: // for our purposes, is completely unordered. michael@0: michael@0: int (*compareFunc)(const void*, const void*, void*); michael@0: michael@0: switch (mSortType) { michael@0: case sortName: michael@0: compareFunc = SortNameCallback; michael@0: break; michael@0: case sortSize: michael@0: compareFunc = SortSizeCallback; michael@0: break; michael@0: case sortDate: michael@0: compareFunc = SortDateCallback; michael@0: break; michael@0: default: michael@0: return; michael@0: } michael@0: michael@0: uint32_t count = aArray.Length(); michael@0: michael@0: nsIFile** array = new nsIFile*[count]; michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: array[i] = aArray[i]; michael@0: } michael@0: michael@0: NS_QuickSort(array, count, sizeof(nsIFile*), compareFunc, nullptr); michael@0: michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: // Use swap() to avoid refcounting. michael@0: aArray[i].swap(array[i]); michael@0: } michael@0: michael@0: delete[] array; michael@0: } michael@0: michael@0: void michael@0: nsFileView::SortInternal() michael@0: { michael@0: SortArray(mDirList); michael@0: SortArray(mFilteredFiles); michael@0: michael@0: if (mReverseSort) { michael@0: ReverseArray(mDirList); michael@0: ReverseArray(mFilteredFiles); michael@0: } michael@0: }