Back to index

lightning-sunbird  0.9+nobinonly
nsAbView.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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  *   Paul Sandoz <paul.sandoz@sun.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 "nsAbView.h"
00040 #include "nsISupports.h"
00041 #include "nsIRDFService.h"
00042 #include "nsCOMPtr.h"
00043 #include "nsIServiceManager.h"
00044 #include "nsIAbCard.h"
00045 #include "nsILocale.h"
00046 #include "nsILocaleService.h"
00047 #include "prmem.h"
00048 #include "nsCollationCID.h"
00049 #include "nsIAddrBookSession.h"
00050 #include "nsAbBaseCID.h"
00051 #include "nsXPCOM.h"
00052 #include "nsISupportsPrimitives.h"
00053 #include "nsITreeColumns.h"
00054 
00055 #include "nsIPrefService.h"
00056 #include "nsIPrefBranch2.h"
00057 #include "nsIStringBundle.h"
00058 #include "nsIPrefLocalizedString.h"
00059 
00060 #include "nsIAddrDatabase.h" // for kPriEmailColumn
00061 
00062 #include "rdf.h"
00063 
00064 #define CARD_NOT_FOUND -1
00065 #define ALL_ROWS -1
00066 
00067 #define PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST "mail.addr_book.lastnamefirst"
00068 #define PREF_MAIL_ADDR_BOOK_DISPLAYNAME_AUTOGENERATION "mail.addr_book.displayName.autoGeneration"
00069 #define PREF_MAIL_ADDR_BOOK_DISPLAYNAME_LASTNAMEFIRST "mail.addr_book.displayName.lastnamefirst"
00070 
00071 // also, our default primary sort
00072 #define GENERATED_NAME_COLUMN_ID "GeneratedName" 
00073 
00074 static NS_DEFINE_CID(kCollationFactoryCID, NS_COLLATIONFACTORY_CID);
00075 
00076 NS_IMPL_ISUPPORTS4(nsAbView, nsIAbView, nsITreeView, nsIAbListener, nsIObserver)
00077 
00078 nsAbView::nsAbView()
00079 {
00080   mMailListAtom = do_GetAtom("MailList");
00081   mSuppressSelectionChange = PR_FALSE;
00082   mSuppressCountChange = PR_FALSE;
00083   mSearchView = PR_FALSE;
00084   mGeneratedNameFormat = 0;
00085 }
00086 
00087 nsAbView::~nsAbView()
00088 {
00089   if (mDirectory) {
00090     nsresult rv;
00091     rv = Close();
00092     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to close view");
00093   }
00094 }
00095 
00096 NS_IMETHODIMP nsAbView::Close()
00097 {
00098   mURI = "";
00099   mDirectory = nsnull;
00100   mAbViewListener = nsnull;
00101   mTree = nsnull;
00102   mTreeSelection = nsnull;
00103   mSearchView = PR_FALSE;
00104 
00105   nsresult rv;
00106 
00107   rv = RemovePrefObservers();
00108   NS_ENSURE_SUCCESS(rv,rv);
00109   
00110   nsCOMPtr<nsIAddrBookSession> abSession = do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv); 
00111   NS_ENSURE_SUCCESS(rv,rv);
00112 
00113   rv = abSession->RemoveAddressBookListener(this);
00114   NS_ENSURE_SUCCESS(rv,rv);
00115 
00116   PRInt32 i = mCards.Count();
00117   while(i-- > 0)
00118   {
00119     rv = RemoveCardAt(i);
00120     NS_ASSERTION(NS_SUCCEEDED(rv), "remove card failed\n");
00121   }
00122   return NS_OK;
00123 }
00124 
00125 nsresult nsAbView::RemoveCardAt(PRInt32 row)
00126 {
00127   nsresult rv;
00128 
00129   AbCard *abcard = (AbCard*) (mCards.ElementAt(row));
00130   NS_IF_RELEASE(abcard->card);
00131   mCards.RemoveElementAt(row);
00132   PR_FREEIF(abcard->primaryCollationKey);
00133   PR_FREEIF(abcard->secondaryCollationKey);
00134   PR_FREEIF(abcard);
00135 
00136   
00137   // this needs to happen after we remove the card, as RowCountChanged() will call GetRowCount()
00138   if (mTree) {
00139     rv = mTree->RowCountChanged(row, -1);
00140     NS_ENSURE_SUCCESS(rv,rv);
00141   }
00142 
00143   if (mAbViewListener && !mSuppressCountChange) {
00144     rv = mAbViewListener->OnCountChanged(mCards.Count());
00145     NS_ENSURE_SUCCESS(rv,rv);
00146   }
00147   return NS_OK;
00148 }
00149 
00150 NS_IMETHODIMP nsAbView::GetURI(char **aURI)
00151 {
00152   *aURI = ToNewCString(mURI);
00153   return NS_OK;
00154 }
00155 
00156 nsresult nsAbView::SetGeneratedNameFormatFromPrefs()
00157 {
00158   nsresult rv;
00159   nsCOMPtr<nsIPrefBranch2> prefBranchInt(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00160   NS_ENSURE_SUCCESS(rv,rv);
00161 
00162   rv = prefBranchInt->GetIntPref(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST, &mGeneratedNameFormat);
00163   NS_ENSURE_SUCCESS(rv, rv);
00164   return rv;
00165 }
00166 
00167 nsresult nsAbView::AddPrefObservers()
00168 {
00169   nsresult rv;
00170 
00171   nsCOMPtr<nsIPrefBranch2> pbi(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00172   NS_ENSURE_SUCCESS(rv,rv);
00173 
00174   rv = pbi->AddObserver(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST, this, PR_FALSE);
00175   return rv;
00176 }
00177 
00178 nsresult nsAbView::RemovePrefObservers()
00179 {
00180   nsresult rv;
00181 
00182   nsCOMPtr<nsIPrefBranch2> pbi(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00183   NS_ENSURE_SUCCESS(rv,rv);
00184 
00185   rv = pbi->RemoveObserver(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST, this);
00186   NS_ENSURE_SUCCESS(rv,rv);
00187   return rv;
00188 }
00189 
00190 NS_IMETHODIMP nsAbView::Init(const char *aURI, PRBool aSearchView, nsIAbViewListener *abViewListener, 
00191                              const PRUnichar *colID, const PRUnichar *sortDirection, PRUnichar **result)
00192 {
00193   nsresult rv;
00194 
00195   NS_ENSURE_ARG_POINTER(result);
00196 
00197   mURI = aURI;
00198   mAbViewListener = abViewListener;
00199 
00200   // clear out old cards
00201   PRInt32 i = mCards.Count();
00202   while(i-- > 0)
00203   {
00204     rv = RemoveCardAt(i);
00205     NS_ASSERTION(NS_SUCCEEDED(rv), "remove card failed\n");
00206   }
00207   if (!mDirectory || mSearchView != aSearchView)
00208   {
00209     mSearchView = aSearchView;
00210     rv = AddPrefObservers();
00211     NS_ENSURE_SUCCESS(rv,rv);
00212 
00213     rv = SetGeneratedNameFormatFromPrefs();
00214     NS_ENSURE_SUCCESS(rv,rv);
00215 
00216     nsCOMPtr <nsIRDFService> rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1",&rv);
00217     NS_ENSURE_SUCCESS(rv, rv);
00218 
00219     nsCOMPtr <nsIRDFResource> resource;
00220     rv = rdfService->GetResource(nsDependentCString(aURI), getter_AddRefs(resource));
00221     NS_ENSURE_SUCCESS(rv, rv);
00222 
00223     mDirectory = do_QueryInterface(resource, &rv);
00224     NS_ENSURE_SUCCESS(rv, rv);
00225   }
00226   else
00227   {
00228     nsCOMPtr <nsIRDFResource> resource = do_QueryInterface(mDirectory);
00229     rv = resource->Init(aURI);
00230   }
00231   rv = EnumerateCards();
00232   NS_ENSURE_SUCCESS(rv, rv);
00233 
00234   NS_NAMED_LITERAL_STRING(generatedNameColumnId, GENERATED_NAME_COLUMN_ID);
00235 
00236   // see if the persisted sortColumn is valid.
00237   // it may not be, if you migrated from older versions, or switched between
00238   // a mozilla build and a commercial build, which have different columns.
00239   nsAutoString actualSortColumn;
00240   if (!generatedNameColumnId.Equals(colID) && mCards.Count()) {
00241     nsIAbCard *card = ((AbCard *)(mCards.ElementAt(0)))->card;
00242     nsXPIDLString value;
00243     // XXX todo
00244     // need to check if _Generic is valid.  GetCardValue() will always return NS_OK for _Generic
00245     // we're going to have to ask mDirectory if it is.
00246     // it might not be.  example:  _ScreenName is valid in Netscape, but not Mozilla.
00247     rv = GetCardValue(card, colID, getter_Copies(value));
00248     if (NS_FAILED(rv))
00249       actualSortColumn = generatedNameColumnId.get();
00250     else
00251       actualSortColumn = colID; 
00252   }
00253   else {
00254     actualSortColumn = colID; 
00255   }
00256 
00257   rv = SortBy(actualSortColumn.get(), sortDirection);
00258   NS_ENSURE_SUCCESS(rv,rv);
00259 
00260   nsCOMPtr<nsIAddrBookSession> abSession = do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv); 
00261   NS_ENSURE_SUCCESS(rv,rv);
00262 
00263   // this listener cares about all events
00264   rv = abSession->AddAddressBookListener(this, nsIAddrBookSession::all);
00265   NS_ENSURE_SUCCESS(rv,rv);
00266   
00267   if (mAbViewListener && !mSuppressCountChange) {
00268     rv = mAbViewListener->OnCountChanged(mCards.Count());
00269     NS_ENSURE_SUCCESS(rv,rv);
00270   }
00271 
00272   *result = ToNewUnicode(actualSortColumn);
00273   return NS_OK;
00274 }
00275 
00276 NS_IMETHODIMP nsAbView::GetDirectory(nsIAbDirectory **aDirectory)
00277 {
00278   NS_ENSURE_ARG_POINTER(aDirectory);
00279   NS_IF_ADDREF(*aDirectory = mDirectory);
00280   return NS_OK;
00281 }
00282 
00283 nsresult nsAbView::EnumerateCards()
00284 {
00285   nsresult rv;    
00286   nsCOMPtr<nsIEnumerator> cardsEnumerator;
00287   nsCOMPtr<nsIAbCard> card;
00288 
00289   if (!mDirectory)
00290     return NS_ERROR_UNEXPECTED;
00291 
00292   rv = mDirectory->GetChildCards(getter_AddRefs(cardsEnumerator));
00293   if (NS_SUCCEEDED(rv) && cardsEnumerator)
00294   {
00295     nsCOMPtr<nsISupports> item;
00296     for (rv = cardsEnumerator->First(); NS_SUCCEEDED(rv); rv = cardsEnumerator->Next())
00297     {
00298       rv = cardsEnumerator->CurrentItem(getter_AddRefs(item));
00299       if (NS_SUCCEEDED(rv))
00300       {
00301         nsCOMPtr <nsIAbCard> card = do_QueryInterface(item);
00302         // malloc these from an arena
00303         AbCard *abcard = (AbCard *) PR_Calloc(1, sizeof(struct AbCard));
00304         if (!abcard) 
00305           return NS_ERROR_OUT_OF_MEMORY;
00306 
00307         abcard->card = card;
00308         NS_IF_ADDREF(abcard->card);
00309 
00310         // XXX todo
00311         // would it be better to do an insertion sort, than append and sort?
00312         // XXX todo
00313         // if we knew how many cards there was going to be
00314         // we could allocate an array of the size, 
00315         // instead of growing and copying as we append
00316         rv = mCards.AppendElement((void *)abcard);
00317         NS_ASSERTION(NS_SUCCEEDED(rv), "failed to append card");
00318       }
00319     }
00320   }
00321 
00322   return NS_OK;
00323 }
00324 
00325 NS_IMETHODIMP nsAbView::GetRowCount(PRInt32 *aRowCount)
00326 {
00327   *aRowCount = mCards.Count();
00328   return NS_OK;
00329 }
00330 
00331 NS_IMETHODIMP nsAbView::GetSelection(nsITreeSelection * *aSelection)
00332 {
00333   NS_IF_ADDREF(*aSelection = mTreeSelection);
00334   return NS_OK;
00335 }
00336 
00337 NS_IMETHODIMP nsAbView::SetSelection(nsITreeSelection * aSelection)
00338 {
00339   mTreeSelection = aSelection;
00340   return NS_OK;
00341 }
00342 
00343 NS_IMETHODIMP nsAbView::GetRowProperties(PRInt32 index, nsISupportsArray *properties)
00344 {
00345     return NS_OK;
00346 }
00347 
00348 NS_IMETHODIMP nsAbView::GetCellProperties(PRInt32 row, nsITreeColumn* col, nsISupportsArray *properties)
00349 {
00350   NS_ENSURE_TRUE(row >= 0, NS_ERROR_UNEXPECTED);
00351 
00352   if (mCards.Count() <= row)
00353     return NS_OK;
00354 
00355   const PRUnichar* colID;
00356   col->GetIdConst(&colID);
00357   // "G" == "GeneratedName"
00358   if (colID[0] != PRUnichar('G'))
00359     return NS_OK;
00360 
00361   nsIAbCard *card = ((AbCard *)(mCards.ElementAt(row)))->card;
00362 
00363   PRBool isMailList;
00364   nsresult rv = card->GetIsMailList(&isMailList);
00365   NS_ENSURE_SUCCESS(rv,rv);
00366 
00367   if (isMailList) {
00368     rv = properties->AppendElement(mMailListAtom);  
00369     NS_ENSURE_SUCCESS(rv,rv);
00370   }
00371 
00372   return NS_OK;
00373 }
00374 
00375 NS_IMETHODIMP nsAbView::GetColumnProperties(nsITreeColumn* col, nsISupportsArray *properties)
00376 {
00377     return NS_OK;
00378 }
00379 
00380 NS_IMETHODIMP nsAbView::IsContainer(PRInt32 index, PRBool *_retval)
00381 {
00382     *_retval = PR_FALSE;
00383     return NS_OK;
00384 }
00385 
00386 NS_IMETHODIMP nsAbView::IsContainerOpen(PRInt32 index, PRBool *_retval)
00387 {
00388     return NS_ERROR_NOT_IMPLEMENTED;
00389 }
00390 
00391 NS_IMETHODIMP nsAbView::IsContainerEmpty(PRInt32 index, PRBool *_retval)
00392 {
00393     return NS_ERROR_NOT_IMPLEMENTED;
00394 }
00395 
00396 NS_IMETHODIMP nsAbView::IsSeparator(PRInt32 index, PRBool *_retval)
00397 {
00398   *_retval = PR_FALSE;
00399   return NS_OK;
00400 }
00401 
00402 NS_IMETHODIMP nsAbView::IsSorted(PRBool *_retval)
00403 {
00404     return NS_ERROR_NOT_IMPLEMENTED;
00405 }
00406 
00407 NS_IMETHODIMP nsAbView::CanDrop(PRInt32 index, PRInt32 orientation, PRBool *_retval)
00408 {
00409     return NS_ERROR_NOT_IMPLEMENTED;
00410 }
00411 
00412 NS_IMETHODIMP nsAbView::Drop(PRInt32 row, PRInt32 orientation)
00413 {
00414     return NS_ERROR_NOT_IMPLEMENTED;
00415 }
00416 
00417 NS_IMETHODIMP nsAbView::GetParentIndex(PRInt32 rowIndex, PRInt32 *_retval)
00418 {
00419   *_retval = -1;
00420   return NS_OK;
00421 }
00422 
00423 NS_IMETHODIMP nsAbView::HasNextSibling(PRInt32 rowIndex, PRInt32 afterIndex, PRBool *_retval)
00424 {
00425     return NS_ERROR_NOT_IMPLEMENTED;
00426 }
00427 
00428 NS_IMETHODIMP nsAbView::GetLevel(PRInt32 index, PRInt32 *_retval)
00429 {
00430   *_retval = 0;
00431   return NS_OK;
00432 }
00433 
00434 NS_IMETHODIMP nsAbView::GetImageSrc(PRInt32 row, nsITreeColumn* col, nsAString& _retval)
00435 {
00436   return NS_OK;
00437 }
00438 
00439 NS_IMETHODIMP nsAbView::GetProgressMode(PRInt32 row, nsITreeColumn* col, PRInt32* _retval)
00440 {
00441   return NS_OK;
00442 }
00443 
00444 NS_IMETHODIMP nsAbView::GetCellValue(PRInt32 row, nsITreeColumn* col, nsAString& _retval)
00445 {
00446   return NS_OK;
00447 }
00448 
00449 nsresult nsAbView::GetCardValue(nsIAbCard *card, const PRUnichar *colID, PRUnichar **_retval)
00450 {
00451   nsresult rv;
00452 
00453   // "G" == "GeneratedName", "_P" == "_PhoneticName"
00454   // else, standard column (like PrimaryEmail and _AimScreenName)
00455   if ((colID[0] == PRUnichar('G')) ||
00456       (colID[0] == PRUnichar('_') && colID[1] == PRUnichar('P'))) {
00457     // XXX todo 
00458     // cache the ab session?
00459     nsCOMPtr<nsIAddrBookSession> abSession = do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv);
00460     NS_ENSURE_SUCCESS(rv,rv);
00461     
00462     if (colID[0] == PRUnichar('G'))
00463       rv = abSession->GenerateNameFromCard(card, mGeneratedNameFormat, _retval);
00464     else
00465       // use LN/FN order for the phonetic name
00466       rv = abSession->GeneratePhoneticNameFromCard(card, PR_TRUE, _retval);
00467     NS_ENSURE_SUCCESS(rv,rv);
00468   }
00469   else {
00470       rv = card->GetCardValue(NS_LossyConvertUCS2toASCII(colID).get(), _retval);
00471   }
00472   return rv;
00473 }
00474 
00475 nsresult nsAbView::RefreshTree()
00476 {
00477   nsresult rv;
00478 
00479   // the PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST pref affects how the GeneratedName column looks.
00480   // so if the GeneratedName is our primary or secondary sort,
00481   // we need to resort.
00482   // the same applies for kPhoneticNameColumn 
00483   //
00484   // XXX optimize me
00485   // PrimaryEmail is always the secondary sort, unless it is currently the
00486   // primary sort.  So, if PrimaryEmail is the primary sort, 
00487   // GeneratedName might be the secondary sort.
00488   //
00489   // one day, we can get fancy and remember what the secondary sort is.
00490   // we do that, we can fix this code.  at best, it will turn a sort into a invalidate.
00491   // 
00492   // if neither the primary nor the secondary sorts are GeneratedName (or kPhoneticNameColumn), 
00493   // all we have to do is invalidate (to show the new GeneratedNames), 
00494   // but the sort will not change.
00495   if (mSortColumn.EqualsLiteral(GENERATED_NAME_COLUMN_ID) ||
00496       mSortColumn.EqualsLiteral(kPriEmailColumn) ||
00497       mSortColumn.EqualsLiteral(kPhoneticNameColumn)) {
00498     rv = SortBy(mSortColumn.get(), mSortDirection.get());
00499   }
00500   else {
00501     rv = InvalidateTree(ALL_ROWS);
00502   }
00503 
00504   return rv;
00505 }
00506 
00507 NS_IMETHODIMP nsAbView::GetCellText(PRInt32 row, nsITreeColumn* col, nsAString& _retval)
00508 {
00509   NS_ENSURE_TRUE(row >= 0 && row < mCards.Count(), NS_ERROR_UNEXPECTED);
00510 
00511   nsIAbCard *card = ((AbCard *)(mCards.ElementAt(row)))->card;
00512   // XXX fix me by converting GetCardValue to take an nsAString&
00513   const PRUnichar* colID;
00514   col->GetIdConst(&colID);
00515   nsXPIDLString cellText;
00516   nsresult rv = GetCardValue(card, colID, getter_Copies(cellText));
00517   _retval.Assign(cellText);
00518   return rv;
00519 }
00520 
00521 NS_IMETHODIMP nsAbView::SetTree(nsITreeBoxObject *tree)
00522 {
00523   mTree = tree;
00524   return NS_OK;
00525 }
00526 
00527 NS_IMETHODIMP nsAbView::ToggleOpenState(PRInt32 index)
00528 {
00529     return NS_ERROR_NOT_IMPLEMENTED;
00530 }
00531 
00532 NS_IMETHODIMP nsAbView::CycleHeader(nsITreeColumn* col)
00533 {
00534   return NS_OK;
00535 }
00536 
00537 nsresult nsAbView::InvalidateTree(PRInt32 row)
00538 {
00539   if (!mTree)
00540     return NS_OK;
00541   
00542   if (row == ALL_ROWS)
00543     return mTree->Invalidate();
00544   else
00545     return mTree->InvalidateRow(row);
00546 }
00547 
00548 NS_IMETHODIMP nsAbView::SelectionChanged()
00549 {
00550   if (mAbViewListener && !mSuppressSelectionChange) {
00551     nsresult rv = mAbViewListener->OnSelectionChanged();
00552     NS_ENSURE_SUCCESS(rv,rv);
00553   }
00554   return NS_OK;
00555 }
00556 
00557 NS_IMETHODIMP nsAbView::CycleCell(PRInt32 row, nsITreeColumn* col)
00558 {
00559     return NS_ERROR_NOT_IMPLEMENTED;
00560 }
00561 
00562 NS_IMETHODIMP nsAbView::IsEditable(PRInt32 row, nsITreeColumn* col, PRBool* _retval)
00563 {
00564     return NS_ERROR_NOT_IMPLEMENTED;
00565 }
00566 
00567 NS_IMETHODIMP nsAbView::SetCellValue(PRInt32 row, nsITreeColumn* col, const nsAString& value)
00568 {
00569     return NS_ERROR_NOT_IMPLEMENTED;
00570 }
00571 
00572 NS_IMETHODIMP nsAbView::SetCellText(PRInt32 row, nsITreeColumn* col, const nsAString& value)
00573 {
00574     return NS_ERROR_NOT_IMPLEMENTED;
00575 }
00576 
00577 NS_IMETHODIMP nsAbView::PerformAction(const PRUnichar *action)
00578 {
00579     return NS_ERROR_NOT_IMPLEMENTED;
00580 }
00581 
00582 NS_IMETHODIMP nsAbView::PerformActionOnRow(const PRUnichar *action, PRInt32 row)
00583 {
00584     return NS_ERROR_NOT_IMPLEMENTED;
00585 }
00586 
00587 NS_IMETHODIMP nsAbView::PerformActionOnCell(const PRUnichar *action, PRInt32 row, nsITreeColumn* col)
00588 {
00589     return NS_ERROR_NOT_IMPLEMENTED;
00590 }
00591 
00592 NS_IMETHODIMP nsAbView::GetCardFromRow(PRInt32 row, nsIAbCard **aCard)
00593 {
00594   *aCard = nsnull;  
00595   if (mCards.Count() <= row) {
00596     return NS_OK;
00597   }
00598 
00599   NS_ENSURE_TRUE(row >= 0, NS_ERROR_UNEXPECTED);
00600 
00601   AbCard *a = ((AbCard *)(mCards.ElementAt(row)));
00602   if (!a)
00603       return NS_OK;
00604 
00605   NS_IF_ADDREF(*aCard = a->card);
00606   return NS_OK;
00607 }
00608 
00609 #define DESCENDING_SORT_FACTOR -1
00610 #define ASCENDING_SORT_FACTOR 1
00611 
00612 typedef struct SortClosure
00613 {
00614   const PRUnichar *colID;
00615   PRInt32 factor;
00616   nsAbView *abView;
00617 } SortClosure;
00618 
00619 static int PR_CALLBACK
00620 inplaceSortCallback(const void *data1, const void *data2, void *privateData)
00621 {
00622   AbCard *card1 = (AbCard *)data1;
00623   AbCard *card2 = (AbCard *)data2;
00624   
00625   SortClosure *closure = (SortClosure *) privateData;
00626   
00627   PRInt32 sortValue;
00628   
00629   // if we are sorting the "PrimaryEmail", swap the collation keys, as the secondary is always the 
00630   // PrimaryEmail.  use the last primary key as the secondary key.
00631   //
00632   // "Pr" to distinguish "PrimaryEmail" from "PagerNumber"
00633   if (closure->colID[0] == PRUnichar('P') && closure->colID[1] == PRUnichar('r')) {
00634     sortValue = closure->abView->CompareCollationKeys(card1->secondaryCollationKey,card1->secondaryCollationKeyLen,card2->secondaryCollationKey,card2->secondaryCollationKeyLen);
00635     if (sortValue)
00636       return sortValue * closure->factor;
00637     else
00638       return closure->abView->CompareCollationKeys(card1->primaryCollationKey,card1->primaryCollationKeyLen,card2->primaryCollationKey,card2->primaryCollationKeyLen) * (closure->factor);
00639   }
00640   else {
00641     sortValue = closure->abView->CompareCollationKeys(card1->primaryCollationKey,card1->primaryCollationKeyLen,card2->primaryCollationKey,card2->primaryCollationKeyLen);
00642     if (sortValue)
00643       return sortValue * (closure->factor);
00644     else
00645       return closure->abView->CompareCollationKeys(card1->secondaryCollationKey,card1->secondaryCollationKeyLen,card2->secondaryCollationKey,card2->secondaryCollationKeyLen) * (closure->factor);
00646   }
00647 }
00648 
00649 static void SetSortClosure(const PRUnichar *sortColumn, const PRUnichar *sortDirection, nsAbView *abView, SortClosure *closure)
00650 {
00651   closure->colID = sortColumn;
00652   
00653   if (sortDirection && !nsCRT::strcmp(sortDirection, NS_LITERAL_STRING("descending").get()))
00654     closure->factor = DESCENDING_SORT_FACTOR;
00655   else 
00656     closure->factor = ASCENDING_SORT_FACTOR;
00657 
00658   closure->abView = abView;
00659   return;
00660 }
00661 
00662 NS_IMETHODIMP nsAbView::SortBy(const PRUnichar *colID, const PRUnichar *sortDir)
00663 {
00664   nsresult rv;
00665 
00666   PRInt32 count = mCards.Count();
00667 
00668   nsAutoString sortColumn;
00669   if (!colID) 
00670     sortColumn = NS_LITERAL_STRING(GENERATED_NAME_COLUMN_ID).get();  // default sort
00671   else
00672     sortColumn = colID;
00673 
00674   PRInt32 i;
00675   // this function does not optimize for the case when sortColumn and sortDirection
00676   // are identical since the last call, the caller is responsible optimizing
00677   // for that case
00678 
00679   // if we are sorting by how we are already sorted, 
00680   // and just the sort direction changes, just reverse
00681   //
00682   // note, we'll call SortBy() with the existing sort column and the
00683   // existing sort direction, and that needs to do a complete resort.
00684   // for example, we do that when the PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST changes
00685   if (!nsCRT::strcmp(mSortColumn.get(),sortColumn.get()) && nsCRT::strcmp(mSortDirection.get(), sortDir)) {
00686     PRInt32 halfPoint = count / 2;
00687     for (i=0; i < halfPoint; i++) {
00688       // swap the elements.
00689       void *ptr1 = mCards.ElementAt(i);
00690       void *ptr2 = mCards.ElementAt(count - i - 1);
00691       mCards.ReplaceElementAt(ptr2, i);
00692       mCards.ReplaceElementAt(ptr1, count - i - 1);
00693     }
00694 
00695     mSortDirection = sortDir;
00696   }
00697   else {
00698     // generate collation keys
00699     for (i=0; i < count; i++) {
00700       AbCard *abcard = (AbCard *)(mCards.ElementAt(i));
00701 
00702       rv = GenerateCollationKeysForCard(sortColumn.get(), abcard);
00703       NS_ENSURE_SUCCESS(rv,rv);
00704     }
00705 
00706     nsAutoString sortDirection;
00707     if (!sortDir)
00708       sortDirection = NS_LITERAL_STRING("ascending").get();  // default direction
00709     else
00710       sortDirection = sortDir;
00711 
00712     SortClosure closure;
00713     SetSortClosure(sortColumn.get(), sortDirection.get(), this, &closure);
00714     
00715     nsCOMPtr <nsISupportsArray> selectedCards;
00716     rv = GetSelectedCards(getter_AddRefs(selectedCards));
00717     NS_ENSURE_SUCCESS(rv,rv);
00718 
00719     nsCOMPtr<nsIAbCard> indexCard;
00720 
00721     if (mTreeSelection) {
00722       PRInt32 currentIndex = -1;
00723 
00724       rv = mTreeSelection->GetCurrentIndex(&currentIndex);
00725       NS_ENSURE_SUCCESS(rv,rv);
00726 
00727       if (currentIndex != -1) {
00728         rv = GetCardFromRow(currentIndex, getter_AddRefs(indexCard));
00729         NS_ENSURE_SUCCESS(rv,rv);
00730       }
00731     }
00732 
00733     mCards.Sort(inplaceSortCallback, (void *)(&closure));
00734     
00735     rv = ReselectCards(selectedCards, indexCard);
00736     NS_ENSURE_SUCCESS(rv,rv);
00737 
00738     mSortColumn = sortColumn.get();
00739     mSortDirection = sortDirection.get();
00740   }
00741 
00742   rv = InvalidateTree(ALL_ROWS);
00743   NS_ENSURE_SUCCESS(rv,rv);
00744   return rv;
00745 }
00746 
00747 PRInt32 nsAbView::CompareCollationKeys(PRUint8 *key1, PRUint32 len1, PRUint8 *key2, PRUint32 len2)
00748 {
00749   NS_ASSERTION(mCollationKeyGenerator, "no key generator");
00750   if (!mCollationKeyGenerator)
00751     return 0;
00752 
00753   PRInt32 result;
00754 
00755   nsresult rv = mCollationKeyGenerator->CompareRawSortKey(key1,len1,key2,len2,&result);
00756   NS_ASSERTION(NS_SUCCEEDED(rv), "key compare failed");
00757   if (NS_FAILED(rv))
00758     result = 0;
00759   return result;
00760 }
00761 
00762 nsresult nsAbView::GenerateCollationKeysForCard(const PRUnichar *colID, AbCard *abcard)
00763 {
00764   nsresult rv;
00765   nsXPIDLString value;
00766 
00767   if (!mCollationKeyGenerator)
00768   {
00769     nsCOMPtr<nsILocaleService> localeSvc = do_GetService(NS_LOCALESERVICE_CONTRACTID,&rv); 
00770     NS_ENSURE_SUCCESS(rv, rv);
00771 
00772     nsCOMPtr<nsILocale> locale; 
00773     rv = localeSvc->GetApplicationLocale(getter_AddRefs(locale));
00774     NS_ENSURE_SUCCESS(rv, rv);
00775 
00776     nsCOMPtr <nsICollationFactory> factory = do_CreateInstance(kCollationFactoryCID, &rv); 
00777     NS_ENSURE_SUCCESS(rv, rv);
00778 
00779     rv = factory->CreateCollation(locale, getter_AddRefs(mCollationKeyGenerator));
00780     NS_ENSURE_SUCCESS(rv, rv);
00781   }
00782 
00783   rv = GetCardValue(abcard->card, colID, getter_Copies(value));
00784   NS_ENSURE_SUCCESS(rv,rv);
00785   
00786   PR_FREEIF(abcard->primaryCollationKey);
00787   rv = mCollationKeyGenerator->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive,
00788     value, &(abcard->primaryCollationKey), &(abcard->primaryCollationKeyLen));
00789   NS_ENSURE_SUCCESS(rv,rv);
00790   
00791   // XXX todo
00792   // fix me, do this with a const getter, to avoid the strcpy
00793 
00794   // hardcode email to be our secondary key
00795   rv = GetCardValue(abcard->card, NS_LITERAL_STRING(kPriEmailColumn).get(), getter_Copies(value));
00796   NS_ENSURE_SUCCESS(rv,rv);
00797   
00798   PR_FREEIF(abcard->secondaryCollationKey);
00799   rv = mCollationKeyGenerator->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive,
00800     value, &(abcard->secondaryCollationKey), &(abcard->secondaryCollationKeyLen));
00801   NS_ENSURE_SUCCESS(rv,rv);
00802   return rv;
00803 }
00804 
00805 NS_IMETHODIMP nsAbView::OnItemAdded(nsISupports *parentDir, nsISupports *item)
00806 {
00807   nsresult rv;
00808   nsCOMPtr <nsIAbDirectory> directory = do_QueryInterface(parentDir,&rv);
00809   NS_ENSURE_SUCCESS(rv,rv);
00810 
00811   if (directory.get() == mDirectory.get()) {
00812     nsCOMPtr <nsIAbCard> addedCard = do_QueryInterface(item);
00813     if (addedCard) {
00814       // malloc these from an arena
00815       AbCard *abcard = (AbCard *) PR_Calloc(1, sizeof(struct AbCard));
00816       if (!abcard) 
00817         return NS_ERROR_OUT_OF_MEMORY;
00818 
00819       abcard->card = addedCard;
00820       NS_IF_ADDREF(abcard->card);
00821     
00822       rv = GenerateCollationKeysForCard(mSortColumn.get(), abcard);
00823       NS_ENSURE_SUCCESS(rv,rv);
00824 
00825       PRInt32 index;
00826       rv = AddCard(abcard, PR_FALSE /* select card */, &index);
00827       NS_ENSURE_SUCCESS(rv,rv);
00828     }
00829   }
00830   return rv;
00831 }
00832 
00833 NS_IMETHODIMP nsAbView::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
00834 {
00835   nsresult rv;
00836 
00837   if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
00838     nsDependentString prefName(someData);
00839     
00840     if (prefName.EqualsLiteral(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST)) {
00841       rv = SetGeneratedNameFormatFromPrefs();
00842       NS_ENSURE_SUCCESS(rv,rv);
00843 
00844       rv = RefreshTree();
00845       NS_ENSURE_SUCCESS(rv,rv);
00846     }
00847   }
00848   return NS_OK;
00849 }
00850 
00851 nsresult nsAbView::AddCard(AbCard *abcard, PRBool selectCardAfterAdding, PRInt32 *index)
00852 {
00853   nsresult rv = NS_OK;
00854   NS_ENSURE_ARG_POINTER(abcard);
00855   
00856   *index = FindIndexForInsert(abcard);
00857   rv = mCards.InsertElementAt((void *)abcard, *index);
00858   NS_ENSURE_SUCCESS(rv,rv);
00859     
00860   // this needs to happen after we insert the card, as RowCountChanged() will call GetRowCount()
00861   if (mTree)
00862     rv = mTree->RowCountChanged(*index, 1);
00863 
00864   if (selectCardAfterAdding && mTreeSelection) {
00865     mTreeSelection->SetCurrentIndex(*index);
00866     mTreeSelection->RangedSelect(*index, *index, PR_FALSE /* augment */);
00867   }
00868 
00869   if (mAbViewListener && !mSuppressCountChange) {
00870     rv = mAbViewListener->OnCountChanged(mCards.Count());
00871     NS_ENSURE_SUCCESS(rv,rv);
00872   }
00873 
00874   return rv;
00875 }
00876 
00877 PRInt32 nsAbView::FindIndexForInsert(AbCard *abcard)
00878 {
00879   PRInt32 count = mCards.Count();
00880   PRInt32 i;
00881 
00882   void *item = (void *)abcard;
00883   
00884   SortClosure closure;
00885   SetSortClosure(mSortColumn.get(), mSortDirection.get(), this, &closure);
00886   
00887   // XXX todo
00888   // make this a binary search
00889   for (i=0; i < count; i++) {
00890     void *current = mCards.ElementAt(i);
00891     PRInt32 value = inplaceSortCallback(item, current, (void *)(&closure));
00892     // XXX fix me, this is not right for both ascending and descending
00893     if (value <= 0) 
00894       break;
00895   }
00896   return i;
00897 }
00898 
00899 NS_IMETHODIMP nsAbView::OnItemRemoved(nsISupports *parentDir, nsISupports *item)
00900 {
00901   nsresult rv;
00902 
00903   nsCOMPtr <nsIAbDirectory> directory = do_QueryInterface(parentDir,&rv);
00904   NS_ENSURE_SUCCESS(rv,rv);
00905 
00906   if (directory.get() == mDirectory.get()) {
00907     rv = RemoveCardAndSelectNextCard(item);
00908     NS_ENSURE_SUCCESS(rv,rv);
00909   }
00910   return rv;
00911 }
00912 
00913 nsresult nsAbView::RemoveCardAndSelectNextCard(nsISupports *item)
00914 {
00915   nsresult rv = NS_OK;
00916   nsCOMPtr <nsIAbCard> card = do_QueryInterface(item);
00917   if (card) {
00918     PRInt32 index = FindIndexForCard(card);
00919     if (index != CARD_NOT_FOUND) {
00920       PRBool selectNextCard = PR_FALSE;
00921       if (mTreeSelection) {
00922         PRInt32 selectedIndex;
00923         // XXX todo
00924         // make sure it works if nothing selected
00925         mTreeSelection->GetCurrentIndex(&selectedIndex);
00926         if (index == selectedIndex)
00927           selectNextCard = PR_TRUE;
00928       }
00929 
00930       rv = RemoveCardAt(index);
00931       NS_ENSURE_SUCCESS(rv,rv);
00932 
00933       if (selectNextCard) {
00934       PRInt32 count = mCards.Count();
00935       if (count && mTreeSelection) {
00936         // if we deleted the last card, adjust so we select the new "last" card
00937         if (index >= (count - 1)) {
00938           index = count -1;
00939         }
00940         mTreeSelection->SetCurrentIndex(index);
00941         mTreeSelection->RangedSelect(index, index, PR_FALSE /* augment */);
00942       }
00943     }
00944   }
00945   }
00946   return rv;
00947 }
00948 
00949 PRInt32 nsAbView::FindIndexForCard(nsIAbCard *card)
00950 {
00951   PRInt32 count = mCards.Count();
00952   PRInt32 i;
00953  
00954   // you can't implement the binary search here, as all you have is the nsIAbCard
00955   // you might be here because one of the card properties has changed, and that property
00956   // could be the collation key.
00957   for (i=0; i < count; i++) {
00958     AbCard *abcard = (AbCard*) (mCards.ElementAt(i));
00959     PRBool equals;
00960     nsresult rv = card->Equals(abcard->card, &equals);
00961     if (NS_SUCCEEDED(rv) && equals) {
00962       return i;
00963     }
00964   }
00965   return CARD_NOT_FOUND;
00966 }
00967 
00968 NS_IMETHODIMP nsAbView::OnItemPropertyChanged(nsISupports *item, const char *property, const PRUnichar *oldValue, const PRUnichar *newValue)
00969 {
00970   nsresult rv;
00971 
00972   nsCOMPtr <nsIAbCard> card = do_QueryInterface(item);
00973   if (!card)
00974     return NS_OK;
00975 
00976   PRInt32 index = FindIndexForCard(card);
00977   if (index == -1)
00978     return NS_OK;
00979 
00980   AbCard *oldCard = (AbCard*) (mCards.ElementAt(index));
00981 
00982   // malloc these from an arena
00983   AbCard *newCard = (AbCard *) PR_Calloc(1, sizeof(struct AbCard));
00984   if (!newCard)
00985     return NS_ERROR_OUT_OF_MEMORY;
00986 
00987   newCard->card = card;
00988   NS_IF_ADDREF(newCard->card);
00989     
00990   rv = GenerateCollationKeysForCard(mSortColumn.get(), newCard);
00991   NS_ENSURE_SUCCESS(rv,rv);
00992 
00993   if (!CompareCollationKeys(newCard->primaryCollationKey,newCard->primaryCollationKeyLen,oldCard->primaryCollationKey,oldCard->primaryCollationKeyLen)
00994     && CompareCollationKeys(newCard->secondaryCollationKey,newCard->secondaryCollationKeyLen,oldCard->secondaryCollationKey,oldCard->secondaryCollationKeyLen)) {
00995     // no need to remove and add, since the collation keys haven't change.
00996     // since they haven't chagned, the card will sort to the same place.
00997     // we just need to clean up what we allocated.
00998     NS_IF_RELEASE(newCard->card);
00999     if (newCard->primaryCollationKey)
01000       nsMemory::Free(newCard->primaryCollationKey);
01001     if (newCard->secondaryCollationKey)
01002       nsMemory::Free(newCard->secondaryCollationKey);
01003     PR_FREEIF(newCard);
01004 
01005     // still need to invalidate, as the other columns may have changed
01006     rv = InvalidateTree(index);
01007     NS_ENSURE_SUCCESS(rv,rv);
01008   }
01009   else {
01010     PRBool cardWasSelected = PR_FALSE;
01011 
01012     if (mTreeSelection) {
01013       rv = mTreeSelection->IsSelected(index, &cardWasSelected);
01014       NS_ENSURE_SUCCESS(rv,rv);
01015     }
01016     
01017     mSuppressSelectionChange = PR_TRUE;
01018     mSuppressCountChange = PR_TRUE;
01019 
01020     // remove the old card
01021     rv = RemoveCardAt(index);
01022     NS_ASSERTION(NS_SUCCEEDED(rv), "remove card failed\n");
01023 
01024     // add the card we created, and select it (to restore selection) if it was selected
01025     rv = AddCard(newCard, cardWasSelected /* select card */, &index);
01026     NS_ASSERTION(NS_SUCCEEDED(rv), "add card failed\n");
01027 
01028     mSuppressSelectionChange = PR_FALSE;
01029     mSuppressCountChange = PR_FALSE;
01030 
01031     // ensure restored selection is visible
01032     if (cardWasSelected && mTree) 
01033       mTree->EnsureRowIsVisible(index);
01034   }
01035   return NS_OK;
01036 }
01037 
01038 NS_IMETHODIMP nsAbView::SelectAll()
01039 {
01040   if (mTreeSelection && mTree) {
01041     mTreeSelection->SelectAll();
01042     mTree->Invalidate();
01043   }
01044   return NS_OK;
01045 }
01046 
01047 NS_IMETHODIMP nsAbView::GetSortDirection(nsAString & aDirection)
01048 {
01049   aDirection = mSortDirection;
01050   return NS_OK;
01051 }
01052 
01053 NS_IMETHODIMP nsAbView::GetSortColumn(nsAString & aColumn)
01054 {
01055   aColumn = mSortColumn;
01056   return NS_OK;
01057 }
01058 
01059 nsresult nsAbView::ReselectCards(nsISupportsArray *cards, nsIAbCard *indexCard)
01060 {
01061   PRUint32 count;
01062   PRUint32 i;
01063 
01064   if (!mTreeSelection || !cards)
01065     return NS_OK;
01066 
01067   nsresult rv = mTreeSelection->ClearSelection();
01068   NS_ENSURE_SUCCESS(rv,rv);
01069 
01070   rv = cards->Count(&count);
01071   NS_ENSURE_SUCCESS(rv, rv);
01072 
01073   for (i = 0; i < count; i++) {
01074     nsCOMPtr <nsIAbCard> card = do_QueryElementAt(cards, i);
01075     if (card) {
01076       PRInt32 index = FindIndexForCard(card);
01077       if (index != CARD_NOT_FOUND) {
01078         mTreeSelection->RangedSelect(index, index, PR_TRUE /* augment */);
01079       }
01080     }
01081   }
01082 
01083   // reset the index card, and ensure it is visible
01084   if (indexCard) {
01085     PRInt32 currentIndex = FindIndexForCard(indexCard);
01086     rv = mTreeSelection->SetCurrentIndex(currentIndex);
01087     NS_ENSURE_SUCCESS(rv, rv);
01088   
01089     if (mTree) {
01090       rv = mTree->EnsureRowIsVisible(currentIndex);
01091       NS_ENSURE_SUCCESS(rv, rv);
01092     }
01093   }
01094 
01095   return NS_OK;
01096 }
01097 
01098 NS_IMETHODIMP nsAbView::DeleteSelectedCards()
01099 {
01100   nsCOMPtr <nsISupportsArray> cardsToDelete;
01101   
01102   nsresult rv = GetSelectedCards(getter_AddRefs(cardsToDelete));
01103   NS_ENSURE_SUCCESS(rv,rv);
01104 
01105   // mDirectory should not be null
01106   // bullet proof (and assert) to help figure out bug #127748
01107   NS_ENSURE_TRUE(mDirectory, NS_ERROR_UNEXPECTED);
01108 
01109   rv = mDirectory->DeleteCards(cardsToDelete);
01110   NS_ENSURE_SUCCESS(rv,rv);
01111   return rv;
01112 }
01113 
01114 nsresult nsAbView::GetSelectedCards(nsISupportsArray **selectedCards)
01115 {
01116   *selectedCards = nsnull;
01117   if (!mTreeSelection)
01118     return NS_OK;
01119   
01120   PRInt32 selectionCount; 
01121   nsresult rv = mTreeSelection->GetRangeCount(&selectionCount);
01122   NS_ENSURE_SUCCESS(rv,rv);
01123   
01124   if (!selectionCount)
01125     return NS_OK;
01126   
01127   rv = NS_NewISupportsArray(selectedCards);
01128   NS_ENSURE_SUCCESS(rv,rv);
01129   
01130   for (PRInt32 i = 0; i < selectionCount; i++)
01131   {
01132     PRInt32 startRange;
01133     PRInt32 endRange;
01134     rv = mTreeSelection->GetRangeAt(i, &startRange, &endRange);
01135     NS_ENSURE_SUCCESS(rv, NS_OK); 
01136     PRInt32 totalCards = mCards.Count();
01137     if (startRange >= 0 && startRange < totalCards)
01138     {
01139       for (PRInt32 rangeIndex = startRange; rangeIndex <= endRange && rangeIndex < totalCards; rangeIndex++) {
01140         nsCOMPtr<nsIAbCard> abCard;
01141         rv = GetCardFromRow(rangeIndex, getter_AddRefs(abCard));
01142         NS_ENSURE_SUCCESS(rv,rv);
01143         
01144         nsCOMPtr<nsISupports> supports = do_QueryInterface(abCard, &rv);
01145         NS_ENSURE_SUCCESS(rv,rv);
01146         
01147         rv = (*selectedCards)->AppendElement(supports);
01148         NS_ENSURE_SUCCESS(rv,rv);
01149       }
01150     }
01151   }
01152   return NS_OK;
01153 }
01154 
01155 NS_IMETHODIMP nsAbView::SwapFirstNameLastName()
01156 {
01157   if (!mTreeSelection)
01158     return NS_OK;
01159   
01160   PRInt32 selectionCount; 
01161   nsresult rv = mTreeSelection->GetRangeCount(&selectionCount);
01162   NS_ENSURE_SUCCESS(rv, rv);
01163   
01164   if (!selectionCount)
01165     return NS_OK;
01166   
01167   // prepare for displayname generation
01168   // no cache for pref and bundle since the swap operation is not executed frequently
01169   PRBool displayNameAutoGeneration;
01170   PRBool displayNameLastnamefirst = PR_FALSE;
01171 
01172   nsCOMPtr<nsIPrefBranch2> pPrefBranchInt(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
01173   NS_ENSURE_SUCCESS(rv, rv);
01174 
01175   rv = pPrefBranchInt->GetBoolPref(PREF_MAIL_ADDR_BOOK_DISPLAYNAME_AUTOGENERATION, &displayNameAutoGeneration);
01176   NS_ENSURE_SUCCESS(rv, rv);
01177 
01178   nsCOMPtr<nsIStringBundle> bundle;
01179   if (displayNameAutoGeneration)
01180   {
01181     nsCOMPtr<nsIPrefLocalizedString> pls;
01182     rv = pPrefBranchInt->GetComplexValue(PREF_MAIL_ADDR_BOOK_DISPLAYNAME_LASTNAMEFIRST,
01183                                          NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(pls));
01184     NS_ENSURE_SUCCESS(rv, rv);
01185 
01186     nsXPIDLString str;
01187     pls->ToString(getter_Copies(str));
01188     displayNameLastnamefirst = str.EqualsLiteral("true");
01189     nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
01190     NS_ENSURE_SUCCESS(rv, rv);
01191 
01192     rv = bundleService->CreateBundle("chrome://messenger/locale/addressbook/addressBook.properties", 
01193                                      getter_AddRefs(bundle));
01194     NS_ENSURE_SUCCESS(rv, rv);
01195   }
01196 
01197   for (PRInt32 i = 0; i < selectionCount; i++)
01198   {
01199     PRInt32 startRange;
01200     PRInt32 endRange;
01201     rv = mTreeSelection->GetRangeAt(i, &startRange, &endRange);
01202     NS_ENSURE_SUCCESS(rv, NS_OK); 
01203     PRInt32 totalCards = mCards.Count();
01204     if (startRange >= 0 && startRange < totalCards)
01205     {
01206       for (PRInt32 rangeIndex = startRange; rangeIndex <= endRange && rangeIndex < totalCards; rangeIndex++) {
01207         nsCOMPtr<nsIAbCard> abCard;
01208         rv = GetCardFromRow(rangeIndex, getter_AddRefs(abCard));
01209         NS_ENSURE_SUCCESS(rv, rv);
01210 
01211         // swap FN/LN
01212         nsXPIDLString fn, ln;
01213         abCard->GetFirstName(getter_Copies(fn));
01214         abCard->GetLastName(getter_Copies(ln));
01215         if (!fn.IsEmpty() || !ln.IsEmpty())
01216         {
01217           abCard->SetFirstName(ln);
01218           abCard->SetLastName(fn);
01219 
01220           // generate display name using the new order
01221           if (displayNameAutoGeneration &&
01222               !fn.IsEmpty() && !ln.IsEmpty())
01223           {
01224             nsXPIDLString dnLnFn;
01225             nsXPIDLString dnFnLn;
01226             const PRUnichar *nameString[2];
01227             const PRUnichar *formatString;
01228 
01229             // the format should stays the same before/after we swap the names
01230             formatString = displayNameLastnamefirst ?
01231                               NS_LITERAL_STRING("lastFirstFormat").get() :
01232                               NS_LITERAL_STRING("firstLastFormat").get();
01233 
01234             // generate both ln/fn and fn/ln combination since we need both later
01235             // to check to see if the current display name was edited
01236             // note that fn/ln still hold the values before the swap
01237             nameString[0] = ln.get();
01238             nameString[1] = fn.get();
01239             rv = bundle->FormatStringFromName(formatString,
01240                                               nameString, 2, getter_Copies(dnLnFn));
01241             NS_ENSURE_SUCCESS(rv, rv);
01242             nameString[0] = fn.get();
01243             nameString[1] = ln.get();
01244             rv = bundle->FormatStringFromName(formatString,
01245                                               nameString, 2, getter_Copies(dnFnLn));
01246             NS_ENSURE_SUCCESS(rv, rv);
01247 
01248             // get the current display name
01249             nsXPIDLString dn;
01250             rv = abCard->GetDisplayName(getter_Copies(dn));
01251             NS_ENSURE_SUCCESS(rv, rv);
01252 
01253             // swap the display name if not edited
01254             if (displayNameLastnamefirst)
01255             {
01256               if (dn.Equals(dnLnFn))
01257                 abCard->SetDisplayName(dnFnLn);
01258             }
01259             else
01260             {
01261               if (dn.Equals(dnFnLn))
01262                 abCard->SetDisplayName(dnLnFn);
01263             }
01264           }
01265 
01266           // swap phonetic names
01267           rv = abCard->GetPhoneticFirstName(getter_Copies(fn));
01268           NS_ENSURE_SUCCESS(rv, rv);
01269           rv = abCard->GetPhoneticLastName(getter_Copies(ln));
01270           NS_ENSURE_SUCCESS(rv, rv);
01271           if (!fn.IsEmpty() || !ln.IsEmpty())
01272           {
01273             abCard->SetPhoneticFirstName(ln);
01274             abCard->SetPhoneticLastName(fn);
01275           }
01276         }
01277       }
01278     }
01279   }
01280   // update the tree
01281   // re-sort if either generated or phonetic name is primary or secondary sort,
01282   // otherwise invalidate to reflect the change
01283   rv = RefreshTree();
01284 
01285   return rv;
01286 }
01287 
01288 NS_IMETHODIMP nsAbView::GetSelectedAddresses(nsISupportsArray **_retval)
01289 {
01290   NS_ENSURE_ARG_POINTER(_retval);
01291  
01292   nsCOMPtr<nsISupportsArray> selectedCards;
01293   nsresult rv = GetSelectedCards(getter_AddRefs(selectedCards));
01294   NS_ENSURE_SUCCESS(rv,rv);
01295 
01296   nsCOMPtr<nsISupportsArray> addresses(do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID));
01297   PRUint32 count;
01298   selectedCards->Count(&count);
01299 
01300   for (PRUint32 i = 0; i < count; i++) {
01301     nsCOMPtr<nsISupports> supports;
01302     selectedCards->GetElementAt(i, getter_AddRefs(supports));
01303     nsCOMPtr<nsIAbCard> card = do_QueryInterface(supports, &rv);
01304     NS_ENSURE_SUCCESS(rv, rv);
01305 
01306     PRBool isMailList;
01307     card->GetIsMailList(&isMailList);
01308     nsXPIDLString primaryEmail;
01309     if (isMailList) {
01310       nsCOMPtr<nsIRDFService> rdfService = do_GetService(NS_RDF_CONTRACTID "/rdf-service;1", &rv);
01311       NS_ENSURE_SUCCESS(rv, rv);
01312       nsXPIDLCString mailListURI;
01313       card->GetMailListURI(getter_Copies(mailListURI));
01314       nsCOMPtr<nsIRDFResource> resource;
01315       rv = rdfService->GetResource(mailListURI, getter_AddRefs(resource));
01316       NS_ENSURE_SUCCESS(rv,rv);
01317 
01318       nsCOMPtr<nsIAbDirectory> mailList = do_QueryInterface(resource, &rv);
01319       NS_ENSURE_SUCCESS(rv,rv);
01320 
01321       nsCOMPtr<nsISupportsArray> mailListAddresses;
01322       rv = mailList->GetAddressLists(getter_AddRefs(mailListAddresses));
01323       NS_ENSURE_SUCCESS(rv,rv);
01324 
01325       PRUint32 mailListCount = 0;
01326       mailListAddresses->Count(&mailListCount);  
01327 
01328       for (PRUint32 j = 0; j < mailListCount; j++) {
01329         nsCOMPtr<nsIAbCard> mailListCard = do_QueryElementAt(mailListAddresses, j, &rv);
01330         NS_ENSURE_SUCCESS(rv,rv);
01331 
01332         rv = mailListCard->GetPrimaryEmail(getter_Copies(primaryEmail));
01333         NS_ENSURE_SUCCESS(rv,rv);
01334 
01335         if (!primaryEmail.IsEmpty()) {
01336           nsCOMPtr<nsISupportsString> supportsEmail(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
01337           supportsEmail->SetData(primaryEmail);
01338           addresses->AppendElement(supportsEmail);
01339         }
01340       }
01341     }
01342     else {
01343       rv = card->GetPrimaryEmail(getter_Copies(primaryEmail));
01344       NS_ENSURE_SUCCESS(rv,rv);
01345 
01346       if (!primaryEmail.IsEmpty()) {
01347         nsCOMPtr<nsISupportsString> supportsEmail(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
01348         supportsEmail->SetData(primaryEmail);
01349         addresses->AppendElement(supportsEmail);
01350       }
01351     }    
01352   }
01353 
01354   NS_IF_ADDREF(*_retval = addresses);
01355 
01356   return NS_OK;
01357 }