|
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 } |