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