toolkit/components/filepicker/nsFileView.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:f1775f49195a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsIFileView.h"
7 #include "nsITreeView.h"
8 #include "mozilla/ModuleUtils.h"
9 #include "nsITreeSelection.h"
10 #include "nsITreeColumns.h"
11 #include "nsITreeBoxObject.h"
12 #include "nsIFile.h"
13 #include "nsString.h"
14 #include "nsReadableUtils.h"
15 #include "nsCRT.h"
16 #include "nsPrintfCString.h"
17 #include "nsIDateTimeFormat.h"
18 #include "nsDateTimeFormatCID.h"
19 #include "nsQuickSort.h"
20 #include "nsIAtom.h"
21 #include "nsIAutoCompleteResult.h"
22 #include "nsIAutoCompleteSearch.h"
23 #include "nsISimpleEnumerator.h"
24 #include "nsAutoPtr.h"
25 #include "nsIMutableArray.h"
26 #include "nsTArray.h"
27 #include "mozilla/Attributes.h"
28
29 #include "nsWildCard.h"
30
31 class nsIDOMDataTransfer;
32
33 #define NS_FILECOMPLETE_CID { 0xcb60980e, 0x18a5, 0x4a77, \
34 { 0x91, 0x10, 0x81, 0x46, 0x61, 0x4c, 0xa7, 0xf0 } }
35 #define NS_FILECOMPLETE_CONTRACTID "@mozilla.org/autocomplete/search;1?name=file"
36
37 class nsFileResult MOZ_FINAL : public nsIAutoCompleteResult
38 {
39 public:
40 // aSearchString is the text typed into the autocomplete widget
41 // aSearchParam is the picker's currently displayed directory
42 nsFileResult(const nsAString& aSearchString, const nsAString& aSearchParam);
43
44 NS_DECL_ISUPPORTS
45 NS_DECL_NSIAUTOCOMPLETERESULT
46
47 nsTArray<nsString> mValues;
48 nsAutoString mSearchString;
49 uint16_t mSearchResult;
50 };
51
52 NS_IMPL_ISUPPORTS(nsFileResult, nsIAutoCompleteResult)
53
54 nsFileResult::nsFileResult(const nsAString& aSearchString,
55 const nsAString& aSearchParam):
56 mSearchString(aSearchString)
57 {
58 if (aSearchString.IsEmpty())
59 mSearchResult = RESULT_IGNORED;
60 else {
61 int32_t slashPos = mSearchString.RFindChar('/');
62 mSearchResult = RESULT_FAILURE;
63 nsCOMPtr<nsIFile> directory;
64 nsDependentSubstring parent(Substring(mSearchString, 0, slashPos + 1));
65 if (!parent.IsEmpty() && parent.First() == '/')
66 NS_NewLocalFile(parent, true, getter_AddRefs(directory));
67 if (!directory) {
68 if (NS_FAILED(NS_NewLocalFile(aSearchParam, true, getter_AddRefs(directory))))
69 return;
70 if (slashPos > 0)
71 directory->AppendRelativePath(Substring(mSearchString, 0, slashPos));
72 }
73 nsCOMPtr<nsISimpleEnumerator> dirEntries;
74 if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(dirEntries))))
75 return;
76 mSearchResult = RESULT_NOMATCH;
77 bool hasMore = false;
78 nsDependentSubstring prefix(Substring(mSearchString, slashPos + 1));
79 while (NS_SUCCEEDED(dirEntries->HasMoreElements(&hasMore)) && hasMore) {
80 nsCOMPtr<nsISupports> nextItem;
81 dirEntries->GetNext(getter_AddRefs(nextItem));
82 nsCOMPtr<nsIFile> nextFile(do_QueryInterface(nextItem));
83 nsAutoString fileName;
84 nextFile->GetLeafName(fileName);
85 if (StringBeginsWith(fileName, prefix)) {
86 fileName.Insert(parent, 0);
87 if (mSearchResult == RESULT_NOMATCH && fileName.Equals(mSearchString))
88 mSearchResult = RESULT_IGNORED;
89 else
90 mSearchResult = RESULT_SUCCESS;
91 bool isDirectory = false;
92 nextFile->IsDirectory(&isDirectory);
93 if (isDirectory)
94 fileName.Append('/');
95 mValues.AppendElement(fileName);
96 }
97 }
98 mValues.Sort();
99 }
100 }
101
102 NS_IMETHODIMP nsFileResult::GetSearchString(nsAString & aSearchString)
103 {
104 aSearchString.Assign(mSearchString);
105 return NS_OK;
106 }
107
108 NS_IMETHODIMP nsFileResult::GetSearchResult(uint16_t *aSearchResult)
109 {
110 NS_ENSURE_ARG_POINTER(aSearchResult);
111 *aSearchResult = mSearchResult;
112 return NS_OK;
113 }
114
115 NS_IMETHODIMP nsFileResult::GetDefaultIndex(int32_t *aDefaultIndex)
116 {
117 NS_ENSURE_ARG_POINTER(aDefaultIndex);
118 *aDefaultIndex = -1;
119 return NS_OK;
120 }
121
122 NS_IMETHODIMP nsFileResult::GetErrorDescription(nsAString & aErrorDescription)
123 {
124 aErrorDescription.Truncate();
125 return NS_OK;
126 }
127
128 NS_IMETHODIMP nsFileResult::GetMatchCount(uint32_t *aMatchCount)
129 {
130 NS_ENSURE_ARG_POINTER(aMatchCount);
131 *aMatchCount = mValues.Length();
132 return NS_OK;
133 }
134
135 NS_IMETHODIMP nsFileResult::GetTypeAheadResult(bool *aTypeAheadResult)
136 {
137 NS_ENSURE_ARG_POINTER(aTypeAheadResult);
138 *aTypeAheadResult = false;
139 return NS_OK;
140 }
141
142 NS_IMETHODIMP nsFileResult::GetValueAt(int32_t index, nsAString & aValue)
143 {
144 aValue = mValues[index];
145 if (aValue.Last() == '/')
146 aValue.Truncate(aValue.Length() - 1);
147 return NS_OK;
148 }
149
150 NS_IMETHODIMP nsFileResult::GetLabelAt(int32_t index, nsAString & aValue)
151 {
152 return GetValueAt(index, aValue);
153 }
154
155 NS_IMETHODIMP nsFileResult::GetCommentAt(int32_t index, nsAString & aComment)
156 {
157 aComment.Truncate();
158 return NS_OK;
159 }
160
161 NS_IMETHODIMP nsFileResult::GetStyleAt(int32_t index, nsAString & aStyle)
162 {
163 if (mValues[index].Last() == '/')
164 aStyle.AssignLiteral("directory");
165 else
166 aStyle.AssignLiteral("file");
167 return NS_OK;
168 }
169
170 NS_IMETHODIMP nsFileResult::GetImageAt(int32_t index, nsAString & aImage)
171 {
172 aImage.Truncate();
173 return NS_OK;
174 }
175 NS_IMETHODIMP nsFileResult::GetFinalCompleteValueAt(int32_t index,
176 nsAString & aValue)
177 {
178 return GetValueAt(index, aValue);
179 }
180
181 NS_IMETHODIMP nsFileResult::RemoveValueAt(int32_t rowIndex, bool removeFromDb)
182 {
183 return NS_OK;
184 }
185
186 class nsFileComplete MOZ_FINAL : public nsIAutoCompleteSearch
187 {
188 public:
189 NS_DECL_ISUPPORTS
190 NS_DECL_NSIAUTOCOMPLETESEARCH
191 };
192
193 NS_IMPL_ISUPPORTS(nsFileComplete, nsIAutoCompleteSearch)
194
195 NS_IMETHODIMP
196 nsFileComplete::StartSearch(const nsAString& aSearchString,
197 const nsAString& aSearchParam,
198 nsIAutoCompleteResult *aPreviousResult,
199 nsIAutoCompleteObserver *aListener)
200 {
201 NS_ENSURE_ARG_POINTER(aListener);
202 nsRefPtr<nsFileResult> result = new nsFileResult(aSearchString, aSearchParam);
203 NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
204 return aListener->OnSearchResult(this, result);
205 }
206
207 NS_IMETHODIMP
208 nsFileComplete::StopSearch()
209 {
210 return NS_OK;
211 }
212
213 #define NS_FILEVIEW_CID { 0xa5570462, 0x1dd1, 0x11b2, \
214 { 0x9d, 0x19, 0xdf, 0x30, 0xa2, 0x7f, 0xbd, 0xc4 } }
215
216 class nsFileView : public nsIFileView,
217 public nsITreeView
218 {
219 public:
220 nsFileView();
221 nsresult Init();
222
223 NS_DECL_ISUPPORTS
224 NS_DECL_NSIFILEVIEW
225 NS_DECL_NSITREEVIEW
226
227 protected:
228 virtual ~nsFileView();
229
230 void FilterFiles();
231 void ReverseArray(nsTArray<nsCOMPtr<nsIFile> >& aArray);
232 void SortArray(nsTArray<nsCOMPtr<nsIFile> >& aArray);
233 void SortInternal();
234
235 nsTArray<nsCOMPtr<nsIFile> > mFileList;
236 nsTArray<nsCOMPtr<nsIFile> > mDirList;
237 nsTArray<nsCOMPtr<nsIFile> > mFilteredFiles;
238
239 nsCOMPtr<nsIFile> mDirectoryPath;
240 nsCOMPtr<nsITreeBoxObject> mTree;
241 nsCOMPtr<nsITreeSelection> mSelection;
242 nsCOMPtr<nsIDateTimeFormat> mDateFormatter;
243
244 int16_t mSortType;
245 int32_t mTotalRows;
246
247 nsTArray<char16_t*> mCurrentFilters;
248
249 bool mShowHiddenFiles;
250 bool mDirectoryFilter;
251 bool mReverseSort;
252 };
253
254 // Factory constructor
255 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFileComplete)
256 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFileView, Init)
257 NS_DEFINE_NAMED_CID(NS_FILECOMPLETE_CID);
258 NS_DEFINE_NAMED_CID(NS_FILEVIEW_CID);
259
260 static const mozilla::Module::CIDEntry kFileViewCIDs[] = {
261 { &kNS_FILECOMPLETE_CID, false, nullptr, nsFileCompleteConstructor },
262 { &kNS_FILEVIEW_CID, false, nullptr, nsFileViewConstructor },
263 { nullptr }
264 };
265
266 static const mozilla::Module::ContractIDEntry kFileViewContracts[] = {
267 { NS_FILECOMPLETE_CONTRACTID, &kNS_FILECOMPLETE_CID },
268 { NS_FILEVIEW_CONTRACTID, &kNS_FILEVIEW_CID },
269 { nullptr }
270 };
271
272 static const mozilla::Module kFileViewModule = {
273 mozilla::Module::kVersion,
274 kFileViewCIDs,
275 kFileViewContracts
276 };
277
278 NSMODULE_DEFN(nsFileViewModule) = &kFileViewModule;
279
280 nsFileView::nsFileView() :
281 mSortType(-1),
282 mTotalRows(0),
283 mShowHiddenFiles(false),
284 mDirectoryFilter(false),
285 mReverseSort(false)
286 {
287 }
288
289 nsFileView::~nsFileView()
290 {
291 uint32_t count = mCurrentFilters.Length();
292 for (uint32_t i = 0; i < count; ++i)
293 NS_Free(mCurrentFilters[i]);
294 }
295
296 nsresult
297 nsFileView::Init()
298 {
299 mDateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID);
300 if (!mDateFormatter)
301 return NS_ERROR_OUT_OF_MEMORY;
302
303 return NS_OK;
304 }
305
306 // nsISupports implementation
307
308 NS_IMPL_ISUPPORTS(nsFileView, nsITreeView, nsIFileView)
309
310 // nsIFileView implementation
311
312 NS_IMETHODIMP
313 nsFileView::SetShowHiddenFiles(bool aShowHidden)
314 {
315 if (aShowHidden != mShowHiddenFiles) {
316 mShowHiddenFiles = aShowHidden;
317
318 // This could be better optimized, but since the hidden
319 // file functionality is not currently used, this will be fine.
320 SetDirectory(mDirectoryPath);
321 }
322
323 return NS_OK;
324 }
325
326 NS_IMETHODIMP
327 nsFileView::GetShowHiddenFiles(bool* aShowHidden)
328 {
329 *aShowHidden = mShowHiddenFiles;
330 return NS_OK;
331 }
332
333 NS_IMETHODIMP
334 nsFileView::SetShowOnlyDirectories(bool aOnlyDirs)
335 {
336 if (aOnlyDirs == mDirectoryFilter)
337 return NS_OK;
338
339 mDirectoryFilter = aOnlyDirs;
340 uint32_t dirCount = mDirList.Length();
341 if (mDirectoryFilter) {
342 int32_t rowDiff = mTotalRows - dirCount;
343
344 mFilteredFiles.Clear();
345 mTotalRows = dirCount;
346 if (mTree)
347 mTree->RowCountChanged(mTotalRows, -rowDiff);
348 } else {
349 // Run the filter again to get the file list back
350 FilterFiles();
351
352 SortArray(mFilteredFiles);
353 if (mReverseSort)
354 ReverseArray(mFilteredFiles);
355
356 if (mTree)
357 mTree->RowCountChanged(dirCount, mTotalRows - dirCount);
358 }
359
360 return NS_OK;
361 }
362
363 NS_IMETHODIMP
364 nsFileView::GetShowOnlyDirectories(bool* aOnlyDirs)
365 {
366 *aOnlyDirs = mDirectoryFilter;
367 return NS_OK;
368 }
369
370 NS_IMETHODIMP
371 nsFileView::GetSortType(int16_t* aSortType)
372 {
373 *aSortType = mSortType;
374 return NS_OK;
375 }
376
377 NS_IMETHODIMP
378 nsFileView::GetReverseSort(bool* aReverseSort)
379 {
380 *aReverseSort = mReverseSort;
381 return NS_OK;
382 }
383
384 NS_IMETHODIMP
385 nsFileView::Sort(int16_t aSortType, bool aReverseSort)
386 {
387 if (aSortType == mSortType) {
388 if (aReverseSort == mReverseSort)
389 return NS_OK;
390
391 mReverseSort = aReverseSort;
392 ReverseArray(mDirList);
393 ReverseArray(mFilteredFiles);
394 } else {
395 mSortType = aSortType;
396 mReverseSort = aReverseSort;
397 SortInternal();
398 }
399
400 if (mTree)
401 mTree->Invalidate();
402
403 return NS_OK;
404 }
405
406 NS_IMETHODIMP
407 nsFileView::SetDirectory(nsIFile* aDirectory)
408 {
409 NS_ENSURE_ARG_POINTER(aDirectory);
410
411 nsCOMPtr<nsISimpleEnumerator> dirEntries;
412 aDirectory->GetDirectoryEntries(getter_AddRefs(dirEntries));
413
414 if (!dirEntries) {
415 // Couldn't read in the directory, this can happen if the user does not
416 // have permission to list it.
417 return NS_ERROR_FAILURE;
418 }
419
420 mDirectoryPath = aDirectory;
421 mFileList.Clear();
422 mDirList.Clear();
423
424 bool hasMore = false;
425
426 while (NS_SUCCEEDED(dirEntries->HasMoreElements(&hasMore)) && hasMore) {
427 nsCOMPtr<nsISupports> nextItem;
428 dirEntries->GetNext(getter_AddRefs(nextItem));
429 nsCOMPtr<nsIFile> theFile = do_QueryInterface(nextItem);
430
431 bool isDirectory = false;
432 if (theFile) {
433 theFile->IsDirectory(&isDirectory);
434
435 if (isDirectory) {
436 bool isHidden;
437 theFile->IsHidden(&isHidden);
438 if (mShowHiddenFiles || !isHidden) {
439 mDirList.AppendElement(theFile);
440 }
441 }
442 else {
443 mFileList.AppendElement(theFile);
444 }
445 }
446 }
447
448 if (mTree) {
449 mTree->BeginUpdateBatch();
450 mTree->RowCountChanged(0, -mTotalRows);
451 }
452
453 FilterFiles();
454 SortInternal();
455
456 if (mTree) {
457 mTree->EndUpdateBatch();
458 mTree->ScrollToRow(0);
459 }
460
461 return NS_OK;
462 }
463
464 NS_IMETHODIMP
465 nsFileView::SetFilter(const nsAString& aFilterString)
466 {
467 uint32_t filterCount = mCurrentFilters.Length();
468 for (uint32_t i = 0; i < filterCount; ++i)
469 NS_Free(mCurrentFilters[i]);
470 mCurrentFilters.Clear();
471
472 nsAString::const_iterator start, iter, end;
473 aFilterString.BeginReading(iter);
474 aFilterString.EndReading(end);
475
476 while (true) {
477 // skip over delimiters
478 while (iter != end && (*iter == ';' || *iter == ' '))
479 ++iter;
480
481 if (iter == end)
482 break;
483
484 start = iter; // start of a filter
485
486 // we know this is neither ';' nor ' ', skip to next char
487 ++iter;
488
489 // find next delimiter or end of string
490 while (iter != end && (*iter != ';' && *iter != ' '))
491 ++iter;
492
493 char16_t* filter = ToNewUnicode(Substring(start, iter));
494 if (!filter)
495 return NS_ERROR_OUT_OF_MEMORY;
496
497 if (!mCurrentFilters.AppendElement(filter)) {
498 NS_Free(filter);
499 return NS_ERROR_OUT_OF_MEMORY;
500 }
501
502 if (iter == end)
503 break;
504
505 ++iter; // we know this is either ';' or ' ', skip to next char
506 }
507
508 if (mTree) {
509 mTree->BeginUpdateBatch();
510 uint32_t count = mDirList.Length();
511 mTree->RowCountChanged(count, count - mTotalRows);
512 }
513
514 mFilteredFiles.Clear();
515
516 FilterFiles();
517
518 SortArray(mFilteredFiles);
519 if (mReverseSort)
520 ReverseArray(mFilteredFiles);
521
522 if (mTree)
523 mTree->EndUpdateBatch();
524
525 return NS_OK;
526 }
527
528 NS_IMETHODIMP
529 nsFileView::GetSelectedFiles(nsIArray** aFiles)
530 {
531 *aFiles = nullptr;
532 if (!mSelection)
533 return NS_OK;
534
535 int32_t numRanges;
536 mSelection->GetRangeCount(&numRanges);
537
538 uint32_t dirCount = mDirList.Length();
539 nsCOMPtr<nsIMutableArray> fileArray =
540 do_CreateInstance(NS_ARRAY_CONTRACTID);
541 NS_ENSURE_STATE(fileArray);
542
543 for (int32_t range = 0; range < numRanges; ++range) {
544 int32_t rangeBegin, rangeEnd;
545 mSelection->GetRangeAt(range, &rangeBegin, &rangeEnd);
546
547 for (int32_t itemIndex = rangeBegin; itemIndex <= rangeEnd; ++itemIndex) {
548 nsIFile* curFile = nullptr;
549
550 if (itemIndex < (int32_t) dirCount)
551 curFile = mDirList[itemIndex];
552 else {
553 if (itemIndex < mTotalRows)
554 curFile = mFilteredFiles[itemIndex - dirCount];
555 }
556
557 if (curFile)
558 fileArray->AppendElement(curFile, false);
559 }
560 }
561
562 NS_ADDREF(*aFiles = fileArray);
563 return NS_OK;
564 }
565
566
567 // nsITreeView implementation
568
569 NS_IMETHODIMP
570 nsFileView::GetRowCount(int32_t* aRowCount)
571 {
572 *aRowCount = mTotalRows;
573 return NS_OK;
574 }
575
576 NS_IMETHODIMP
577 nsFileView::GetSelection(nsITreeSelection** aSelection)
578 {
579 *aSelection = mSelection;
580 NS_IF_ADDREF(*aSelection);
581 return NS_OK;
582 }
583
584 NS_IMETHODIMP
585 nsFileView::SetSelection(nsITreeSelection* aSelection)
586 {
587 mSelection = aSelection;
588 return NS_OK;
589 }
590
591 NS_IMETHODIMP
592 nsFileView::GetRowProperties(int32_t aIndex, nsAString& aProps)
593 {
594 return NS_OK;
595 }
596
597 NS_IMETHODIMP
598 nsFileView::GetCellProperties(int32_t aRow, nsITreeColumn* aCol,
599 nsAString& aProps)
600 {
601 uint32_t dirCount = mDirList.Length();
602
603 if (aRow < (int32_t) dirCount)
604 aProps.AppendLiteral("directory");
605 else if (aRow < mTotalRows)
606 aProps.AppendLiteral("file");
607
608 return NS_OK;
609 }
610
611 NS_IMETHODIMP
612 nsFileView::GetColumnProperties(nsITreeColumn* aCol, nsAString& aProps)
613 {
614 return NS_OK;
615 }
616
617 NS_IMETHODIMP
618 nsFileView::IsContainer(int32_t aIndex, bool* aIsContainer)
619 {
620 *aIsContainer = false;
621 return NS_OK;
622 }
623
624 NS_IMETHODIMP
625 nsFileView::IsContainerOpen(int32_t aIndex, bool* aIsOpen)
626 {
627 *aIsOpen = false;
628 return NS_OK;
629 }
630
631 NS_IMETHODIMP
632 nsFileView::IsContainerEmpty(int32_t aIndex, bool* aIsEmpty)
633 {
634 *aIsEmpty = false;
635 return NS_OK;
636 }
637
638 NS_IMETHODIMP
639 nsFileView::IsSeparator(int32_t aIndex, bool* aIsSeparator)
640 {
641 *aIsSeparator = false;
642 return NS_OK;
643 }
644
645 NS_IMETHODIMP
646 nsFileView::IsSorted(bool* aIsSorted)
647 {
648 *aIsSorted = (mSortType >= 0);
649 return NS_OK;
650 }
651
652 NS_IMETHODIMP
653 nsFileView::CanDrop(int32_t aIndex, int32_t aOrientation,
654 nsIDOMDataTransfer* dataTransfer, bool* aCanDrop)
655 {
656 *aCanDrop = false;
657 return NS_OK;
658 }
659
660 NS_IMETHODIMP
661 nsFileView::Drop(int32_t aRow, int32_t aOrientation, nsIDOMDataTransfer* dataTransfer)
662 {
663 return NS_OK;
664 }
665
666 NS_IMETHODIMP
667 nsFileView::GetParentIndex(int32_t aRowIndex, int32_t* aParentIndex)
668 {
669 *aParentIndex = -1;
670 return NS_OK;
671 }
672
673 NS_IMETHODIMP
674 nsFileView::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex,
675 bool* aHasSibling)
676 {
677 *aHasSibling = (aAfterIndex < (mTotalRows - 1));
678 return NS_OK;
679 }
680
681 NS_IMETHODIMP
682 nsFileView::GetLevel(int32_t aIndex, int32_t* aLevel)
683 {
684 *aLevel = 0;
685 return NS_OK;
686 }
687
688 NS_IMETHODIMP
689 nsFileView::GetImageSrc(int32_t aRow, nsITreeColumn* aCol,
690 nsAString& aImageSrc)
691 {
692 return NS_OK;
693 }
694
695 NS_IMETHODIMP
696 nsFileView::GetProgressMode(int32_t aRow, nsITreeColumn* aCol,
697 int32_t* aProgressMode)
698 {
699 return NS_OK;
700 }
701
702 NS_IMETHODIMP
703 nsFileView::GetCellValue(int32_t aRow, nsITreeColumn* aCol,
704 nsAString& aCellValue)
705 {
706 return NS_OK;
707 }
708
709 NS_IMETHODIMP
710 nsFileView::GetCellText(int32_t aRow, nsITreeColumn* aCol,
711 nsAString& aCellText)
712 {
713 uint32_t dirCount = mDirList.Length();
714 bool isDirectory;
715 nsIFile* curFile = nullptr;
716
717 if (aRow < (int32_t) dirCount) {
718 isDirectory = true;
719 curFile = mDirList[aRow];
720 } else if (aRow < mTotalRows) {
721 isDirectory = false;
722 curFile = mFilteredFiles[aRow - dirCount];
723 } else {
724 // invalid row
725 aCellText.SetCapacity(0);
726 return NS_OK;
727 }
728
729 const char16_t* colID;
730 aCol->GetIdConst(&colID);
731 if (NS_LITERAL_STRING("FilenameColumn").Equals(colID)) {
732 curFile->GetLeafName(aCellText);
733 } else if (NS_LITERAL_STRING("LastModifiedColumn").Equals(colID)) {
734 PRTime lastModTime;
735 curFile->GetLastModifiedTime(&lastModTime);
736 // XXX FormatPRTime could take an nsAString&
737 nsAutoString temp;
738 mDateFormatter->FormatPRTime(nullptr, kDateFormatShort, kTimeFormatSeconds,
739 lastModTime * 1000, temp);
740 aCellText = temp;
741 } else {
742 // file size
743 if (isDirectory)
744 aCellText.SetCapacity(0);
745 else {
746 int64_t fileSize;
747 curFile->GetFileSize(&fileSize);
748 CopyUTF8toUTF16(nsPrintfCString("%lld", fileSize), aCellText);
749 }
750 }
751
752 return NS_OK;
753 }
754
755 NS_IMETHODIMP
756 nsFileView::SetTree(nsITreeBoxObject* aTree)
757 {
758 mTree = aTree;
759 return NS_OK;
760 }
761
762 NS_IMETHODIMP
763 nsFileView::ToggleOpenState(int32_t aIndex)
764 {
765 return NS_OK;
766 }
767
768 NS_IMETHODIMP
769 nsFileView::CycleHeader(nsITreeColumn* aCol)
770 {
771 return NS_OK;
772 }
773
774 NS_IMETHODIMP
775 nsFileView::SelectionChanged()
776 {
777 return NS_OK;
778 }
779
780 NS_IMETHODIMP
781 nsFileView::CycleCell(int32_t aRow, nsITreeColumn* aCol)
782 {
783 return NS_OK;
784 }
785
786 NS_IMETHODIMP
787 nsFileView::IsEditable(int32_t aRow, nsITreeColumn* aCol,
788 bool* aIsEditable)
789 {
790 *aIsEditable = false;
791 return NS_OK;
792 }
793
794 NS_IMETHODIMP
795 nsFileView::IsSelectable(int32_t aRow, nsITreeColumn* aCol,
796 bool* aIsSelectable)
797 {
798 *aIsSelectable = false;
799 return NS_OK;
800 }
801
802 NS_IMETHODIMP
803 nsFileView::SetCellValue(int32_t aRow, nsITreeColumn* aCol,
804 const nsAString& aValue)
805 {
806 return NS_OK;
807 }
808
809 NS_IMETHODIMP
810 nsFileView::SetCellText(int32_t aRow, nsITreeColumn* aCol,
811 const nsAString& aValue)
812 {
813 return NS_OK;
814 }
815
816 NS_IMETHODIMP
817 nsFileView::PerformAction(const char16_t* aAction)
818 {
819 return NS_OK;
820 }
821
822 NS_IMETHODIMP
823 nsFileView::PerformActionOnRow(const char16_t* aAction, int32_t aRow)
824 {
825 return NS_OK;
826 }
827
828 NS_IMETHODIMP
829 nsFileView::PerformActionOnCell(const char16_t* aAction, int32_t aRow,
830 nsITreeColumn* aCol)
831 {
832 return NS_OK;
833 }
834
835 // Private methods
836
837 void
838 nsFileView::FilterFiles()
839 {
840 uint32_t count = mDirList.Length();
841 mTotalRows = count;
842 count = mFileList.Length();
843 mFilteredFiles.Clear();
844 uint32_t filterCount = mCurrentFilters.Length();
845
846 for (uint32_t i = 0; i < count; ++i) {
847 nsIFile* file = mFileList[i];
848 bool isHidden = false;
849 if (!mShowHiddenFiles)
850 file->IsHidden(&isHidden);
851
852 nsAutoString ucsLeafName;
853 if(NS_FAILED(file->GetLeafName(ucsLeafName))) {
854 // need to check return value for GetLeafName()
855 continue;
856 }
857
858 if (!isHidden) {
859 for (uint32_t j = 0; j < filterCount; ++j) {
860 bool matched = false;
861 if (!nsCRT::strcmp(mCurrentFilters.ElementAt(j),
862 MOZ_UTF16("..apps")))
863 {
864 file->IsExecutable(&matched);
865 } else
866 matched = (NS_WildCardMatch(ucsLeafName.get(),
867 mCurrentFilters.ElementAt(j),
868 true) == MATCH);
869
870 if (matched) {
871 mFilteredFiles.AppendElement(file);
872 ++mTotalRows;
873 break;
874 }
875 }
876 }
877 }
878 }
879
880 void
881 nsFileView::ReverseArray(nsTArray<nsCOMPtr<nsIFile> >& aArray)
882 {
883 uint32_t count = aArray.Length();
884 for (uint32_t i = 0; i < count/2; ++i) {
885 // If we get references to the COMPtrs in the array, and then .swap() them
886 // we avoid AdRef() / Release() calls.
887 nsCOMPtr<nsIFile>& element = aArray[i];
888 nsCOMPtr<nsIFile>& element2 = aArray[count - i - 1];
889 element.swap(element2);
890 }
891 }
892
893 static int
894 SortNameCallback(const void* aElement1, const void* aElement2, void* aContext)
895 {
896 nsIFile* file1 = *static_cast<nsIFile* const *>(aElement1);
897 nsIFile* file2 = *static_cast<nsIFile* const *>(aElement2);
898
899 nsAutoString leafName1, leafName2;
900 file1->GetLeafName(leafName1);
901 file2->GetLeafName(leafName2);
902
903 return Compare(leafName1, leafName2);
904 }
905
906 static int
907 SortSizeCallback(const void* aElement1, const void* aElement2, void* aContext)
908 {
909 nsIFile* file1 = *static_cast<nsIFile* const *>(aElement1);
910 nsIFile* file2 = *static_cast<nsIFile* const *>(aElement2);
911
912 int64_t size1, size2;
913 file1->GetFileSize(&size1);
914 file2->GetFileSize(&size2);
915
916 if (size1 == size2)
917 return 0;
918
919 return size1 < size2 ? -1 : 1;
920 }
921
922 static int
923 SortDateCallback(const void* aElement1, const void* aElement2, void* aContext)
924 {
925 nsIFile* file1 = *static_cast<nsIFile* const *>(aElement1);
926 nsIFile* file2 = *static_cast<nsIFile* const *>(aElement2);
927
928 PRTime time1, time2;
929 file1->GetLastModifiedTime(&time1);
930 file2->GetLastModifiedTime(&time2);
931
932 if (time1 == time2)
933 return 0;
934
935 return time1 < time2 ? -1 : 1;
936 }
937
938 void
939 nsFileView::SortArray(nsTArray<nsCOMPtr<nsIFile> >& aArray)
940 {
941 // We assume the array to be in filesystem order, which
942 // for our purposes, is completely unordered.
943
944 int (*compareFunc)(const void*, const void*, void*);
945
946 switch (mSortType) {
947 case sortName:
948 compareFunc = SortNameCallback;
949 break;
950 case sortSize:
951 compareFunc = SortSizeCallback;
952 break;
953 case sortDate:
954 compareFunc = SortDateCallback;
955 break;
956 default:
957 return;
958 }
959
960 uint32_t count = aArray.Length();
961
962 nsIFile** array = new nsIFile*[count];
963 for (uint32_t i = 0; i < count; ++i) {
964 array[i] = aArray[i];
965 }
966
967 NS_QuickSort(array, count, sizeof(nsIFile*), compareFunc, nullptr);
968
969 for (uint32_t i = 0; i < count; ++i) {
970 // Use swap() to avoid refcounting.
971 aArray[i].swap(array[i]);
972 }
973
974 delete[] array;
975 }
976
977 void
978 nsFileView::SortInternal()
979 {
980 SortArray(mDirList);
981 SortArray(mFilteredFiles);
982
983 if (mReverseSort) {
984 ReverseArray(mDirList);
985 ReverseArray(mFilteredFiles);
986 }
987 }

mercurial