Back to index

lightning-sunbird  0.9+nobinonly
nsFileView.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Brian Ryner <bryner@brianryner.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsIFileView.h"
00040 #include "nsITreeView.h"
00041 #include "nsIGenericFactory.h"
00042 #include "nsITreeSelection.h"
00043 #include "nsITreeColumns.h"
00044 #include "nsITreeBoxObject.h"
00045 #include "nsILocalFile.h"
00046 #include "nsString.h"
00047 #include "nsReadableUtils.h"
00048 #include "nsCRT.h"
00049 #include "prmem.h"
00050 #include "nsPrintfCString.h"
00051 #include "nsVoidArray.h"
00052 #include "nsIDateTimeFormat.h"
00053 #include "nsDateTimeFormatCID.h"
00054 #include "nsQuickSort.h"
00055 #include "nsIAtom.h"
00056 #include "nsIAutoCompleteResult.h"
00057 #include "nsIAutoCompleteSearch.h"
00058 #include "nsISimpleEnumerator.h"
00059 #include "nsAutoPtr.h"
00060 #include "nsArray.h"
00061 
00062 #include "nsWildCard.h"
00063 
00064 #define NS_FILECOMPLETE_CID { 0xcb60980e, 0x18a5, 0x4a77, \
00065                             { 0x91, 0x10, 0x81, 0x46, 0x61, 0x4c, 0xa7, 0xf0 } }
00066 #define NS_FILECOMPLETE_CONTRACTID "@mozilla.org/autocomplete/search;1?name=file"
00067 
00068 class nsFileResult : public nsIAutoCompleteResult
00069 {
00070 public:
00071   // aSearchString is the text typed into the autocomplete widget
00072   // aSearchParam is the picker's currently displayed directory
00073   nsFileResult(const nsAString& aSearchString, const nsAString& aSearchParam);
00074 
00075   NS_DECL_ISUPPORTS
00076   NS_DECL_NSIAUTOCOMPLETERESULT
00077 
00078   nsStringArray mValues;
00079   nsAutoString mSearchString;
00080   PRInt32 mSlashPos;
00081   PRUint16 mSearchResult;
00082 };
00083 
00084 NS_IMPL_ISUPPORTS1(nsFileResult, nsIAutoCompleteResult)
00085 
00086 nsFileResult::nsFileResult(const nsAString& aSearchString,
00087                            const nsAString& aSearchParam):
00088   mSearchString(aSearchString),
00089   mSlashPos(mSearchString.RFindChar('/'))
00090 {
00091   if (aSearchString.IsEmpty())
00092     mSearchResult = RESULT_IGNORED;
00093   else {
00094     mSearchResult = RESULT_FAILURE;
00095     nsCOMPtr<nsILocalFile> directory;
00096     nsDependentSubstring parent(Substring(mSearchString, 0, mSlashPos + 1));
00097     if (mSlashPos != kNotFound)
00098       NS_NewLocalFile(parent, PR_TRUE, getter_AddRefs(directory));
00099     if (!directory) {
00100       if (NS_FAILED(NS_NewLocalFile(aSearchParam, PR_TRUE, getter_AddRefs(directory))))
00101         return;
00102       if (mSlashPos > 0)
00103         directory->AppendRelativePath(Substring(mSearchString, 0, mSlashPos));
00104     }
00105     nsCOMPtr<nsISimpleEnumerator> dirEntries;
00106     if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(dirEntries))))
00107       return;
00108     mSearchResult = RESULT_NOMATCH;
00109     PRBool hasMore = PR_FALSE;
00110     nsDependentSubstring prefix(Substring(mSearchString, mSlashPos + 1));
00111     while (NS_SUCCEEDED(dirEntries->HasMoreElements(&hasMore)) && hasMore) {
00112       nsCOMPtr<nsISupports> nextItem;
00113       dirEntries->GetNext(getter_AddRefs(nextItem));
00114       nsCOMPtr<nsILocalFile> nextFile(do_QueryInterface(nextItem));
00115       nsAutoString fileName;
00116       nextFile->GetLeafName(fileName);
00117       if (StringBeginsWith(fileName, prefix)) {
00118         fileName.Insert(parent, 0);
00119         mValues.AppendString(fileName);
00120         if (mSearchResult == RESULT_NOMATCH && fileName.Equals(mSearchString))
00121           mSearchResult = RESULT_IGNORED;
00122         else
00123           mSearchResult = RESULT_SUCCESS;
00124       }
00125     }
00126     mValues.Sort();
00127   }
00128 }
00129 
00130 NS_IMETHODIMP nsFileResult::GetSearchString(nsAString & aSearchString)
00131 {
00132   aSearchString.Assign(mSearchString);
00133   return NS_OK;
00134 }
00135 
00136 NS_IMETHODIMP nsFileResult::GetSearchResult(PRUint16 *aSearchResult)
00137 {
00138   NS_ENSURE_ARG_POINTER(aSearchResult);
00139   *aSearchResult = mSearchResult;
00140   return NS_OK;
00141 }
00142 
00143 NS_IMETHODIMP nsFileResult::GetDefaultIndex(PRInt32 *aDefaultIndex)
00144 {
00145   NS_ENSURE_ARG_POINTER(aDefaultIndex);
00146   *aDefaultIndex = -1;
00147   return NS_OK;
00148 }
00149 
00150 NS_IMETHODIMP nsFileResult::GetErrorDescription(nsAString & aErrorDescription)
00151 {
00152   aErrorDescription.Truncate();
00153   return NS_OK;
00154 }
00155 
00156 NS_IMETHODIMP nsFileResult::GetMatchCount(PRUint32 *aMatchCount)
00157 {
00158   NS_ENSURE_ARG_POINTER(aMatchCount);
00159   *aMatchCount = mValues.Count();
00160   return NS_OK;
00161 }
00162 
00163 NS_IMETHODIMP nsFileResult::GetValueAt(PRInt32 index, nsAString & aValue)
00164 {
00165   mValues.StringAt(index, aValue);
00166   return NS_OK;
00167 }
00168 
00169 NS_IMETHODIMP nsFileResult::GetCommentAt(PRInt32 index, nsAString & aComment)
00170 {
00171   aComment.Truncate();
00172   return NS_OK;
00173 }
00174 
00175 NS_IMETHODIMP nsFileResult::GetStyleAt(PRInt32 index, nsAString & aStyle)
00176 {
00177   aStyle.Truncate();
00178   return NS_OK;
00179 }
00180 
00181 NS_IMETHODIMP nsFileResult::RemoveValueAt(PRInt32 rowIndex, PRBool removeFromDb)
00182 {
00183   return NS_OK;
00184 }
00185 
00186 class nsFileComplete : public nsIAutoCompleteSearch
00187 {
00188 public:
00189   NS_DECL_ISUPPORTS
00190   NS_DECL_NSIAUTOCOMPLETESEARCH
00191 };
00192 
00193 NS_IMPL_ISUPPORTS1(nsFileComplete, nsIAutoCompleteSearch)
00194 
00195 NS_IMETHODIMP
00196 nsFileComplete::StartSearch(const nsAString& aSearchString,
00197                             const nsAString& aSearchParam,
00198                             nsIAutoCompleteResult *aPreviousResult,
00199                             nsIAutoCompleteObserver *aListener)
00200 {
00201   NS_ENSURE_ARG_POINTER(aListener);
00202   nsRefPtr<nsFileResult> result = new nsFileResult(aSearchString, aSearchParam);
00203   NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
00204   return aListener->OnSearchResult(this, result);
00205 }
00206 
00207 NS_IMETHODIMP
00208 nsFileComplete::StopSearch()
00209 {
00210   return NS_OK;
00211 }
00212 
00213 #define NS_FILEVIEW_CID { 0xa5570462, 0x1dd1, 0x11b2, \
00214                          { 0x9d, 0x19, 0xdf, 0x30, 0xa2, 0x7f, 0xbd, 0xc4 } }
00215 
00216 static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID);
00217 
00218 class nsFileView : public nsIFileView,
00219                    public nsITreeView
00220 {
00221 public:
00222   nsFileView();
00223   nsresult Init();
00224 
00225   NS_DECL_ISUPPORTS
00226   NS_DECL_NSIFILEVIEW
00227   NS_DECL_NSITREEVIEW
00228   
00229 protected:
00230   virtual ~nsFileView();
00231   
00232   void FilterFiles();
00233   void ReverseArray(nsISupportsArray* aArray);
00234   void SortArray(nsISupportsArray* aArray);
00235   void SortInternal();
00236 
00237   nsCOMPtr<nsISupportsArray> mFileList;
00238   nsCOMPtr<nsISupportsArray> mDirList;
00239   nsCOMPtr<nsISupportsArray> mFilteredFiles;
00240 
00241   nsCOMPtr<nsIFile> mDirectoryPath;
00242   nsCOMPtr<nsITreeBoxObject> mTree;
00243   nsCOMPtr<nsITreeSelection> mSelection;
00244   nsCOMPtr<nsIAtom> mDirectoryAtom;
00245   nsCOMPtr<nsIAtom> mFileAtom;
00246   nsCOMPtr<nsIDateTimeFormat> mDateFormatter;
00247 
00248   PRInt16 mSortType;
00249   PRInt32 mTotalRows;
00250 
00251   nsVoidArray mCurrentFilters;
00252 
00253   PRPackedBool mShowHiddenFiles;
00254   PRPackedBool mDirectoryFilter;
00255   PRPackedBool mReverseSort;
00256 };
00257 
00258 // Factory constructor
00259 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFileComplete)
00260 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFileView, Init)
00261 
00262 static const nsModuleComponentInfo components[] =
00263 {
00264   { "nsFileComplete", NS_FILECOMPLETE_CID,
00265     NS_FILECOMPLETE_CONTRACTID, nsFileCompleteConstructor },
00266   { "nsFileView", NS_FILEVIEW_CID,
00267     NS_FILEVIEW_CONTRACTID, nsFileViewConstructor }
00268 };
00269 
00270 NS_IMPL_NSGETMODULE(nsFileViewModule, components)
00271 
00272 
00273 nsFileView::nsFileView() :
00274   mSortType(-1),
00275   mTotalRows(0),
00276   mShowHiddenFiles(PR_FALSE),
00277   mDirectoryFilter(PR_FALSE),
00278   mReverseSort(PR_FALSE)
00279 {
00280 }
00281 
00282 nsFileView::~nsFileView()
00283 {
00284   PRInt32 count = mCurrentFilters.Count();
00285   for (PRInt32 i = 0; i < count; ++i)
00286     PR_Free(mCurrentFilters[i]);
00287 }
00288 
00289 nsresult
00290 nsFileView::Init()
00291 {
00292   mDirectoryAtom = do_GetAtom("directory");
00293   mFileAtom = do_GetAtom("file");
00294   NS_NewISupportsArray(getter_AddRefs(mFileList));
00295   NS_NewISupportsArray(getter_AddRefs(mDirList));
00296   NS_NewISupportsArray(getter_AddRefs(mFilteredFiles));
00297   mDateFormatter = do_CreateInstance(kDateTimeFormatCID);
00298 
00299   return NS_OK;
00300 }
00301 
00302 // nsISupports implementation
00303 
00304 NS_IMPL_ISUPPORTS2(nsFileView, nsITreeView, nsIFileView)
00305 
00306 // nsIFileView implementation
00307 
00308 NS_IMETHODIMP
00309 nsFileView::SetShowHiddenFiles(PRBool aShowHidden)
00310 {
00311   if (aShowHidden != mShowHiddenFiles) {
00312     mShowHiddenFiles = aShowHidden;
00313 
00314     // This could be better optimized, but since the hidden
00315     // file functionality is not currently used, this will be fine.
00316     SetDirectory(mDirectoryPath);
00317   }
00318     
00319   return NS_OK;
00320 }
00321 
00322 NS_IMETHODIMP
00323 nsFileView::GetShowHiddenFiles(PRBool* aShowHidden)
00324 {
00325   *aShowHidden = mShowHiddenFiles;
00326   return NS_OK;
00327 }
00328 
00329 NS_IMETHODIMP
00330 nsFileView::SetShowOnlyDirectories(PRBool aOnlyDirs)
00331 {
00332   if (aOnlyDirs == mDirectoryFilter)
00333     return NS_OK;
00334 
00335   mDirectoryFilter = aOnlyDirs;
00336   PRUint32 dirCount;
00337   mDirList->Count(&dirCount);
00338   if (mDirectoryFilter) {
00339     PRInt32 rowDiff = mTotalRows - dirCount;
00340 
00341     mFilteredFiles->Clear();
00342     mTotalRows = dirCount;
00343     if (mTree)
00344       mTree->RowCountChanged(mTotalRows, -rowDiff);
00345   } else {
00346     // Run the filter again to get the file list back
00347     FilterFiles();
00348 
00349     SortArray(mFilteredFiles);
00350     if (mReverseSort)
00351       ReverseArray(mFilteredFiles);
00352 
00353     if (mTree)
00354       mTree->RowCountChanged(dirCount, mTotalRows - dirCount);
00355   }
00356 
00357   return NS_OK;
00358 }
00359 
00360 NS_IMETHODIMP
00361 nsFileView::GetShowOnlyDirectories(PRBool* aOnlyDirs)
00362 {
00363   *aOnlyDirs = mDirectoryFilter;
00364   return NS_OK;
00365 }
00366 
00367 NS_IMETHODIMP
00368 nsFileView::GetSortType(PRInt16* aSortType)
00369 {
00370   *aSortType = mSortType;
00371   return NS_OK;
00372 }
00373 
00374 NS_IMETHODIMP
00375 nsFileView::GetReverseSort(PRBool* aReverseSort)
00376 {
00377   *aReverseSort = mReverseSort;
00378   return NS_OK;
00379 }
00380 
00381 NS_IMETHODIMP
00382 nsFileView::Sort(PRInt16 aSortType, PRBool aReverseSort)
00383 {
00384   if (aSortType == mSortType) {
00385     if (aReverseSort != mReverseSort) {
00386       mReverseSort = aReverseSort;
00387       ReverseArray(mDirList);
00388       ReverseArray(mFilteredFiles);
00389     } else
00390       return NS_OK;
00391   } else {
00392     mSortType = aSortType;
00393     mReverseSort = aReverseSort;
00394     SortInternal();
00395   }
00396 
00397   if (mTree)
00398     mTree->Invalidate();
00399 
00400   return NS_OK;
00401 }
00402 
00403 NS_IMETHODIMP
00404 nsFileView::SetDirectory(nsIFile* aDirectory)
00405 {
00406   nsCOMPtr<nsISimpleEnumerator> dirEntries;
00407   aDirectory->GetDirectoryEntries(getter_AddRefs(dirEntries));
00408 
00409   if (!dirEntries) {
00410     // Couldn't read in the directory, this can happen if the user does not
00411     // have permission to list it.
00412     return NS_ERROR_FAILURE;
00413   }
00414 
00415   mDirectoryPath = aDirectory;
00416   mFileList->Clear();
00417   mDirList->Clear();
00418 
00419   PRBool hasMore = PR_FALSE;
00420 
00421   while (NS_SUCCEEDED(dirEntries->HasMoreElements(&hasMore)) && hasMore) {
00422     nsCOMPtr<nsISupports> nextItem;
00423     dirEntries->GetNext(getter_AddRefs(nextItem));
00424     nsCOMPtr<nsIFile> theFile = do_QueryInterface(nextItem);
00425 
00426     PRBool isDirectory;
00427     theFile->IsDirectory(&isDirectory);
00428 
00429     if (isDirectory) {
00430       PRBool isHidden;
00431       theFile->IsHidden(&isHidden);
00432       if (mShowHiddenFiles || !isHidden) {
00433         mDirList->AppendElement(theFile);
00434       }
00435     }
00436     else {
00437       mFileList->AppendElement(theFile);
00438     }
00439   }
00440 
00441   if (mTree) {
00442     mTree->BeginUpdateBatch();
00443     mTree->RowCountChanged(0, -mTotalRows);
00444   }
00445 
00446   FilterFiles();
00447   SortInternal();
00448 
00449   if (mTree) {
00450     mTree->EndUpdateBatch();
00451     mTree->ScrollToRow(0);
00452   }
00453 
00454   return NS_OK;
00455 }
00456 
00457 NS_IMETHODIMP
00458 nsFileView::SetFilter(const PRUnichar* aFilterString)
00459 {
00460   PRInt32 filterCount = mCurrentFilters.Count();
00461   for (PRInt32 i = 0; i < filterCount; ++i)
00462     PR_Free(mCurrentFilters[i]);
00463   mCurrentFilters.Clear();
00464   
00465   const PRUnichar* chr, *aPos = aFilterString;
00466   for (chr = aFilterString; *chr; ++chr) {
00467     if (*chr == ';') {
00468       PRUnichar* aNewString = nsCRT::strndup(aPos, (chr - aPos));
00469       mCurrentFilters.AppendElement(aNewString);
00470 
00471       // ; will be followed by a space, and then the next filter
00472       chr += 2;
00473       aPos = chr;
00474     }
00475   }
00476 
00477   if ((aPos < chr) && *aPos) {
00478     PRUnichar* aNewString = nsCRT::strndup(aPos, (chr - aPos));
00479     mCurrentFilters.AppendElement(aNewString);
00480   }
00481 
00482   if (mTree) {
00483     mTree->BeginUpdateBatch();
00484     PRUint32 count;
00485     mDirList->Count(&count);
00486     mTree->RowCountChanged(count, count - mTotalRows);
00487   }
00488 
00489   mFilteredFiles->Clear();
00490 
00491   FilterFiles();
00492 
00493   SortArray(mFilteredFiles);
00494   if (mReverseSort)
00495     ReverseArray(mFilteredFiles);
00496 
00497   if (mTree)
00498     mTree->EndUpdateBatch();
00499 
00500   return NS_OK;
00501 }
00502 
00503 NS_IMETHODIMP
00504 nsFileView::GetSelectedFiles(nsIArray** aFiles)
00505 {
00506   *aFiles = nsnull;
00507 
00508   PRInt32 numRanges;
00509   mSelection->GetRangeCount(&numRanges);
00510 
00511   PRUint32 dirCount;
00512   mDirList->Count(&dirCount);
00513 
00514   nsCOMArray<nsIFile> fileArray;
00515 
00516   for (PRInt32 range = 0; range < numRanges; ++range) {
00517     PRInt32 rangeBegin, rangeEnd;
00518     mSelection->GetRangeAt(range, &rangeBegin, &rangeEnd);
00519 
00520     for (PRInt32 itemIndex = rangeBegin; itemIndex <= rangeEnd; ++itemIndex) {
00521       nsCOMPtr<nsIFile> curFile;
00522 
00523       if (itemIndex < (PRInt32) dirCount)
00524         curFile = do_QueryElementAt(mDirList, itemIndex);
00525       else {
00526         if (itemIndex < mTotalRows)
00527           curFile = do_QueryElementAt(mFilteredFiles, itemIndex - dirCount);
00528       }
00529 
00530       if (curFile)
00531         fileArray.AppendObject(curFile);
00532     }
00533   }
00534 
00535   nsIMutableArray* outArray;
00536   NS_NewArray(&outArray, fileArray);  // addrefs, return the reference
00537   *aFiles = outArray;
00538   return NS_OK;
00539 }
00540 
00541 
00542 // nsITreeView implementation
00543 
00544 NS_IMETHODIMP
00545 nsFileView::GetRowCount(PRInt32* aRowCount)
00546 {
00547   *aRowCount = mTotalRows;
00548   return NS_OK;
00549 }
00550 
00551 NS_IMETHODIMP
00552 nsFileView::GetSelection(nsITreeSelection** aSelection)
00553 {
00554   *aSelection = mSelection;
00555   NS_IF_ADDREF(*aSelection);
00556   return NS_OK;
00557 }
00558 
00559 NS_IMETHODIMP
00560 nsFileView::SetSelection(nsITreeSelection* aSelection)
00561 {
00562   mSelection = aSelection;
00563   return NS_OK;
00564 }
00565 
00566 NS_IMETHODIMP
00567 nsFileView::GetRowProperties(PRInt32 aIndex,
00568                              nsISupportsArray* aProperties)
00569 {
00570   return NS_OK;
00571 }
00572 
00573 NS_IMETHODIMP
00574 nsFileView::GetCellProperties(PRInt32 aRow, nsITreeColumn* aCol,
00575                               nsISupportsArray* aProperties)
00576 {
00577   PRUint32 dirCount;
00578   mDirList->Count(&dirCount);
00579 
00580   if (aRow < (PRInt32) dirCount)
00581     aProperties->AppendElement(mDirectoryAtom);
00582   else if (aRow < mTotalRows)
00583     aProperties->AppendElement(mFileAtom);
00584 
00585   return NS_OK;
00586 }
00587 
00588 NS_IMETHODIMP
00589 nsFileView::GetColumnProperties(nsITreeColumn* aCol,
00590                                 nsISupportsArray* aProperties)
00591 {
00592   return NS_OK;
00593 }
00594 
00595 NS_IMETHODIMP
00596 nsFileView::IsContainer(PRInt32 aIndex, PRBool* aIsContainer)
00597 {
00598   *aIsContainer = PR_FALSE;
00599   return NS_OK;
00600 }
00601 
00602 NS_IMETHODIMP
00603 nsFileView::IsContainerOpen(PRInt32 aIndex, PRBool* aIsOpen)
00604 {
00605   *aIsOpen = PR_FALSE;
00606   return NS_OK;
00607 }
00608 
00609 NS_IMETHODIMP
00610 nsFileView::IsContainerEmpty(PRInt32 aIndex, PRBool* aIsEmpty)
00611 {
00612   *aIsEmpty = PR_FALSE;
00613   return NS_OK;
00614 }
00615 
00616 NS_IMETHODIMP
00617 nsFileView::IsSeparator(PRInt32 aIndex, PRBool* aIsSeparator)
00618 {
00619   *aIsSeparator = PR_FALSE;
00620   return NS_OK;
00621 }
00622 
00623 NS_IMETHODIMP
00624 nsFileView::IsSorted(PRBool* aIsSorted)
00625 {
00626   *aIsSorted = (mSortType >= 0);
00627   return NS_OK;
00628 }
00629 
00630 NS_IMETHODIMP
00631 nsFileView::CanDrop(PRInt32 aIndex, PRInt32 aOrientation, PRBool* aCanDrop)
00632 {
00633   *aCanDrop = PR_FALSE;
00634   return NS_OK;
00635 }
00636 
00637 NS_IMETHODIMP
00638 nsFileView::Drop(PRInt32 aRow, PRInt32 aOrientation)
00639 {
00640   return NS_OK;
00641 }
00642 
00643 NS_IMETHODIMP
00644 nsFileView::GetParentIndex(PRInt32 aRowIndex, PRInt32* aParentIndex)
00645 {
00646   *aParentIndex = -1;
00647   return NS_OK;
00648 }
00649 
00650 NS_IMETHODIMP
00651 nsFileView::HasNextSibling(PRInt32 aRowIndex, PRInt32 aAfterIndex, 
00652                            PRBool* aHasSibling)
00653 {
00654   *aHasSibling = (aAfterIndex < (mTotalRows - 1));
00655   return NS_OK;
00656 }
00657 
00658 NS_IMETHODIMP
00659 nsFileView::GetLevel(PRInt32 aIndex, PRInt32* aLevel)
00660 {
00661   *aLevel = 0;
00662   return NS_OK;
00663 }
00664 
00665 NS_IMETHODIMP
00666 nsFileView::GetImageSrc(PRInt32 aRow, nsITreeColumn* aCol,
00667                         nsAString& aImageSrc)
00668 {
00669   return NS_OK;
00670 }
00671 
00672 NS_IMETHODIMP
00673 nsFileView::GetProgressMode(PRInt32 aRow, nsITreeColumn* aCol,
00674                             PRInt32* aProgressMode)
00675 {
00676   return NS_OK;
00677 }
00678 
00679 NS_IMETHODIMP
00680 nsFileView::GetCellValue(PRInt32 aRow, nsITreeColumn* aCol,
00681                          nsAString& aCellValue)
00682 {
00683   return NS_OK;
00684 }
00685 
00686 NS_IMETHODIMP
00687 nsFileView::GetCellText(PRInt32 aRow, nsITreeColumn* aCol,
00688                         nsAString& aCellText)
00689 {
00690   PRUint32 dirCount, fileCount;
00691   mDirList->Count(&dirCount);
00692   mFilteredFiles->Count(&fileCount);
00693 
00694   PRBool isDirectory;
00695   nsCOMPtr<nsIFile> curFile;
00696 
00697   if (aRow < (PRInt32) dirCount) {
00698     isDirectory = PR_TRUE;
00699     curFile = do_QueryElementAt(mDirList, aRow);
00700   } else if (aRow < mTotalRows) {
00701     isDirectory = PR_FALSE;
00702     curFile = do_QueryElementAt(mFilteredFiles, aRow - dirCount);
00703   } else {
00704     // invalid row
00705     aCellText.SetCapacity(0);
00706     return NS_OK;
00707   }
00708 
00709   const PRUnichar* colID;
00710   aCol->GetIdConst(&colID);
00711   if (NS_LITERAL_STRING("FilenameColumn").Equals(colID)) {
00712     curFile->GetLeafName(aCellText);
00713   } else if (NS_LITERAL_STRING("LastModifiedColumn").Equals(colID)) {
00714     PRInt64 lastModTime;
00715     curFile->GetLastModifiedTime(&lastModTime);
00716     // XXX FormatPRTime could take an nsAString&
00717     nsAutoString temp;
00718     mDateFormatter->FormatPRTime(nsnull, kDateFormatShort, kTimeFormatSeconds,
00719                                  lastModTime * 1000, temp);
00720     aCellText = temp;
00721   } else {
00722     // file size
00723     if (isDirectory)
00724       aCellText.SetCapacity(0);
00725     else {
00726       PRInt64 fileSize;
00727       curFile->GetFileSize(&fileSize);
00728       CopyUTF8toUTF16(nsPrintfCString("%lld", fileSize), aCellText);
00729     }
00730   }
00731 
00732   return NS_OK;
00733 }
00734 
00735 NS_IMETHODIMP
00736 nsFileView::SetTree(nsITreeBoxObject* aTree)
00737 {
00738   mTree = aTree;
00739   return NS_OK;
00740 }
00741 
00742 NS_IMETHODIMP
00743 nsFileView::ToggleOpenState(PRInt32 aIndex)
00744 {
00745   return NS_OK;
00746 }
00747 
00748 NS_IMETHODIMP
00749 nsFileView::CycleHeader(nsITreeColumn* aCol)
00750 {
00751   return NS_OK;
00752 }
00753 
00754 NS_IMETHODIMP
00755 nsFileView::SelectionChanged()
00756 {
00757   return NS_OK;
00758 }
00759 
00760 NS_IMETHODIMP
00761 nsFileView::CycleCell(PRInt32 aRow, nsITreeColumn* aCol)
00762 {
00763   return NS_OK;
00764 }
00765 
00766 NS_IMETHODIMP
00767 nsFileView::IsEditable(PRInt32 aRow, nsITreeColumn* aCol,
00768                        PRBool* aIsEditable)
00769 {
00770   *aIsEditable = PR_FALSE;
00771   return NS_OK;
00772 }
00773 
00774 NS_IMETHODIMP
00775 nsFileView::SetCellValue(PRInt32 aRow, nsITreeColumn* aCol,
00776                          const nsAString& aValue)
00777 {
00778   return NS_OK;
00779 }
00780 
00781 NS_IMETHODIMP
00782 nsFileView::SetCellText(PRInt32 aRow, nsITreeColumn* aCol,
00783                         const nsAString& aValue)
00784 {
00785   return NS_OK;
00786 }
00787 
00788 NS_IMETHODIMP
00789 nsFileView::PerformAction(const PRUnichar* aAction)
00790 {
00791   return NS_OK;
00792 }
00793 
00794 NS_IMETHODIMP
00795 nsFileView::PerformActionOnRow(const PRUnichar* aAction, PRInt32 aRow)
00796 {
00797   return NS_OK;
00798 }
00799 
00800 NS_IMETHODIMP
00801 nsFileView::PerformActionOnCell(const PRUnichar* aAction, PRInt32 aRow,
00802                                 nsITreeColumn* aCol)
00803 {
00804   return NS_OK;
00805 }
00806 
00807 // Private methods
00808 
00809 void
00810 nsFileView::FilterFiles()
00811 {
00812   PRUint32 count = 0;
00813   mDirList->Count(&count);
00814   mTotalRows = count;
00815   mFileList->Count(&count);
00816   mFilteredFiles->Clear();
00817   PRInt32 filterCount = mCurrentFilters.Count();
00818 
00819   nsCOMPtr<nsIFile> file;
00820   for (PRUint32 i = 0; i < count; ++i) {
00821     file = do_QueryElementAt(mFileList, i);
00822     PRBool isHidden = PR_FALSE;
00823     if (!mShowHiddenFiles)
00824       file->IsHidden(&isHidden);
00825     
00826     nsAutoString ucsLeafName;
00827     if(NS_FAILED(file->GetLeafName(ucsLeafName))) {
00828       // need to check return value for GetLeafName()
00829       continue;
00830     }
00831     
00832     if (!isHidden) {
00833       for (PRInt32 j = 0; j < filterCount; ++j) {
00834         PRBool matched = PR_FALSE;
00835         if (!nsCRT::strcmp((const PRUnichar*) mCurrentFilters.ElementAt(j),
00836                            NS_LITERAL_STRING("..apps").get()))
00837         {
00838           file->IsExecutable(&matched);
00839         } else
00840           matched = (NS_WildCardMatch(ucsLeafName.get(),
00841                                       (const PRUnichar*) mCurrentFilters.ElementAt(j),
00842                                       PR_TRUE) == MATCH);
00843 
00844         if (matched) {
00845           mFilteredFiles->AppendElement(file);
00846           ++mTotalRows;
00847           break;
00848         }
00849       }
00850     }
00851   }
00852 }
00853 
00854 void
00855 nsFileView::ReverseArray(nsISupportsArray* aArray)
00856 {
00857   PRUint32 count;
00858   aArray->Count(&count);
00859   for (PRUint32 i = 0; i < count/2; ++i) {
00860     nsCOMPtr<nsISupports> element = dont_AddRef(aArray->ElementAt(i));
00861     nsCOMPtr<nsISupports> element2 = dont_AddRef(aArray->ElementAt(count-i-1));
00862     aArray->ReplaceElementAt(element2, i);
00863     aArray->ReplaceElementAt(element, count-i-1);
00864   }
00865 }
00866 
00867 PR_STATIC_CALLBACK(int)
00868 SortNameCallback(const void* aElement1, const void* aElement2, void* aContext)
00869 {
00870   nsIFile* file1 = *NS_STATIC_CAST(nsIFile* const *, aElement1);
00871   nsIFile* file2 = *NS_STATIC_CAST(nsIFile* const *, aElement2);
00872   
00873   nsAutoString leafName1, leafName2;
00874   file1->GetLeafName(leafName1);
00875   file2->GetLeafName(leafName2);
00876 
00877   return Compare(leafName1, leafName2);
00878 }
00879 
00880 PR_STATIC_CALLBACK(int)
00881 SortSizeCallback(const void* aElement1, const void* aElement2, void* aContext)
00882 {
00883   nsIFile* file1 = *NS_STATIC_CAST(nsIFile* const *, aElement1);
00884   nsIFile* file2 = *NS_STATIC_CAST(nsIFile* const *, aElement2);
00885 
00886   PRInt64 size1, size2;
00887   file1->GetFileSize(&size1);
00888   file2->GetFileSize(&size2);
00889 
00890   if (LL_EQ(size1, size2))
00891     return 0;
00892 
00893   return (LL_CMP(size1, <, size2) ? -1 : 1);
00894 }
00895 
00896 PR_STATIC_CALLBACK(int)
00897 SortDateCallback(const void* aElement1, const void* aElement2, void* aContext)
00898 {
00899   nsIFile* file1 = *NS_STATIC_CAST(nsIFile* const *, aElement1);
00900   nsIFile* file2 = *NS_STATIC_CAST(nsIFile* const *, aElement2);
00901 
00902   PRInt64 time1, time2;
00903   file1->GetLastModifiedTime(&time1);
00904   file2->GetLastModifiedTime(&time2);
00905 
00906   if (LL_EQ(time1, time2))
00907     return 0;
00908 
00909   return (LL_CMP(time1, <, time2) ? -1 : 1);
00910 }
00911 
00912 void
00913 nsFileView::SortArray(nsISupportsArray* aArray)
00914 {
00915   // We assume the array to be in filesystem order, which
00916   // for our purposes, is completely unordered.
00917 
00918   int (*compareFunc)(const void*, const void*, void*);
00919 
00920   switch (mSortType) {
00921   case sortName:
00922     compareFunc = SortNameCallback;
00923     break;
00924   case sortSize:
00925     compareFunc = SortSizeCallback;
00926     break;
00927   case sortDate:
00928     compareFunc = SortDateCallback;
00929     break;
00930   default:
00931     return;
00932   }
00933 
00934   PRUint32 count;
00935   aArray->Count(&count);
00936 
00937   // each item will have an additional refcount while
00938   // the array is alive.
00939   nsIFile** array = new nsIFile*[count];
00940   PRUint32 i;
00941   for (i = 0; i < count; ++i)
00942     aArray->QueryElementAt(i, NS_GET_IID(nsIFile), (void**)&(array[i]));
00943 
00944   NS_QuickSort(array, count, sizeof(nsIFile*), compareFunc, nsnull);
00945 
00946   for (i = 0; i < count; ++i) {
00947     aArray->ReplaceElementAt(array[i], i);
00948     NS_RELEASE(array[i]);
00949   }
00950 
00951   delete[] array;
00952 }
00953 
00954 void
00955 nsFileView::SortInternal()
00956 {
00957   SortArray(mDirList);
00958   SortArray(mFilteredFiles);
00959 
00960   if (mReverseSort) {
00961     ReverseArray(mDirList);
00962     ReverseArray(mFilteredFiles);
00963   }
00964 }