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