Back to index

lightning-sunbird  0.9+nobinonly
nsCharsetMenu.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 Communicator client 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) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or 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 "nsIServiceManager.h"
00040 #include "nsIComponentManager.h" 
00041 #include "rdf.h"
00042 #include "nsIRDFDataSource.h"
00043 #include "nsIRDFService.h"
00044 #include "nsIRDFContainerUtils.h"
00045 #include "nsRDFCID.h"
00046 #include "nsXPIDLString.h"
00047 #include "nsCharsetMenu.h"
00048 #include "nsICharsetConverterManager.h"
00049 #include "nsICollation.h"
00050 #include "nsCollationCID.h"
00051 #include "nsLocaleCID.h"
00052 #include "nsILocaleService.h"
00053 #include "nsIPrefService.h"
00054 #include "nsIPrefBranch.h"
00055 #include "nsIPrefBranch2.h"
00056 #include "nsIPrefLocalizedString.h"
00057 #include "nsICurrentCharsetListener.h"
00058 #include "nsQuickSort.h"
00059 #include "nsIObserver.h"
00060 #include "nsStringEnumerator.h"
00061 #include "nsVoidArray.h"
00062 #include "nsIObserverService.h"
00063 #include "nsIRequestObserver.h"
00064 #include "nsITimelineService.h"
00065 #include "nsCRT.h"
00066 #include "prmem.h"
00067 
00068 //----------------------------------------------------------------------------
00069 // Global functions and data [declaration]
00070 
00071 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
00072 static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
00073 static NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID);
00074 static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
00075 static NS_DEFINE_CID(kRDFContainerCID, NS_RDFCONTAINER_CID);
00076 static NS_DEFINE_CID(kCollationFactoryCID, NS_COLLATIONFACTORY_CID);
00077 static NS_DEFINE_CID(kLocaleServiceCID, NS_LOCALESERVICE_CID); 
00078 
00079 static const char kURINC_BrowserAutodetMenuRoot[] = "NC:BrowserAutodetMenuRoot";
00080 static const char kURINC_BrowserCharsetMenuRoot[] = "NC:BrowserCharsetMenuRoot";
00081 static const char kURINC_BrowserMoreCharsetMenuRoot[] = "NC:BrowserMoreCharsetMenuRoot";
00082 static const char kURINC_BrowserMore1CharsetMenuRoot[] = "NC:BrowserMore1CharsetMenuRoot";
00083 static const char kURINC_BrowserMore2CharsetMenuRoot[] = "NC:BrowserMore2CharsetMenuRoot";
00084 static const char kURINC_BrowserMore3CharsetMenuRoot[] = "NC:BrowserMore3CharsetMenuRoot";
00085 static const char kURINC_BrowserMore4CharsetMenuRoot[] = "NC:BrowserMore4CharsetMenuRoot";
00086 static const char kURINC_BrowserMore5CharsetMenuRoot[] = "NC:BrowserMore5CharsetMenuRoot";
00087 static const char kURINC_BrowserUnicodeCharsetMenuRoot[] = "NC:BrowserUnicodeCharsetMenuRoot";
00088 static const char kURINC_MaileditCharsetMenuRoot[] = "NC:MaileditCharsetMenuRoot";
00089 static const char kURINC_MailviewCharsetMenuRoot[] = "NC:MailviewCharsetMenuRoot";
00090 static const char kURINC_ComposerCharsetMenuRoot[] = "NC:ComposerCharsetMenuRoot";
00091 static const char kURINC_DecodersRoot[] = "NC:DecodersRoot";
00092 static const char kURINC_EncodersRoot[] = "NC:EncodersRoot";
00093 DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, Name);
00094 DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, Checked);
00095 DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, BookmarkSeparator);
00096 DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, CharsetDetector);
00097 DEFINE_RDF_VOCAB(RDF_NAMESPACE_URI, NC, type);
00098 
00099 // Note here that browser and mailview have the same static area and cache 
00100 // size but the cache itself is separate.
00101 
00102 #define kBrowserStaticPrefKey       "intl.charsetmenu.browser.static"
00103 #define kBrowserCachePrefKey        "intl.charsetmenu.browser.cache"
00104 #define kBrowserCacheSizePrefKey    "intl.charsetmenu.browser.cache.size"
00105 
00106 #define kMailviewStaticPrefKey      "intl.charsetmenu.browser.static"
00107 #define kMailviewCachePrefKey       "intl.charsetmenu.mailview.cache"
00108 #define kMailviewCacheSizePrefKey   "intl.charsetmenu.browser.cache.size"
00109 
00110 #define kComposerStaticPrefKey      "intl.charsetmenu.browser.static"
00111 #define kComposerCachePrefKey       "intl.charsetmenu.composer.cache"
00112 #define kComposerCacheSizePrefKey   "intl.charsetmenu.browser.cache.size"
00113 
00114 #define kMaileditPrefKey            "intl.charsetmenu.mailedit"
00115 
00116 static void CloneCStringArray(const nsCStringArray& src, nsCStringArray& dest)
00117 {
00118   PRUint32 count = src.Count();
00119   PRUint32 i;
00120   for (i=0; i<count; i++) {
00121     dest.AppendCString(*src.CStringAt(i));
00122   }
00123 }
00124 
00125 //----------------------------------------------------------------------------
00126 // Class nsMenuEntry [declaration]
00127 
00134 class nsMenuEntry
00135 {
00136 public: 
00137   // memory & ref counting & leak prevention stuff
00138   nsMenuEntry() { MOZ_COUNT_CTOR(nsMenuEntry); }
00139   ~nsMenuEntry() { MOZ_COUNT_DTOR(nsMenuEntry); }
00140 
00141   nsCAutoString mCharset;
00142   nsAutoString      mTitle;
00143 };
00144 
00145 MOZ_DECL_CTOR_COUNTER(nsMenuEntry)
00146 
00147 //----------------------------------------------------------------------------
00148 // Class nsCharsetMenu [declaration]
00149 
00150 
00158 class nsCharsetMenu : public nsIRDFDataSource, public nsICurrentCharsetListener
00159 {
00160   NS_DECL_ISUPPORTS
00161 
00162 private:
00163   static nsIRDFResource * kNC_BrowserAutodetMenuRoot;
00164   static nsIRDFResource * kNC_BrowserCharsetMenuRoot;
00165   static nsIRDFResource * kNC_BrowserMoreCharsetMenuRoot;
00166   static nsIRDFResource * kNC_BrowserMore1CharsetMenuRoot;
00167   static nsIRDFResource * kNC_BrowserMore2CharsetMenuRoot;
00168   static nsIRDFResource * kNC_BrowserMore3CharsetMenuRoot;
00169   static nsIRDFResource * kNC_BrowserMore4CharsetMenuRoot;
00170   static nsIRDFResource * kNC_BrowserMore5CharsetMenuRoot;
00171   static nsIRDFResource * kNC_BrowserUnicodeCharsetMenuRoot;
00172   static nsIRDFResource * kNC_MaileditCharsetMenuRoot;
00173   static nsIRDFResource * kNC_MailviewCharsetMenuRoot;
00174   static nsIRDFResource * kNC_ComposerCharsetMenuRoot;
00175   static nsIRDFResource * kNC_DecodersRoot;
00176   static nsIRDFResource * kNC_EncodersRoot;
00177   static nsIRDFResource * kNC_Name;
00178   static nsIRDFResource * kNC_Checked;
00179   static nsIRDFResource * kNC_CharsetDetector;
00180   static nsIRDFResource * kNC_BookmarkSeparator;
00181   static nsIRDFResource * kRDF_type;
00182 
00183   static nsIRDFDataSource * mInner;
00184 
00185   PRPackedBool mInitialized;
00186   PRPackedBool mBrowserMenuInitialized;
00187   PRPackedBool mMailviewMenuInitialized;
00188   PRPackedBool mComposerMenuInitialized;
00189   PRPackedBool mMaileditMenuInitialized;
00190   PRPackedBool mSecondaryTiersInitialized;
00191   PRPackedBool mAutoDetectInitialized;
00192   PRPackedBool mOthersInitialized;
00193 
00194   nsVoidArray   mBrowserMenu;
00195   PRInt32       mBrowserCacheStart;
00196   PRInt32       mBrowserCacheSize;
00197   PRInt32       mBrowserMenuRDFPosition;
00198 
00199   nsVoidArray   mMailviewMenu;
00200   PRInt32       mMailviewCacheStart;
00201   PRInt32       mMailviewCacheSize;
00202   PRInt32       mMailviewMenuRDFPosition;
00203 
00204   nsVoidArray   mComposerMenu;
00205   PRInt32       mComposerCacheStart;
00206   PRInt32       mComposerCacheSize;
00207   PRInt32       mComposerMenuRDFPosition;
00208 
00209   nsCOMPtr<nsIRDFService>               mRDFService;
00210   nsCOMPtr<nsICharsetConverterManager> mCCManager;
00211   nsCOMPtr<nsIPrefBranch>               mPrefs;
00212   nsCOMPtr<nsIObserver>                 mCharsetMenuObserver;
00213   nsCStringArray                        mDecoderList;
00214 
00215   nsresult Done();
00216   nsresult SetCharsetCheckmark(nsString * aCharset, PRBool aValue);
00217 
00218   nsresult FreeResources();
00219 
00220   nsresult InitStaticMenu(nsCStringArray& aDecs, 
00221                           nsIRDFResource * aResource,
00222                           const char * aKey,
00223                           nsVoidArray * aArray);
00224   nsresult InitCacheMenu(nsCStringArray& aDecs,
00225                          nsIRDFResource * aResource,
00226                          const char * aKey,
00227                          nsVoidArray * aArray);
00228   
00229   nsresult InitMoreMenu(nsCStringArray& aDecs,
00230                         nsIRDFResource * aResource, 
00231                         const char * aFlag);
00232   
00233   nsresult InitMoreSubmenus(nsCStringArray& aDecs);
00234 
00235   static nsresult SetArrayFromEnumerator(nsIUTF8StringEnumerator* aEnumerator,
00236                                          nsCStringArray& aArray);
00237   
00238   nsresult AddCharsetToItemArray(nsVoidArray* aArray,
00239                                  const nsAFlatCString& aCharset, 
00240                                  nsMenuEntry ** aResult,
00241                                  PRInt32 aPlace);
00242   nsresult AddCharsetArrayToItemArray(nsVoidArray &aArray, 
00243                                       const nsCStringArray& aCharsets);
00244   nsresult AddMenuItemToContainer(nsIRDFContainer * aContainer, 
00245     nsMenuEntry * aItem, nsIRDFResource * aType, const char * aIDPrefix, 
00246     PRInt32 aPlace);
00247   nsresult AddMenuItemArrayToContainer(nsIRDFContainer * aContainer, 
00248     nsVoidArray * aArray, nsIRDFResource * aType);
00249   nsresult AddCharsetToContainer(nsVoidArray * aArray, 
00250                                  nsIRDFContainer * aContainer,
00251                                  const nsAFlatCString& aCharset,
00252                                  const char * aIDPrefix, 
00253     PRInt32 aPlace, PRInt32 aRDFPlace);
00254 
00255   nsresult AddFromPrefsToMenu(nsVoidArray * aArray, 
00256                               nsIRDFContainer * aContainer,
00257                               const char * aKey,
00258                               nsCStringArray& aDecs,
00259                               const char * aIDPrefix);
00260   nsresult AddFromNolocPrefsToMenu(nsVoidArray * aArray, 
00261                                    nsIRDFContainer * aContainer,
00262                                    const char * aKey,
00263                                    nsCStringArray& aDecs,
00264                                    const char * aIDPrefix);
00265   nsresult AddFromStringToMenu(char * aCharsetList,
00266                                nsVoidArray * aArray,
00267                                nsIRDFContainer * aContainer,
00268                                nsCStringArray& aDecs,
00269                                const char * aIDPrefix);
00270 
00271   nsresult AddSeparatorToContainer(nsIRDFContainer * aContainer);
00272   nsresult AddCharsetToCache(const nsAFlatCString& aCharset,
00273                              nsVoidArray * aArray, 
00274                              nsIRDFResource * aRDFResource,
00275                              PRInt32 aCacheStart, PRInt32 aCacheSize, 
00276                              PRInt32 aRDFPlace);
00277 
00278   nsresult WriteCacheToPrefs(nsVoidArray * aArray, PRInt32 aCacheStart, 
00279     const char * aKey);
00280   nsresult UpdateCachePrefs(const char * aCacheKey, const char * aCacheSizeKey, 
00281     const char * aStaticKey, const PRUnichar * aCharset);
00282 
00283   nsresult ClearMenu(nsIRDFContainer * aContainer, nsVoidArray * aArray);
00284   nsresult RemoveLastMenuItem(nsIRDFContainer * aContainer, 
00285                               nsVoidArray * aArray);
00286 
00287   nsresult RemoveFlaggedCharsets(nsCStringArray& aList, const nsString& aProp);
00288   nsresult NewRDFContainer(nsIRDFDataSource * aDataSource, 
00289     nsIRDFResource * aResource, nsIRDFContainer ** aResult);
00290   void FreeMenuItemArray(nsVoidArray * aArray);
00291   PRInt32 FindMenuItemInArray(const nsVoidArray* aArray,
00292                               const nsAFlatCString& aCharset, 
00293                               nsMenuEntry ** aResult);
00294   nsresult ReorderMenuItemArray(nsVoidArray * aArray);
00295   nsresult GetCollation(nsICollation ** aCollation);
00296 
00297 public:
00298   nsCharsetMenu();
00299   virtual ~nsCharsetMenu();
00300 
00301   nsresult Init();
00302   nsresult InitBrowserMenu();
00303   nsresult InitMaileditMenu();
00304   nsresult InitMailviewMenu();
00305   nsresult InitComposerMenu();
00306   nsresult InitOthers();
00307   nsresult InitSecondaryTiers();
00308   nsresult InitAutodetMenu();
00309   nsresult RefreshBrowserMenu();
00310   nsresult RefreshMailviewMenu();
00311   nsresult RefreshMaileditMenu();
00312   nsresult RefreshComposerMenu();
00313 
00314   //--------------------------------------------------------------------------
00315   // Interface nsICurrentCharsetListener [declaration]
00316 
00317   NS_IMETHOD SetCurrentCharset(const PRUnichar * aCharset);
00318   NS_IMETHOD SetCurrentMailCharset(const PRUnichar * aCharset);
00319   NS_IMETHOD SetCurrentComposerCharset(const PRUnichar * aCharset);
00320 
00321   //--------------------------------------------------------------------------
00322   // Interface nsIRDFDataSource [declaration]
00323 
00324   NS_DECL_NSIRDFDATASOURCE
00325 };
00326 
00327 //----------------------------------------------------------------------------
00328 // Global functions and data [implementation]
00329 
00330 NS_IMETHODIMP NS_NewCharsetMenu(nsISupports * aOuter, const nsIID & aIID, 
00331                                 void ** aResult)
00332 {
00333   if (!aResult) {
00334     return NS_ERROR_NULL_POINTER;
00335   }
00336   if (aOuter) {
00337     *aResult = nsnull;
00338     return NS_ERROR_NO_AGGREGATION;
00339   }
00340   nsCharsetMenu* inst = new nsCharsetMenu();
00341   if (!inst) {
00342     *aResult = nsnull;
00343     return NS_ERROR_OUT_OF_MEMORY;
00344   }
00345   nsresult res = inst->QueryInterface(aIID, aResult);
00346   if (NS_FAILED(res)) {
00347     *aResult = nsnull;
00348     delete inst;
00349   }
00350   return res;
00351 }
00352 
00353 struct charsetMenuSortRecord {
00354   nsMenuEntry* item;
00355   PRUint8*     key;
00356   PRUint32     len;
00357 
00358 };
00359 
00360 static int PR_CALLBACK CompareMenuItems(const void* aArg1, const void* aArg2, void *data)
00361 {
00362   PRInt32 res; 
00363   nsICollation * collation = (nsICollation *) data;
00364   charsetMenuSortRecord *rec1 = (charsetMenuSortRecord *) aArg1;
00365   charsetMenuSortRecord *rec2 = (charsetMenuSortRecord *) aArg2;
00366 
00367   collation->CompareRawSortKey(rec1->key, rec1->len, rec2->key, rec2->len, &res);
00368 
00369   return res;
00370 }
00371 
00372 nsresult
00373 nsCharsetMenu::SetArrayFromEnumerator(nsIUTF8StringEnumerator* aEnumerator,
00374                                       nsCStringArray& aArray)
00375 {
00376   nsresult rv;
00377   
00378   PRBool hasMore;
00379   rv = aEnumerator->HasMore(&hasMore);
00380   
00381   nsCAutoString value;
00382   while (NS_SUCCEEDED(rv) && hasMore) {
00383     rv = aEnumerator->GetNext(value);
00384     if (NS_SUCCEEDED(rv))
00385       aArray.AppendCString(value);
00386 
00387     rv = aEnumerator->HasMore(&hasMore);
00388   }
00389 
00390   return rv;
00391 }
00392   
00393 //----------------------------------------------------------------------------
00394 // Class nsCharsetMenuObserver
00395 
00396 class nsCharsetMenuObserver : public nsIObserver {
00397 
00398 public:
00399   NS_DECL_ISUPPORTS
00400   NS_DECL_NSIOBSERVER
00401 
00402   nsCharsetMenuObserver(nsCharsetMenu * menu)
00403     : mCharsetMenu(menu)
00404   {
00405   }
00406 
00407   virtual ~nsCharsetMenuObserver() {}
00408 
00409 private:
00410   nsCharsetMenu* mCharsetMenu;
00411 };
00412 
00413 NS_IMPL_ISUPPORTS1(nsCharsetMenuObserver, nsIObserver)
00414 
00415 NS_IMETHODIMP nsCharsetMenuObserver::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
00416 {
00417   NS_TIMELINE_START_TIMER("nsCharsetMenu:Observe");
00418   nsresult rv = NS_OK;
00419  
00420   //XUL event handler
00421   if (!nsCRT::strcmp(aTopic, "charsetmenu-selected")) {
00422     nsDependentString nodeName(someData);
00423     rv = mCharsetMenu->Init();
00424     if (nodeName.EqualsLiteral("browser")) {
00425       rv = mCharsetMenu->InitBrowserMenu();
00426     }
00427     if (nodeName.EqualsLiteral("composer")) {
00428       rv = mCharsetMenu->InitComposerMenu();
00429     }
00430     if (nodeName.EqualsLiteral("mailview")) {
00431       rv = mCharsetMenu->InitMailviewMenu();
00432     }
00433     if (nodeName.EqualsLiteral("mailedit")) {
00434       rv = mCharsetMenu->InitMaileditMenu();
00435       rv = mCharsetMenu->InitOthers();
00436     }
00437     if (nodeName.EqualsLiteral("more-menu")) {
00438       rv = mCharsetMenu->InitSecondaryTiers();
00439       rv = mCharsetMenu->InitAutodetMenu();
00440     }
00441     if (nodeName.EqualsLiteral("other")) {
00442       rv = mCharsetMenu->InitOthers();
00443       rv = mCharsetMenu->InitMaileditMenu();
00444     }
00445   }
00446    
00447    //pref event handler
00448   if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
00449     nsDependentString prefName(someData);
00450 
00451     if (prefName.EqualsLiteral(kBrowserStaticPrefKey)) {
00452       // refresh menus which share this pref
00453       rv = mCharsetMenu->RefreshBrowserMenu();
00454       NS_ENSURE_SUCCESS(rv, rv);
00455       rv = mCharsetMenu->RefreshMailviewMenu();
00456       NS_ENSURE_SUCCESS(rv, rv);
00457       rv = mCharsetMenu->RefreshComposerMenu();
00458     }
00459     else if (prefName.EqualsLiteral(kMaileditPrefKey)) {
00460       rv = mCharsetMenu->RefreshMaileditMenu();
00461     }
00462   }
00463 
00464   NS_TIMELINE_STOP_TIMER("nsCharsetMenu:Observe");
00465   NS_TIMELINE_MARK_TIMER("nsCharsetMenu:Observe");
00466   return rv;
00467 }
00468 
00469 //----------------------------------------------------------------------------
00470 // Class nsCharsetMenu [implementation]
00471 
00472 NS_IMPL_ISUPPORTS2(nsCharsetMenu, nsIRDFDataSource, nsICurrentCharsetListener)
00473 
00474 nsIRDFDataSource * nsCharsetMenu::mInner = NULL;
00475 nsIRDFResource * nsCharsetMenu::kNC_BrowserAutodetMenuRoot = NULL;
00476 nsIRDFResource * nsCharsetMenu::kNC_BrowserCharsetMenuRoot = NULL;
00477 nsIRDFResource * nsCharsetMenu::kNC_BrowserMoreCharsetMenuRoot = NULL;
00478 nsIRDFResource * nsCharsetMenu::kNC_BrowserMore1CharsetMenuRoot = NULL;
00479 nsIRDFResource * nsCharsetMenu::kNC_BrowserMore2CharsetMenuRoot = NULL;
00480 nsIRDFResource * nsCharsetMenu::kNC_BrowserMore3CharsetMenuRoot = NULL;
00481 nsIRDFResource * nsCharsetMenu::kNC_BrowserMore4CharsetMenuRoot = NULL;
00482 nsIRDFResource * nsCharsetMenu::kNC_BrowserMore5CharsetMenuRoot = NULL;
00483 nsIRDFResource * nsCharsetMenu::kNC_BrowserUnicodeCharsetMenuRoot = NULL;
00484 nsIRDFResource * nsCharsetMenu::kNC_MaileditCharsetMenuRoot = NULL;
00485 nsIRDFResource * nsCharsetMenu::kNC_MailviewCharsetMenuRoot = NULL;
00486 nsIRDFResource * nsCharsetMenu::kNC_ComposerCharsetMenuRoot = NULL;
00487 nsIRDFResource * nsCharsetMenu::kNC_DecodersRoot = NULL;
00488 nsIRDFResource * nsCharsetMenu::kNC_EncodersRoot = NULL;
00489 nsIRDFResource * nsCharsetMenu::kNC_Name = NULL;
00490 nsIRDFResource * nsCharsetMenu::kNC_Checked = NULL;
00491 nsIRDFResource * nsCharsetMenu::kNC_CharsetDetector = NULL;
00492 nsIRDFResource * nsCharsetMenu::kNC_BookmarkSeparator = NULL;
00493 nsIRDFResource * nsCharsetMenu::kRDF_type = NULL;
00494 
00495 nsCharsetMenu::nsCharsetMenu() 
00496 : mInitialized(PR_FALSE), 
00497   mBrowserMenuInitialized(PR_FALSE),
00498   mMailviewMenuInitialized(PR_FALSE),
00499   mComposerMenuInitialized(PR_FALSE),
00500   mMaileditMenuInitialized(PR_FALSE),
00501   mSecondaryTiersInitialized(PR_FALSE),
00502   mAutoDetectInitialized(PR_FALSE),
00503   mOthersInitialized(PR_FALSE)
00504 {
00505   NS_TIMELINE_START_TIMER("nsCharsetMenu::nsCharsetMenu");
00506   nsresult res = NS_OK;
00507 
00508   //get charset manager
00509   mCCManager = do_GetService(kCharsetConverterManagerCID, &res);
00510 
00511   //initialize skeleton RDF source
00512   mRDFService = do_GetService(kRDFServiceCID, &res);
00513 
00514   if (NS_SUCCEEDED(res))  {
00515     mRDFService->RegisterDataSource(this, PR_FALSE);
00516   
00517     CallCreateInstance(kRDFInMemoryDataSourceCID, &mInner);
00518 
00519     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserCharsetMenuRoot),
00520                              &kNC_BrowserCharsetMenuRoot);
00521   }
00522 
00523   //get pref service
00524   nsCOMPtr<nsIPrefService> PrefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &res);
00525   if (NS_SUCCEEDED(res))
00526     res = PrefService->GetBranch(nsnull, getter_AddRefs(mPrefs));
00527 
00528   //register event listener
00529   mCharsetMenuObserver = new nsCharsetMenuObserver(this);
00530 
00531   if (mCharsetMenuObserver)  {
00532     nsCOMPtr<nsIObserverService> observerService = 
00533              do_GetService("@mozilla.org/observer-service;1", &res);
00534 
00535     if (NS_SUCCEEDED(res))
00536       res = observerService->AddObserver(mCharsetMenuObserver, 
00537                                          "charsetmenu-selected", 
00538                                          PR_FALSE);
00539   }
00540 
00541   NS_ASSERTION(NS_SUCCEEDED(res), "Failed to initialize nsCharsetMenu");
00542   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::nsCharsetMenu");
00543   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::nsCharsetMenu");
00544 }
00545 
00546 nsCharsetMenu::~nsCharsetMenu() 
00547 {
00548   Done();
00549 
00550   FreeMenuItemArray(&mBrowserMenu);
00551   FreeMenuItemArray(&mMailviewMenu);
00552   FreeMenuItemArray(&mComposerMenu);
00553 
00554   FreeResources();
00555 }
00556 
00557 // XXX collapse these 2 in one
00558 
00559 nsresult nsCharsetMenu::RefreshBrowserMenu()
00560 {
00561   nsresult res = NS_OK;
00562 
00563   nsCOMPtr<nsIRDFContainer> container;
00564   res = NewRDFContainer(mInner, kNC_BrowserCharsetMenuRoot, getter_AddRefs(container));
00565   if (NS_FAILED(res)) return res;
00566 
00567   // clean the menu
00568   res = ClearMenu(container, &mBrowserMenu);
00569   if (NS_FAILED(res)) return res;
00570 
00571   // rebuild the menu
00572   nsCOMPtr<nsIUTF8StringEnumerator> decoders;
00573   res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
00574   if (NS_FAILED(res)) return res;
00575 
00576   nsCStringArray decs;
00577   SetArrayFromEnumerator(decoders, decs);
00578   
00579   res = AddFromPrefsToMenu(&mBrowserMenu, container, kBrowserStaticPrefKey, 
00580                            decs, "charset.");
00581   NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
00582 
00583   // mark the end of the static area, the rest is cache
00584   mBrowserCacheStart = mBrowserMenu.Count();
00585 
00586   // Remove "notForBrowser" entries before populating cache menu
00587   res = RemoveFlaggedCharsets(decs, NS_LITERAL_STRING(".notForBrowser"));
00588   NS_ASSERTION(NS_SUCCEEDED(res), "error removing flagged charsets");
00589 
00590   res = InitCacheMenu(decs, kNC_BrowserCharsetMenuRoot, kBrowserCachePrefKey, 
00591                       &mBrowserMenu);
00592   NS_ASSERTION(NS_SUCCEEDED(res), "error initializing browser cache charset menu");
00593 
00594   return res;
00595 }
00596 
00597 nsresult nsCharsetMenu::RefreshMailviewMenu()
00598 {
00599   nsresult res = NS_OK;
00600 
00601   nsCOMPtr<nsIRDFContainer> container;
00602   res = NewRDFContainer(mInner, kNC_MailviewCharsetMenuRoot, getter_AddRefs(container));
00603   if (NS_FAILED(res)) return res;
00604 
00605   // clean the menu
00606   res = ClearMenu(container, &mMailviewMenu);
00607   if (NS_FAILED(res)) return res;
00608 
00609   nsCOMPtr<nsIUTF8StringEnumerator> decoders;
00610   res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
00611   if (NS_FAILED(res)) return res;
00612 
00613   nsCStringArray decs;
00614   SetArrayFromEnumerator(decoders, decs);
00615   
00616   res = AddFromPrefsToMenu(&mMailviewMenu, container, kMailviewStaticPrefKey, 
00617                            decs, "charset.");
00618   NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
00619 
00620   // mark the end of the static area, the rest is cache
00621   mMailviewCacheStart = mMailviewMenu.Count();
00622 
00623   res = InitCacheMenu(decs, kNC_MailviewCharsetMenuRoot, 
00624     kMailviewCachePrefKey, &mMailviewMenu);
00625   NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailview cache charset menu");
00626 
00627   return res;
00628 }
00629 
00630 nsresult nsCharsetMenu::RefreshMaileditMenu()
00631 {
00632   nsresult res;
00633 
00634   nsCOMPtr<nsIRDFContainer> container;
00635   res = NewRDFContainer(mInner, kNC_MaileditCharsetMenuRoot, getter_AddRefs(container));
00636   NS_ENSURE_SUCCESS(res, res);
00637 
00638   nsCOMPtr<nsISimpleEnumerator> enumerator;
00639   res = container->GetElements(getter_AddRefs(enumerator));
00640   NS_ENSURE_SUCCESS(res, res);
00641 
00642   // clear the menu
00643   nsCOMPtr<nsIRDFNode> node;
00644   while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(node)))) {
00645 
00646     res = mInner->Unassert(kNC_MaileditCharsetMenuRoot, kNC_Name, node);
00647     NS_ENSURE_SUCCESS(res, res);
00648 
00649     res = container->RemoveElement(node, PR_FALSE);
00650     NS_ENSURE_SUCCESS(res, res);
00651   }
00652 
00653   // get a list of available encoders
00654   nsCOMPtr<nsIUTF8StringEnumerator> encoders;
00655   res = mCCManager->GetEncoderList(getter_AddRefs(encoders));
00656   NS_ENSURE_SUCCESS(res, res);
00657 
00658   nsCStringArray encs;
00659   SetArrayFromEnumerator(encoders, encs);
00660   
00661   // add menu items from pref
00662   res = AddFromPrefsToMenu(NULL, container, kMaileditPrefKey, encs, NULL);
00663   NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailedit charset menu from prefs");
00664 
00665   return res;
00666 }
00667 
00668 nsresult nsCharsetMenu::RefreshComposerMenu()
00669 {
00670   nsresult res = NS_OK;
00671 
00672   nsCOMPtr<nsIRDFContainer> container;
00673   res = NewRDFContainer(mInner, kNC_ComposerCharsetMenuRoot, getter_AddRefs(container));
00674   if (NS_FAILED(res)) return res;
00675 
00676   // clean the menu
00677   res = ClearMenu(container, &mComposerMenu);
00678   if (NS_FAILED(res)) return res;
00679 
00680   nsCOMPtr<nsIUTF8StringEnumerator> decoders;
00681   res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
00682   if (NS_FAILED(res)) return res;
00683 
00684   nsCStringArray decs;
00685   SetArrayFromEnumerator(decoders, decs);
00686   
00687   res = AddFromPrefsToMenu(&mComposerMenu, container, kComposerStaticPrefKey, 
00688                            decs, "charset.");
00689   NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
00690 
00691   // mark the end of the static area, the rest is cache
00692   mComposerCacheStart = mComposerMenu.Count();
00693 
00694   res = InitCacheMenu(decs, kNC_ComposerCharsetMenuRoot, 
00695     kComposerCachePrefKey, &mComposerMenu);
00696   NS_ASSERTION(NS_SUCCEEDED(res), "error initializing composer cache charset menu");
00697 
00698   return res;
00699 }
00700 
00701 nsresult nsCharsetMenu::Init() 
00702 {
00703   nsresult res = NS_OK;
00704 
00705   if (!mInitialized)  {
00706 
00707     //enumerate decoders
00708     nsCOMPtr<nsIUTF8StringEnumerator> decoders;
00709     res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
00710     if (NS_FAILED(res)) return res;
00711 
00712     SetArrayFromEnumerator(decoders, mDecoderList);
00713 
00714     //initialize all remaining RDF template nodes
00715     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserAutodetMenuRoot),
00716                              &kNC_BrowserAutodetMenuRoot);
00717     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMoreCharsetMenuRoot),
00718                              &kNC_BrowserMoreCharsetMenuRoot);
00719     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore1CharsetMenuRoot),
00720                              &kNC_BrowserMore1CharsetMenuRoot);
00721     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore2CharsetMenuRoot),
00722                              &kNC_BrowserMore2CharsetMenuRoot);
00723     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore3CharsetMenuRoot),
00724                              &kNC_BrowserMore3CharsetMenuRoot);
00725     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore4CharsetMenuRoot),
00726                              &kNC_BrowserMore4CharsetMenuRoot);
00727     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore5CharsetMenuRoot),
00728                              &kNC_BrowserMore5CharsetMenuRoot);
00729     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserUnicodeCharsetMenuRoot),
00730                              &kNC_BrowserUnicodeCharsetMenuRoot);
00731     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_MaileditCharsetMenuRoot),
00732                              &kNC_MaileditCharsetMenuRoot);
00733     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_MailviewCharsetMenuRoot),
00734                              &kNC_MailviewCharsetMenuRoot);
00735     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_ComposerCharsetMenuRoot),
00736                              &kNC_ComposerCharsetMenuRoot);
00737     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_DecodersRoot),
00738                              &kNC_DecodersRoot);
00739     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_EncodersRoot),
00740                              &kNC_EncodersRoot);
00741     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_Name),
00742                              &kNC_Name);
00743     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_Checked),
00744                              &kNC_Checked);
00745     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_CharsetDetector),
00746                              &kNC_CharsetDetector);
00747     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BookmarkSeparator),
00748                              &kNC_BookmarkSeparator);
00749     mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_type), &kRDF_type);
00750 
00751     nsIRDFContainerUtils * rdfUtil = NULL;
00752     res = CallGetService(kRDFContainerUtilsCID, &rdfUtil);
00753     if (NS_FAILED(res)) goto done;
00754 
00755     res = rdfUtil->MakeSeq(mInner, kNC_BrowserAutodetMenuRoot, NULL);
00756     if (NS_FAILED(res)) goto done;
00757     res = rdfUtil->MakeSeq(mInner, kNC_BrowserCharsetMenuRoot, NULL);
00758     if (NS_FAILED(res)) goto done;
00759     res = rdfUtil->MakeSeq(mInner, kNC_BrowserMoreCharsetMenuRoot, NULL);
00760     if (NS_FAILED(res)) goto done;
00761     res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore1CharsetMenuRoot, NULL);
00762     if (NS_FAILED(res)) goto done;
00763     res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore2CharsetMenuRoot, NULL);
00764     if (NS_FAILED(res)) goto done;
00765     res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore3CharsetMenuRoot, NULL);
00766     if (NS_FAILED(res)) goto done;
00767     res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore4CharsetMenuRoot, NULL);
00768     if (NS_FAILED(res)) goto done;
00769     res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore5CharsetMenuRoot, NULL);
00770     if (NS_FAILED(res)) goto done;
00771     res = rdfUtil->MakeSeq(mInner, kNC_BrowserUnicodeCharsetMenuRoot, NULL);
00772     if (NS_FAILED(res)) goto done;
00773     res = rdfUtil->MakeSeq(mInner, kNC_MaileditCharsetMenuRoot, NULL);
00774     if (NS_FAILED(res)) goto done;
00775     res = rdfUtil->MakeSeq(mInner, kNC_MailviewCharsetMenuRoot, NULL);
00776     if (NS_FAILED(res)) goto done;
00777     res = rdfUtil->MakeSeq(mInner, kNC_ComposerCharsetMenuRoot, NULL);
00778     if (NS_FAILED(res)) goto done;
00779     res = rdfUtil->MakeSeq(mInner, kNC_DecodersRoot, NULL);
00780     if (NS_FAILED(res)) goto done;
00781     res = rdfUtil->MakeSeq(mInner, kNC_EncodersRoot, NULL);
00782     if (NS_FAILED(res)) goto done;
00783 
00784   done:
00785     NS_IF_RELEASE(rdfUtil);
00786     if (NS_FAILED(res)) return res;
00787   }
00788   mInitialized = NS_SUCCEEDED(res);
00789   return res;
00790 }
00791 
00792 nsresult nsCharsetMenu::Done() 
00793 {
00794   nsresult res = NS_OK;
00795   res = mRDFService->UnregisterDataSource(this);
00796 
00797   NS_IF_RELEASE(kNC_BrowserAutodetMenuRoot);
00798   NS_IF_RELEASE(kNC_BrowserCharsetMenuRoot);
00799   NS_IF_RELEASE(kNC_BrowserMoreCharsetMenuRoot);
00800   NS_IF_RELEASE(kNC_BrowserMore1CharsetMenuRoot);
00801   NS_IF_RELEASE(kNC_BrowserMore2CharsetMenuRoot);
00802   NS_IF_RELEASE(kNC_BrowserMore3CharsetMenuRoot);
00803   NS_IF_RELEASE(kNC_BrowserMore4CharsetMenuRoot);
00804   NS_IF_RELEASE(kNC_BrowserMore5CharsetMenuRoot);
00805   NS_IF_RELEASE(kNC_BrowserUnicodeCharsetMenuRoot);
00806   NS_IF_RELEASE(kNC_MaileditCharsetMenuRoot);
00807   NS_IF_RELEASE(kNC_MailviewCharsetMenuRoot);
00808   NS_IF_RELEASE(kNC_ComposerCharsetMenuRoot);
00809   NS_IF_RELEASE(kNC_DecodersRoot);
00810   NS_IF_RELEASE(kNC_EncodersRoot);
00811   NS_IF_RELEASE(kNC_Name);
00812   NS_IF_RELEASE(kNC_Checked);
00813   NS_IF_RELEASE(kNC_CharsetDetector);
00814   NS_IF_RELEASE(kNC_BookmarkSeparator);
00815   NS_IF_RELEASE(kRDF_type);
00816   NS_IF_RELEASE(mInner);
00817 
00818   return res;
00819 }
00820 
00821 nsresult nsCharsetMenu::SetCharsetCheckmark(nsString * aCharset, 
00822                                             PRBool aValue)
00823 {
00824   nsresult res = NS_OK;
00825   nsCOMPtr<nsIRDFContainer> container;
00826   nsCOMPtr<nsIRDFResource> node;
00827 
00828   res = NewRDFContainer(mInner, kNC_BrowserCharsetMenuRoot, getter_AddRefs(container));
00829   if (NS_FAILED(res)) return res;
00830 
00831   // find RDF node for given charset
00832   res = mRDFService->GetUnicodeResource(*aCharset, getter_AddRefs(node));
00833   if (NS_FAILED(res)) return res;
00834 
00835   // set checkmark value
00836   nsCOMPtr<nsIRDFLiteral> checkedLiteral;
00837   nsAutoString checked; checked.AssignWithConversion((aValue == PR_TRUE) ? "true" : "false");
00838   res = mRDFService->GetLiteral(checked.get(), getter_AddRefs(checkedLiteral));
00839   if (NS_FAILED(res)) return res;
00840   res = Assert(node, kNC_Checked, checkedLiteral, PR_TRUE);
00841   if (NS_FAILED(res)) return res;
00842 
00843   return res;
00844 }
00845 
00849 nsresult nsCharsetMenu::FreeResources()
00850 {
00851   nsresult res = NS_OK;
00852 
00853   if (mCharsetMenuObserver) {
00854     nsCOMPtr<nsIPrefBranch2> pbi = do_QueryInterface(mPrefs);
00855     if (pbi) {
00856       pbi->RemoveObserver(kBrowserStaticPrefKey, mCharsetMenuObserver);
00857       pbi->RemoveObserver(kMaileditPrefKey, mCharsetMenuObserver);
00858     }
00859     /* nsIObserverService has to have released nsCharsetMenu already */
00860   }
00861 
00862   mRDFService = NULL;
00863   mCCManager  = NULL;
00864   mPrefs      = NULL;
00865 
00866   return res;
00867 }
00868 
00869 nsresult nsCharsetMenu::InitBrowserMenu() 
00870 {
00871   NS_TIMELINE_START_TIMER("nsCharsetMenu::InitBrowserMenu");
00872 
00873   nsresult res = NS_OK;
00874 
00875   if (!mBrowserMenuInitialized)  {
00876     nsCOMPtr<nsIRDFContainer> container;
00877     res = NewRDFContainer(mInner, kNC_BrowserCharsetMenuRoot, getter_AddRefs(container));
00878     if (NS_FAILED(res)) return res;
00879 
00880 
00881     // how to clone mDecoderList??
00882     nsCStringArray browserDecoderList;
00883     CloneCStringArray(mDecoderList, browserDecoderList);
00884 
00885     res = InitStaticMenu(browserDecoderList, kNC_BrowserCharsetMenuRoot, 
00886                          kBrowserStaticPrefKey, &mBrowserMenu);
00887     NS_ASSERTION(NS_SUCCEEDED(res), "error initializing browser static charset menu");
00888 
00889     // mark the end of the static area, the rest is cache
00890     mBrowserCacheStart = mBrowserMenu.Count();
00891     mPrefs->GetIntPref(kBrowserCacheSizePrefKey, &mBrowserCacheSize);
00892 
00893     // compute the position of the menu in the RDF container
00894     res = container->GetCount(&mBrowserMenuRDFPosition);
00895     if (NS_FAILED(res)) return res;
00896     // this "1" here is a correction necessary because the RDF container 
00897     // elements are numbered from 1 (why god, WHY?!?!?!)
00898     mBrowserMenuRDFPosition -= mBrowserCacheStart - 1;
00899 
00900     // Remove "notForBrowser" entries before populating cache menu
00901     res = RemoveFlaggedCharsets(browserDecoderList, NS_LITERAL_STRING(".notForBrowser"));
00902     NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
00903 
00904     res = InitCacheMenu(browserDecoderList, kNC_BrowserCharsetMenuRoot, kBrowserCachePrefKey, 
00905       &mBrowserMenu);
00906     NS_ASSERTION(NS_SUCCEEDED(res), "error initializing browser cache charset menu");
00907 
00908     // register prefs callback
00909     nsCOMPtr<nsIPrefBranch2> pbi = do_QueryInterface(mPrefs);
00910     if (pbi)
00911       res = pbi->AddObserver(kBrowserStaticPrefKey, mCharsetMenuObserver, PR_FALSE);
00912   }
00913 
00914   mBrowserMenuInitialized = NS_SUCCEEDED(res);
00915 
00916   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitBrowserMenu");
00917   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitBrowserMenu");
00918 
00919   return res;
00920 }
00921 
00922 nsresult nsCharsetMenu::InitMaileditMenu() 
00923 {
00924   NS_TIMELINE_START_TIMER("nsCharsetMenu::InitMaileditMenu");
00925 
00926   nsresult res = NS_OK;
00927 
00928   if (!mMaileditMenuInitialized)  {
00929     nsCOMPtr<nsIRDFContainer> container;
00930     res = NewRDFContainer(mInner, kNC_MaileditCharsetMenuRoot, getter_AddRefs(container));
00931     if (NS_FAILED(res)) return res;
00932 
00933     //enumerate encoders
00934     // this would bring in a whole bunch of 'font encoders' as well as genuine 
00935     // charset encoders, but it's safe because we rely on prefs to filter
00936     // them out. Moreover, 'customize' menu lists only genuine charset 
00937     // encoders further guarding  against  'font encoders' sneaking in. 
00938     nsCOMPtr<nsIUTF8StringEnumerator> encoders;
00939     res = mCCManager->GetEncoderList(getter_AddRefs(encoders));
00940     if (NS_FAILED(res))  return res;
00941 
00942     nsCStringArray maileditEncoderList;
00943     SetArrayFromEnumerator(encoders, maileditEncoderList);
00944   
00945     res = AddFromPrefsToMenu(NULL, container, kMaileditPrefKey, maileditEncoderList, NULL);
00946     NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailedit charset menu from prefs");
00947 
00948     // register prefs callback
00949     nsCOMPtr<nsIPrefBranch2> pbi = do_QueryInterface(mPrefs);
00950     if (pbi)
00951       res = pbi->AddObserver(kMaileditPrefKey, mCharsetMenuObserver, PR_FALSE);
00952   }
00953 
00954   mMaileditMenuInitialized = NS_SUCCEEDED(res);
00955 
00956   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitMaileditMenu");
00957   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitMaileditMenu");
00958 
00959   return res;
00960 }
00961 
00962 nsresult nsCharsetMenu::InitMailviewMenu() 
00963 {
00964   NS_TIMELINE_START_TIMER("nsCharsetMenu::InitMailviewMenu");
00965   
00966   nsresult res = NS_OK;
00967 
00968   if (!mMailviewMenuInitialized)  {
00969     nsCOMPtr<nsIRDFContainer> container;
00970     res = NewRDFContainer(mInner, kNC_MailviewCharsetMenuRoot, getter_AddRefs(container));
00971     if (NS_FAILED(res)) return res;
00972 
00973     nsCStringArray mailviewDecoderList;
00974     CloneCStringArray(mDecoderList, mailviewDecoderList);
00975 
00976     res = InitStaticMenu(mailviewDecoderList, kNC_MailviewCharsetMenuRoot, 
00977                          kMailviewStaticPrefKey, &mMailviewMenu);
00978     NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailview static charset menu");
00979 
00980     // mark the end of the static area, the rest is cache
00981     mMailviewCacheStart = mMailviewMenu.Count();
00982     mPrefs->GetIntPref(kMailviewCacheSizePrefKey, &mMailviewCacheSize);
00983 
00984     // compute the position of the menu in the RDF container
00985     res = container->GetCount(&mMailviewMenuRDFPosition);
00986     if (NS_FAILED(res)) return res;
00987     // this "1" here is a correction necessary because the RDF container 
00988     // elements are numbered from 1 (why god, WHY?!?!?!)
00989     mMailviewMenuRDFPosition -= mMailviewCacheStart - 1;
00990 
00991     res = InitCacheMenu(mailviewDecoderList, kNC_MailviewCharsetMenuRoot, 
00992       kMailviewCachePrefKey, &mMailviewMenu);
00993     NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailview cache charset menu");
00994   }
00995 
00996   mMailviewMenuInitialized = NS_SUCCEEDED(res);
00997 
00998   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitMailviewMenu");
00999   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitMailviewMenu");
01000 
01001   return res;
01002 }
01003 
01004 nsresult nsCharsetMenu::InitComposerMenu() 
01005 {
01006   NS_TIMELINE_START_TIMER("nsCharsetMenu::InitComposerMenu");
01007  
01008   nsresult res = NS_OK;
01009 
01010   if (!mComposerMenuInitialized)  {
01011     nsCOMPtr<nsIRDFContainer> container;
01012     res = NewRDFContainer(mInner, kNC_ComposerCharsetMenuRoot, getter_AddRefs(container));
01013     if (NS_FAILED(res)) return res;
01014 
01015     nsCStringArray composerDecoderList;
01016     CloneCStringArray(mDecoderList, composerDecoderList);
01017 
01018     // even if we fail, the show must go on
01019     res = InitStaticMenu(composerDecoderList, kNC_ComposerCharsetMenuRoot, 
01020       kComposerStaticPrefKey, &mComposerMenu);
01021     NS_ASSERTION(NS_SUCCEEDED(res), "error initializing composer static charset menu");
01022 
01023     // mark the end of the static area, the rest is cache
01024     mComposerCacheStart = mComposerMenu.Count();
01025     mPrefs->GetIntPref(kComposerCacheSizePrefKey, &mComposerCacheSize);
01026 
01027     // compute the position of the menu in the RDF container
01028     res = container->GetCount(&mComposerMenuRDFPosition);
01029     if (NS_FAILED(res)) return res;
01030     // this "1" here is a correction necessary because the RDF container 
01031     // elements are numbered from 1 (why god, WHY?!?!?!)
01032     mComposerMenuRDFPosition -= mComposerCacheStart - 1;
01033 
01034     res = InitCacheMenu(composerDecoderList, kNC_ComposerCharsetMenuRoot, 
01035       kComposerCachePrefKey, &mComposerMenu);
01036     NS_ASSERTION(NS_SUCCEEDED(res), "error initializing composer cache charset menu");
01037   }
01038 
01039   mComposerMenuInitialized = NS_SUCCEEDED(res);
01040 
01041   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitComposerMenu");
01042   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitComposerMenu");
01043   
01044   return res;
01045 }
01046 
01047 nsresult nsCharsetMenu::InitOthers() 
01048 {
01049   NS_TIMELINE_START_TIMER("nsCharsetMenu::InitOthers");
01050 
01051   nsresult res = NS_OK;
01052 
01053   if (!mOthersInitialized) {
01054     nsCStringArray othersDecoderList;
01055     CloneCStringArray(mDecoderList, othersDecoderList);
01056 
01057     res = InitMoreMenu(othersDecoderList, kNC_DecodersRoot, ".notForBrowser");                 
01058     if (NS_FAILED(res))  return res;
01059 
01060     // Using mDecoderList instead of GetEncoderList(), we can avoid having to
01061     // tag a whole bunch of 'font encoders' with '.notForOutgoing' in 
01062     // charsetData.properties file. 
01063     nsCStringArray othersEncoderList;
01064     CloneCStringArray(mDecoderList, othersEncoderList);
01065 
01066     res = InitMoreMenu(othersEncoderList, kNC_EncodersRoot, ".notForOutgoing");                 
01067     if (NS_FAILED(res)) return res;
01068   }
01069 
01070   mOthersInitialized = NS_SUCCEEDED(res);
01071 
01072   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitOthers");
01073   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitOthers");
01074 
01075   return res;
01076 }
01077 
01083 nsresult nsCharsetMenu::InitSecondaryTiers()
01084 {
01085   NS_TIMELINE_START_TIMER("nsCharsetMenu::InitSecondaryTiers");
01086 
01087   nsresult res = NS_OK;
01088 
01089   if (!mSecondaryTiersInitialized)  {
01090     nsCStringArray secondaryTiersDecoderList;
01091     CloneCStringArray(mDecoderList, secondaryTiersDecoderList);
01092 
01093     res = InitMoreSubmenus(secondaryTiersDecoderList);
01094     NS_ASSERTION(NS_SUCCEEDED(res), "err init browser charset more submenus");
01095 
01096     res = InitMoreMenu(secondaryTiersDecoderList, kNC_BrowserMoreCharsetMenuRoot, ".notForBrowser");
01097     NS_ASSERTION(NS_SUCCEEDED(res), "err init browser charset more menu");
01098   }
01099 
01100   mSecondaryTiersInitialized = NS_SUCCEEDED(res);
01101 
01102   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitSecondaryTiers");
01103   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitSecondaryTiers");
01104 
01105   return res;
01106 }
01107 
01108 nsresult nsCharsetMenu::InitStaticMenu(nsCStringArray& aDecs,
01109                                        nsIRDFResource * aResource, 
01110                                        const char * aKey, 
01111                                        nsVoidArray * aArray)
01112 {
01113   NS_TIMELINE_START_TIMER("nsCharsetMenu::InitStaticMenu");
01114 
01115   nsresult res = NS_OK;
01116   nsCOMPtr<nsIRDFContainer> container;
01117 
01118   res = NewRDFContainer(mInner, aResource, getter_AddRefs(container));
01119   if (NS_FAILED(res)) return res;
01120 
01121   // XXX work around bug that causes the submenus to be first instead of last
01122   res = AddSeparatorToContainer(container);
01123   NS_ASSERTION(NS_SUCCEEDED(res), "error adding separator to container");
01124 
01125   res = AddFromPrefsToMenu(aArray, container, aKey, aDecs, "charset.");
01126   NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
01127 
01128   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitStaticMenu");
01129   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitStaticMenu");
01130 
01131   return res;
01132 }
01133 
01134 nsresult nsCharsetMenu::InitCacheMenu(
01135                         nsCStringArray& aDecs,
01136                         nsIRDFResource * aResource, 
01137                         const char * aKey, 
01138                         nsVoidArray * aArray)
01139 {
01140   NS_TIMELINE_START_TIMER("nsCharsetMenu::InitCacheMenu");
01141 
01142   nsresult res = NS_OK;
01143   nsCOMPtr<nsIRDFContainer> container;
01144 
01145   res = NewRDFContainer(mInner, aResource, getter_AddRefs(container));
01146   if (NS_FAILED(res)) return res;
01147 
01148   res = AddFromNolocPrefsToMenu(aArray, container, aKey, aDecs, "charset.");
01149   NS_ASSERTION(NS_SUCCEEDED(res), "error initializing cache charset menu from prefs");
01150 
01151   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitCacheMenu");
01152   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitCacheMenu");
01153 
01154   return res;
01155 }
01156 
01157 nsresult nsCharsetMenu::InitAutodetMenu()
01158 {
01159   NS_TIMELINE_START_TIMER("nsCharsetMenu::InitAutodetMenu");
01160 
01161   nsresult res = NS_OK;
01162 
01163   if (!mAutoDetectInitialized) {
01164     nsVoidArray chardetArray;
01165     nsCOMPtr<nsIRDFContainer> container;
01166     nsCStringArray detectorArray;
01167 
01168     res = NewRDFContainer(mInner, kNC_BrowserAutodetMenuRoot, getter_AddRefs(container));
01169     if (NS_FAILED(res)) return res;
01170 
01171     nsCOMPtr<nsIUTF8StringEnumerator> detectors;
01172     res = mCCManager->GetCharsetDetectorList(getter_AddRefs(detectors));
01173     if (NS_FAILED(res)) goto done;
01174 
01175     res = SetArrayFromEnumerator(detectors, detectorArray);
01176     if (NS_FAILED(res)) goto done;
01177     
01178     res = AddCharsetArrayToItemArray(chardetArray, detectorArray);
01179     if (NS_FAILED(res)) goto done;
01180 
01181     // reorder the array
01182     res = ReorderMenuItemArray(&chardetArray);
01183     if (NS_FAILED(res)) goto done;
01184 
01185     res = AddMenuItemArrayToContainer(container, &chardetArray, 
01186       kNC_CharsetDetector);
01187     if (NS_FAILED(res)) goto done;
01188 
01189   done:
01190     // free the elements in the VoidArray
01191     FreeMenuItemArray(&chardetArray);
01192   }
01193 
01194   mAutoDetectInitialized = NS_SUCCEEDED(res);
01195 
01196   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitAutodetMenu");
01197   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitAutodetMenu");
01198 
01199   return res;
01200 }
01201 
01202 nsresult nsCharsetMenu::InitMoreMenu(nsCStringArray& aDecs, 
01203                                      nsIRDFResource * aResource, 
01204                                      const char * aFlag)
01205 {
01206   NS_TIMELINE_START_TIMER("nsCharsetMenu::InitMoreMenu");
01207 
01208   nsresult res = NS_OK;
01209   nsCOMPtr<nsIRDFContainer> container;
01210   nsVoidArray moreMenu;
01211 
01212   res = NewRDFContainer(mInner, aResource, getter_AddRefs(container));
01213   if (NS_FAILED(res)) goto done;
01214 
01215   // remove charsets "not for browser"
01216   res = RemoveFlaggedCharsets(aDecs, NS_ConvertASCIItoUTF16(aFlag));
01217   if (NS_FAILED(res)) goto done;
01218 
01219   res = AddCharsetArrayToItemArray(moreMenu, aDecs);
01220   if (NS_FAILED(res)) goto done;
01221 
01222   // reorder the array
01223   res = ReorderMenuItemArray(&moreMenu);
01224   if (NS_FAILED(res)) goto done;
01225 
01226   res = AddMenuItemArrayToContainer(container, &moreMenu, NULL);
01227   if (NS_FAILED(res)) goto done;
01228 
01229 done:
01230   // free the elements in the VoidArray
01231   FreeMenuItemArray(&moreMenu);
01232 
01233   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitMoreMenu");
01234   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitMoreMenu");
01235 
01236   return res;
01237 }
01238 
01239 // XXX please make this method more general; the cut&pasted code is laughable
01240 nsresult nsCharsetMenu::InitMoreSubmenus(nsCStringArray& aDecs)
01241 {
01242   NS_TIMELINE_START_TIMER("nsCharsetMenu::InitMoreSubmenus");
01243 
01244   nsresult res = NS_OK;
01245 
01246   nsCOMPtr<nsIRDFContainer> container1;
01247   nsCOMPtr<nsIRDFContainer> container2;
01248   nsCOMPtr<nsIRDFContainer> container3;
01249   nsCOMPtr<nsIRDFContainer> container4;
01250   nsCOMPtr<nsIRDFContainer> container5;
01251   nsCOMPtr<nsIRDFContainer> containerU;
01252   const char key1[] = "intl.charsetmenu.browser.more1";
01253   const char key2[] = "intl.charsetmenu.browser.more2";
01254   const char key3[] = "intl.charsetmenu.browser.more3";
01255   const char key4[] = "intl.charsetmenu.browser.more4";
01256   const char key5[] = "intl.charsetmenu.browser.more5";
01257   const char keyU[] = "intl.charsetmenu.browser.unicode";
01258 
01259   res = NewRDFContainer(mInner, kNC_BrowserMore1CharsetMenuRoot, 
01260     getter_AddRefs(container1));
01261   if (NS_FAILED(res)) return res;
01262   AddFromPrefsToMenu(NULL, container1, key1, aDecs, NULL);
01263 
01264   res = NewRDFContainer(mInner, kNC_BrowserMore2CharsetMenuRoot, 
01265     getter_AddRefs(container2));
01266   if (NS_FAILED(res)) return res;
01267   AddFromPrefsToMenu(NULL, container2, key2, aDecs, NULL);
01268 
01269   res = NewRDFContainer(mInner, kNC_BrowserMore3CharsetMenuRoot, 
01270     getter_AddRefs(container3));
01271   if (NS_FAILED(res)) return res;
01272   AddFromPrefsToMenu(NULL, container3, key3, aDecs, NULL);
01273 
01274   res = NewRDFContainer(mInner, kNC_BrowserMore4CharsetMenuRoot, 
01275     getter_AddRefs(container4));
01276   if (NS_FAILED(res)) return res;
01277   AddFromPrefsToMenu(NULL, container4, key4, aDecs, NULL);
01278 
01279   res = NewRDFContainer(mInner, kNC_BrowserMore5CharsetMenuRoot, 
01280     getter_AddRefs(container5));
01281   if (NS_FAILED(res)) return res;
01282   AddFromPrefsToMenu(NULL, container5, key5, aDecs, NULL);
01283 
01284   res = NewRDFContainer(mInner, kNC_BrowserUnicodeCharsetMenuRoot, 
01285     getter_AddRefs(containerU));
01286   if (NS_FAILED(res)) return res;
01287   AddFromPrefsToMenu(NULL, containerU, keyU, aDecs, NULL);
01288 
01289   NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitMoreSubmenus");
01290   NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitMoreSubmenus");
01291 
01292   return res;
01293 }
01294 
01295 nsresult nsCharsetMenu::AddCharsetToItemArray(nsVoidArray *aArray, 
01296                                               const nsAFlatCString& aCharset, 
01297                                               nsMenuEntry ** aResult,
01298                                               PRInt32 aPlace) 
01299 {
01300   nsresult res = NS_OK;
01301   nsMenuEntry * item = NULL; 
01302 
01303   if (aResult != NULL) *aResult = NULL;
01304   
01305   item = new nsMenuEntry();
01306   if (item == NULL) {
01307     res = NS_ERROR_OUT_OF_MEMORY;
01308     goto done;
01309   }
01310 
01311   item->mCharset = aCharset;
01312 
01313   res = mCCManager->GetCharsetTitle(aCharset.get(), item->mTitle);
01314   if (NS_FAILED(res)) {
01315     item->mTitle.AssignWithConversion(aCharset.get());
01316   }
01317 
01318   if (aArray != NULL) {
01319     if (aPlace < 0) {
01320       res = aArray->AppendElement(item);
01321       if (NS_FAILED(res)) goto done;
01322     } else {
01323       res = aArray->InsertElementAt(item, aPlace);
01324       if (NS_FAILED(res)) goto done;
01325     }
01326   }
01327 
01328   if (aResult != NULL) *aResult = item;
01329 
01330   // if we have made another reference to "item", do not delete it 
01331   if ((aArray != NULL) || (aResult != NULL)) item = NULL; 
01332 
01333 done:
01334   if (item != NULL) delete item;
01335 
01336   return res;
01337 }
01338 
01339 nsresult
01340 nsCharsetMenu::AddCharsetArrayToItemArray(nsVoidArray& aArray, 
01341                                           const nsCStringArray& aCharsets) 
01342 {
01343   PRUint32 count = aCharsets.Count();
01344 
01345   for (PRUint32 i = 0; i < count; i++) {
01346 
01347     nsCString* str = aCharsets.CStringAt(i);
01348     if (str) {
01349       nsresult res = AddCharsetToItemArray(&aArray, *str, NULL, -1);
01350     
01351       if (NS_FAILED(res)) return res;
01352     }
01353   }
01354 
01355   return NS_OK;
01356 }
01357 
01358 // aPlace < -1 for Remove
01359 // aPlace < 0 for Append
01360 nsresult nsCharsetMenu::AddMenuItemToContainer(
01361                         nsIRDFContainer * aContainer,
01362                         nsMenuEntry * aItem,
01363                         nsIRDFResource * aType,
01364                         const char * aIDPrefix,
01365                         PRInt32 aPlace) 
01366 {
01367   nsresult res = NS_OK;
01368   nsCOMPtr<nsIRDFResource> node;
01369 
01370   nsCAutoString id;
01371   if (aIDPrefix != NULL) id.Assign(aIDPrefix);
01372   id.Append(aItem->mCharset);
01373 
01374   // Make up a unique ID and create the RDF NODE
01375   res = mRDFService->GetResource(id, getter_AddRefs(node));
01376   if (NS_FAILED(res)) return res;
01377 
01378   const PRUnichar * title = aItem->mTitle.get();
01379 
01380   // set node's title
01381   nsCOMPtr<nsIRDFLiteral> titleLiteral;
01382   res = mRDFService->GetLiteral(title, getter_AddRefs(titleLiteral));
01383   if (NS_FAILED(res)) return res;
01384 
01385   if (aPlace < -1) {
01386     res = Unassert(node, kNC_Name, titleLiteral);
01387     if (NS_FAILED(res)) return res;
01388   } else {
01389     res = Assert(node, kNC_Name, titleLiteral, PR_TRUE);
01390     if (NS_FAILED(res)) return res;
01391   }
01392 
01393   if (aType != NULL) {
01394     if (aPlace < -1) {
01395       res = Unassert(node, kRDF_type, aType);
01396       if (NS_FAILED(res)) return res;
01397     } else {
01398       res = Assert(node, kRDF_type, aType, PR_TRUE);
01399       if (NS_FAILED(res)) return res;
01400     }
01401   }
01402 
01403   // Add the element to the container
01404   if (aPlace < -1) {
01405     res = aContainer->RemoveElement(node, PR_TRUE);
01406     if (NS_FAILED(res)) return res;
01407   } else if (aPlace < 0) {
01408     res = aContainer->AppendElement(node);
01409     if (NS_FAILED(res)) return res;
01410   } else {
01411     res = aContainer->InsertElementAt(node, aPlace, PR_TRUE);
01412     if (NS_FAILED(res)) return res;
01413   } 
01414 
01415   return res;
01416 }
01417 
01418 nsresult nsCharsetMenu::AddMenuItemArrayToContainer(
01419                         nsIRDFContainer * aContainer,
01420                         nsVoidArray * aArray,
01421                         nsIRDFResource * aType) 
01422 {
01423   PRUint32 count = aArray->Count();
01424   nsresult res = NS_OK;
01425 
01426   for (PRUint32 i = 0; i < count; i++) {
01427     nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(i);
01428     if (item == NULL) return NS_ERROR_UNEXPECTED;
01429 
01430     res = AddMenuItemToContainer(aContainer, item, aType, NULL, -1);
01431     if (NS_FAILED(res)) return res;
01432   }
01433 
01434   return NS_OK;
01435 }
01436 
01437 nsresult nsCharsetMenu::AddCharsetToContainer(nsVoidArray *aArray, 
01438                                               nsIRDFContainer * aContainer, 
01439                                               const nsAFlatCString& aCharset, 
01440                                               const char * aIDPrefix,
01441                                               PRInt32 aPlace,
01442                                           PRInt32 aRDFPlace)
01443 {
01444   nsresult res = NS_OK;
01445   nsMenuEntry * item = NULL; 
01446   
01447   res = AddCharsetToItemArray(aArray, aCharset, &item, aPlace);
01448   if (NS_FAILED(res)) goto done;
01449 
01450   res = AddMenuItemToContainer(aContainer, item, NULL, aIDPrefix, 
01451     aPlace + aRDFPlace);
01452   if (NS_FAILED(res)) goto done;
01453 
01454   // if we have made another reference to "item", do not delete it 
01455   if (aArray != NULL) item = NULL; 
01456 
01457 done:
01458   if (item != NULL) delete item;
01459 
01460   return res;
01461 }
01462 
01463 nsresult nsCharsetMenu::AddFromPrefsToMenu(
01464                         nsVoidArray * aArray, 
01465                         nsIRDFContainer * aContainer, 
01466                         const char * aKey, 
01467                         nsCStringArray& aDecs, 
01468                         const char * aIDPrefix)
01469 {
01470   nsresult res = NS_OK;
01471 
01472   nsCOMPtr<nsIPrefLocalizedString> pls;
01473   res = mPrefs->GetComplexValue(aKey, NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(pls));
01474   if (NS_FAILED(res)) return res;
01475 
01476   if (pls) {
01477     nsXPIDLString ucsval;
01478     pls->ToString(getter_Copies(ucsval));
01479     NS_ConvertUCS2toUTF8 utf8val(ucsval);
01480     if (ucsval)
01481       res = AddFromStringToMenu(utf8val.BeginWriting(), aArray,
01482                                 aContainer, aDecs, aIDPrefix);
01483   }
01484 
01485   return res;
01486 }
01487 
01488 nsresult
01489 nsCharsetMenu::AddFromNolocPrefsToMenu(nsVoidArray * aArray, 
01490                                        nsIRDFContainer * aContainer, 
01491                                        const char * aKey, 
01492                                        nsCStringArray& aDecs, 
01493                                        const char * aIDPrefix)
01494 {
01495   nsresult res = NS_OK;
01496 
01497   char * value = NULL;
01498   res = mPrefs->GetCharPref(aKey, &value);
01499   if (NS_FAILED(res)) return res;
01500 
01501   if (value != NULL) {
01502     res = AddFromStringToMenu(value, aArray, aContainer, aDecs, aIDPrefix);
01503     nsMemory::Free(value);
01504   }
01505 
01506   return res;
01507 }
01508 
01509 nsresult nsCharsetMenu::AddFromStringToMenu(
01510                         char * aCharsetList, 
01511                         nsVoidArray * aArray, 
01512                         nsIRDFContainer * aContainer, 
01513                         nsCStringArray& aDecs, 
01514                         const char * aIDPrefix)
01515 {
01516   nsresult res = NS_OK;
01517   char * p = aCharsetList;
01518   char * q = p;
01519   while (*p != 0) {
01520          for (; (*q != ',') && (*q != ' ') && (*q != 0); q++) {;}
01521     char temp = *q;
01522     *q = 0;
01523 
01524     // if this charset is not on the accepted list of charsets, ignore it
01525     PRInt32 index;
01526     index = aDecs.IndexOfIgnoreCase(nsCAutoString(p));
01527     if (index >= 0) {
01528 
01529       // else, add it to the menu
01530       res = AddCharsetToContainer(aArray, aContainer, nsDependentCString(p),
01531                                   aIDPrefix, -1, 0);
01532       NS_ASSERTION(NS_SUCCEEDED(res), "cannot add charset to menu");
01533       if (NS_FAILED(res)) break;
01534 
01535       res = aDecs.RemoveCStringAt(index);
01536       NS_ASSERTION(NS_SUCCEEDED(res), "cannot remove atom from array");
01537     }
01538 
01539     *q = temp;
01540     for (; (*q == ',') || (*q == ' '); q++) {;}
01541     p=q;
01542   }
01543 
01544   return NS_OK;
01545 }
01546 
01547 nsresult nsCharsetMenu::AddSeparatorToContainer(nsIRDFContainer * aContainer)
01548 {
01549   nsCAutoString str;
01550   str.AssignLiteral("----");
01551 
01552   // hack to generate unique id's for separators
01553   static PRInt32 u = 0;
01554   u++;
01555   str.AppendInt(u);
01556 
01557   nsMenuEntry item;
01558   item.mCharset = str;
01559   item.mTitle.AssignWithConversion(str.get());
01560 
01561   return AddMenuItemToContainer(aContainer, &item, kNC_BookmarkSeparator, 
01562     NULL, -1);
01563 }
01564 
01565 nsresult
01566 nsCharsetMenu::AddCharsetToCache(const nsAFlatCString& aCharset,
01567                                  nsVoidArray * aArray,
01568                                  nsIRDFResource * aRDFResource, 
01569                                  PRInt32 aCacheStart, 
01570                                  PRInt32 aCacheSize,
01571                                  PRInt32 aRDFPlace)
01572 {
01573   PRInt32 i;
01574   nsresult res = NS_OK;
01575 
01576   i = FindMenuItemInArray(aArray, aCharset, NULL);
01577   if (i >= 0) return res;
01578 
01579   nsCOMPtr<nsIRDFContainer> container;
01580   res = NewRDFContainer(mInner, aRDFResource, getter_AddRefs(container));
01581   if (NS_FAILED(res)) return res;
01582 
01583   // iff too many items, remove last one
01584   if (aArray->Count() - aCacheStart >= aCacheSize){
01585     res = RemoveLastMenuItem(container, aArray);
01586     if (NS_FAILED(res)) return res;
01587   }
01588 
01589   res = AddCharsetToContainer(aArray, container, aCharset, "charset.", 
01590                               aCacheStart, aRDFPlace);
01591 
01592   return res;
01593 }
01594 
01595 nsresult nsCharsetMenu::WriteCacheToPrefs(nsVoidArray * aArray, 
01596                                           PRInt32 aCacheStart, 
01597                                           const char * aKey)
01598 {
01599   nsresult res = NS_OK;
01600 
01601   // create together the cache string
01602   nsCAutoString cache;
01603   nsCAutoString sep(NS_LITERAL_CSTRING(", "));
01604   PRInt32 count = aArray->Count();
01605 
01606   for (PRInt32 i = aCacheStart; i < count; i++) {
01607     nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(i);
01608     if (item != NULL) {    
01609       cache.Append(item->mCharset);
01610       if (i < count - 1) {
01611         cache.Append(sep);
01612       }
01613     }
01614   }
01615 
01616   // write the pref
01617   res = mPrefs->SetCharPref(aKey, cache.get());
01618 
01619   return res;
01620 }
01621 
01622 nsresult nsCharsetMenu::UpdateCachePrefs(const char * aCacheKey,
01623                                          const char * aCacheSizeKey,
01624                                          const char * aStaticKey,
01625                                          const PRUnichar * aCharset)
01626 {
01627   nsresult rv = NS_OK;
01628   nsXPIDLCString cachePrefValue;
01629   nsXPIDLCString staticPrefValue;
01630   NS_LossyConvertUTF16toASCII currentCharset(aCharset);
01631   PRInt32 cacheSize = 0;
01632 
01633   mPrefs->GetCharPref(aCacheKey, getter_Copies(cachePrefValue));
01634   mPrefs->GetCharPref(aStaticKey, getter_Copies(staticPrefValue));
01635   rv = mPrefs->GetIntPref(aCacheSizeKey, &cacheSize);
01636 
01637   if (NS_FAILED(rv) || cacheSize <= 0)
01638     return NS_ERROR_UNEXPECTED;
01639 
01640   if ((cachePrefValue.Find(currentCharset) == kNotFound) && 
01641       (staticPrefValue.Find(currentCharset) == kNotFound)) {
01642 
01643     if (!cachePrefValue.IsEmpty())
01644       cachePrefValue.Insert(", ", 0);
01645 
01646     cachePrefValue.Insert(currentCharset, 0);
01647     if (cacheSize < (PRInt32) cachePrefValue.CountChar(',') + 1)
01648       cachePrefValue.Truncate(cachePrefValue.RFindChar(','));
01649 
01650     rv = mPrefs->SetCharPref(aCacheKey, cachePrefValue);
01651   }
01652 
01653   return rv;
01654 }
01655 
01656 nsresult nsCharsetMenu::ClearMenu(nsIRDFContainer * aContainer,  
01657                                   nsVoidArray * aArray)
01658 {
01659   nsresult res = NS_OK;
01660 
01661   // clean the RDF data source
01662   PRInt32 count = aArray->Count();
01663   for (PRInt32 i = 0; i < count; i++) {
01664     nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(i);
01665     if (item != NULL) {    
01666       res = AddMenuItemToContainer(aContainer, item, NULL, "charset.", -2);
01667       if (NS_FAILED(res)) return res;
01668     }
01669   }
01670 
01671   // clean the internal data structures
01672   FreeMenuItemArray(aArray);
01673 
01674   return res;
01675 }
01676 
01677 nsresult nsCharsetMenu::RemoveLastMenuItem(nsIRDFContainer * aContainer,
01678                                            nsVoidArray * aArray)
01679 {
01680   nsresult res = NS_OK;
01681 
01682   PRInt32 last = aArray->Count() - 1;
01683   if (last >= 0) {
01684     nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(last);
01685     if (item != NULL) {    
01686       res = AddMenuItemToContainer(aContainer, item, NULL, "charset.", -2);
01687       if (NS_FAILED(res)) return res;
01688 
01689       res = aArray->RemoveElementAt(last);
01690       if (NS_FAILED(res)) return res;
01691     }
01692   }
01693 
01694   return res;
01695 }
01696 
01697 nsresult nsCharsetMenu::RemoveFlaggedCharsets(nsCStringArray& aList, 
01698                                               const nsString& aProp)
01699 {
01700   nsresult res = NS_OK;
01701   PRUint32 count;
01702 
01703   count = aList.Count();
01704   if (NS_FAILED(res)) return res;
01705 
01706   nsCString* charset;
01707   nsAutoString str;
01708   for (PRUint32 i = 0; i < count; i++) {
01709 
01710     charset = aList.CStringAt(i);
01711     if (!charset) continue;
01712 
01713     res = mCCManager->GetCharsetData(charset->get(), aProp.get(), str);
01714     if (NS_FAILED(res)) continue;
01715 
01716     aList.RemoveCStringAt(i);
01717 
01718     i--; 
01719     count--;
01720   }
01721 
01722   return NS_OK;
01723 }
01724 
01725 nsresult nsCharsetMenu::NewRDFContainer(nsIRDFDataSource * aDataSource, 
01726                                         nsIRDFResource * aResource, 
01727                                         nsIRDFContainer ** aResult)
01728 {
01729   nsresult res = CallCreateInstance(kRDFContainerCID, aResult);
01730   if (NS_FAILED(res)) return res;
01731 
01732   res = (*aResult)->Init(aDataSource, aResource);
01733   if (NS_FAILED(res)) NS_RELEASE(*aResult);
01734 
01735   return res;
01736 }
01737 
01738 void nsCharsetMenu::FreeMenuItemArray(nsVoidArray * aArray)
01739 {
01740   PRUint32 count = aArray->Count();
01741   for (PRUint32 i = 0; i < count; i++) {
01742     nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(i);
01743     if (item != NULL) {
01744       delete item;
01745     }
01746   }
01747   aArray->Clear();
01748 }
01749 
01750 PRInt32 nsCharsetMenu::FindMenuItemInArray(const nsVoidArray* aArray, 
01751                                            const nsAFlatCString& aCharset, 
01752                                            nsMenuEntry ** aResult)
01753 {
01754   PRUint32 count = aArray->Count();
01755 
01756   for (PRUint32 i=0; i < count; i++) {
01757     nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(i);
01758     if (item->mCharset == aCharset) {
01759       if (aResult != NULL) *aResult = item;
01760       return i;
01761     }
01762   }
01763 
01764   if (aResult != NULL) *aResult = NULL;
01765   return -1;
01766 }
01767 
01768 nsresult nsCharsetMenu::ReorderMenuItemArray(nsVoidArray * aArray)
01769 {
01770   nsresult res = NS_OK;
01771   nsCOMPtr<nsICollation> collation;
01772   PRUint32 count = aArray->Count();
01773   PRUint32 i;
01774 
01775   // we need to use a temporary array
01776   charsetMenuSortRecord *array = new charsetMenuSortRecord [count];
01777   NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
01778   for (i = 0; i < count; i++)
01779     array[i].key = nsnull;
01780 
01781   res = GetCollation(getter_AddRefs(collation));
01782   if (NS_FAILED(res))
01783     goto done;
01784 
01785   for (i = 0; i < count && NS_SUCCEEDED(res); i++) {
01786     array[i].item = (nsMenuEntry *)aArray->ElementAt(i);
01787 
01788     res = collation->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive, 
01789                                        (array[i].item)->mTitle, &array[i].key, &array[i].len);
01790   }
01791 
01792   // reorder the array
01793   if (NS_SUCCEEDED(res)) {
01794     NS_QuickSort(array, count, sizeof(*array), CompareMenuItems, collation);
01795 
01796     // move the elements from the temporary array into the the real one
01797     aArray->Clear();
01798     for (i = 0; i < count; i++) {
01799       aArray->AppendElement(array[i].item);
01800     }
01801   }
01802 
01803 done:
01804   for (i = 0; i < count; i++) {
01805     PR_FREEIF(array[i].key);
01806   }
01807   delete [] array;
01808   return res;
01809 }
01810 
01811 nsresult nsCharsetMenu::GetCollation(nsICollation ** aCollation)
01812 {
01813   nsresult res = NS_OK;
01814   nsCOMPtr<nsILocale> locale = nsnull;
01815   nsICollationFactory * collationFactory = nsnull;
01816   
01817   nsCOMPtr<nsILocaleService> localeServ = 
01818            do_GetService(kLocaleServiceCID, &res);
01819   if (NS_FAILED(res)) return res;
01820 
01821   res = localeServ->GetApplicationLocale(getter_AddRefs(locale));
01822   if (NS_FAILED(res)) return res;
01823 
01824   res = CallCreateInstance(kCollationFactoryCID, &collationFactory);
01825   if (NS_FAILED(res)) return res;
01826 
01827   res = collationFactory->CreateCollation(locale, aCollation);
01828   NS_RELEASE(collationFactory);
01829   return res;
01830 }
01831 
01832 //----------------------------------------------------------------------------
01833 // Interface nsICurrentCharsetListener [implementation]
01834 
01835 NS_IMETHODIMP nsCharsetMenu::SetCurrentCharset(const PRUnichar * aCharset)
01836 {
01837   NS_TIMELINE_START_TIMER("nsCharsetMenu:SetCurrentCharset");
01838   nsresult res = NS_OK;
01839 
01840   if (mBrowserMenuInitialized) {
01841     // Don't add item to the cache if it's marked "notForBrowser"
01842     nsAutoString str;
01843     res = mCCManager->GetCharsetData(NS_LossyConvertUTF16toASCII(aCharset).get(),
01844                                      NS_LITERAL_STRING(".notForBrowser").get(), str);
01845     if (NS_SUCCEEDED(res)) // succeeded means attribute exists
01846       return res; // don't throw
01847 
01848     res = AddCharsetToCache(NS_LossyConvertUCS2toASCII(aCharset),
01849                             &mBrowserMenu, kNC_BrowserCharsetMenuRoot, 
01850                             mBrowserCacheStart, mBrowserCacheSize,
01851                             mBrowserMenuRDFPosition);
01852     if (NS_FAILED(res)) {
01853         NS_TIMELINE_LEAVE("nsCharsetMenu:SetCurrentCharset");
01854         return res;
01855     }
01856 
01857     res = WriteCacheToPrefs(&mBrowserMenu, mBrowserCacheStart, 
01858       kBrowserCachePrefKey);
01859   } else {
01860     res = UpdateCachePrefs(kBrowserCachePrefKey, kBrowserCacheSizePrefKey, 
01861                            kBrowserStaticPrefKey, aCharset);
01862   }
01863   NS_TIMELINE_STOP_TIMER("nsCharsetMenu:SetCurrentCharset");
01864   NS_TIMELINE_MARK_TIMER("nsCharsetMenu:SetCurrentCharset");
01865   return res;
01866 }
01867 
01868 NS_IMETHODIMP nsCharsetMenu::SetCurrentMailCharset(const PRUnichar * aCharset)
01869 {
01870   NS_TIMELINE_START_TIMER("nsCharsetMenu:SetCurrentMailCharset");
01871   nsresult res = NS_OK;
01872 
01873   if (mMailviewMenuInitialized) {
01874     res = AddCharsetToCache(NS_LossyConvertUCS2toASCII(aCharset),
01875                             &mMailviewMenu, kNC_MailviewCharsetMenuRoot, 
01876                             mMailviewCacheStart, mMailviewCacheSize,
01877                             mMailviewMenuRDFPosition);
01878     if (NS_FAILED(res)) return res;
01879 
01880     res = WriteCacheToPrefs(&mMailviewMenu, mMailviewCacheStart, 
01881                             kMailviewCachePrefKey);
01882   } else {
01883     res = UpdateCachePrefs(kMailviewCachePrefKey, kMailviewCacheSizePrefKey, 
01884                            kMailviewStaticPrefKey, aCharset);
01885   }
01886   NS_TIMELINE_STOP_TIMER("nsCharsetMenu:SetCurrentMailCharset");
01887   NS_TIMELINE_MARK_TIMER("nsCharsetMenu:SetCurrentMailCharset");
01888   return res;
01889 }
01890 
01891 NS_IMETHODIMP nsCharsetMenu::SetCurrentComposerCharset(const PRUnichar * aCharset)
01892 {
01893   NS_TIMELINE_START_TIMER("nsCharsetMenu:SetCurrentComposerCharset");
01894   nsresult res = NS_OK;
01895 
01896   if (mComposerMenuInitialized) {
01897 
01898     res = AddCharsetToCache(NS_LossyConvertUCS2toASCII(aCharset),
01899                             &mComposerMenu, kNC_ComposerCharsetMenuRoot, 
01900                             mComposerCacheStart, mComposerCacheSize,
01901                             mComposerMenuRDFPosition);
01902     if (NS_FAILED(res)) return res;
01903 
01904     res = WriteCacheToPrefs(&mComposerMenu, mComposerCacheStart, 
01905       kComposerCachePrefKey);
01906   } else {
01907     res = UpdateCachePrefs(kComposerCachePrefKey, kComposerCacheSizePrefKey, 
01908                            kComposerStaticPrefKey, aCharset);
01909   }
01910   NS_TIMELINE_STOP_TIMER("nsCharsetMenu:SetCurrentComposerCharset");
01911   NS_TIMELINE_MARK_TIMER("nsCharsetMenu:SetCurrentComposerCharset");
01912   return res;
01913 }
01914 
01915 //----------------------------------------------------------------------------
01916 // Interface nsIRDFDataSource [implementation]
01917 
01918 NS_IMETHODIMP nsCharsetMenu::GetURI(char ** uri)
01919 {
01920   if (!uri) return NS_ERROR_NULL_POINTER;
01921 
01922   *uri = nsCRT::strdup("rdf:charset-menu");
01923   if (!(*uri)) return NS_ERROR_OUT_OF_MEMORY;
01924 
01925   return NS_OK;
01926 }
01927 
01928 NS_IMETHODIMP nsCharsetMenu::GetSource(nsIRDFResource* property,
01929                                        nsIRDFNode* target,
01930                                        PRBool tv,
01931                                        nsIRDFResource** source)
01932 {
01933   return mInner->GetSource(property, target, tv, source);
01934 }
01935 
01936 NS_IMETHODIMP nsCharsetMenu::GetSources(nsIRDFResource* property,
01937                                         nsIRDFNode* target,
01938                                         PRBool tv,
01939                                         nsISimpleEnumerator** sources)
01940 {
01941   return mInner->GetSources(property, target, tv, sources);
01942 }
01943 
01944 NS_IMETHODIMP nsCharsetMenu::GetTarget(nsIRDFResource* source,
01945                                        nsIRDFResource* property,
01946                                        PRBool tv,
01947                                        nsIRDFNode** target)
01948 {
01949   return mInner->GetTarget(source, property, tv, target);
01950 }
01951 
01952 NS_IMETHODIMP nsCharsetMenu::GetTargets(nsIRDFResource* source,
01953                                         nsIRDFResource* property,
01954                                         PRBool tv,
01955                                         nsISimpleEnumerator** targets)
01956 {
01957   return mInner->GetTargets(source, property, tv, targets);
01958 }
01959 
01960 NS_IMETHODIMP nsCharsetMenu::Assert(nsIRDFResource* aSource,
01961                                     nsIRDFResource* aProperty,
01962                                     nsIRDFNode* aTarget,
01963                                     PRBool aTruthValue)
01964 {
01965   // TODO: filter out asserts we don't care about
01966   return mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
01967 }
01968 
01969 NS_IMETHODIMP nsCharsetMenu::Unassert(nsIRDFResource* aSource,
01970                                       nsIRDFResource* aProperty,
01971                                       nsIRDFNode* aTarget)
01972 {
01973   // TODO: filter out unasserts we don't care about
01974   return mInner->Unassert(aSource, aProperty, aTarget);
01975 }
01976 
01977 
01978 NS_IMETHODIMP nsCharsetMenu::Change(nsIRDFResource* aSource,
01979                                     nsIRDFResource* aProperty,
01980                                     nsIRDFNode* aOldTarget,
01981                                     nsIRDFNode* aNewTarget)
01982 {
01983   // TODO: filter out changes we don't care about
01984   return mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
01985 }
01986 
01987 NS_IMETHODIMP nsCharsetMenu::Move(nsIRDFResource* aOldSource,
01988                                   nsIRDFResource* aNewSource,
01989                                   nsIRDFResource* aProperty,
01990                                   nsIRDFNode* aTarget)
01991 {
01992   // TODO: filter out changes we don't care about
01993   return mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
01994 }
01995 
01996 
01997 NS_IMETHODIMP nsCharsetMenu::HasAssertion(nsIRDFResource* source, 
01998                                           nsIRDFResource* property, 
01999                                           nsIRDFNode* target, PRBool tv, 
02000                                           PRBool* hasAssertion)
02001 {
02002   return mInner->HasAssertion(source, property, target, tv, hasAssertion);
02003 }
02004 
02005 NS_IMETHODIMP nsCharsetMenu::AddObserver(nsIRDFObserver* n)
02006 {
02007   return mInner->AddObserver(n);
02008 }
02009 
02010 NS_IMETHODIMP nsCharsetMenu::RemoveObserver(nsIRDFObserver* n)
02011 {
02012   return mInner->RemoveObserver(n);
02013 }
02014 
02015 NS_IMETHODIMP 
02016 nsCharsetMenu::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, PRBool *result)
02017 {
02018   return mInner->HasArcIn(aNode, aArc, result);
02019 }
02020 
02021 NS_IMETHODIMP 
02022 nsCharsetMenu::HasArcOut(nsIRDFResource *source, nsIRDFResource *aArc, PRBool *result)
02023 {
02024   return mInner->HasArcOut(source, aArc, result);
02025 }
02026 
02027 NS_IMETHODIMP nsCharsetMenu::ArcLabelsIn(nsIRDFNode* node, 
02028                                          nsISimpleEnumerator** labels)
02029 {
02030   return mInner->ArcLabelsIn(node, labels);
02031 }
02032 
02033 NS_IMETHODIMP nsCharsetMenu::ArcLabelsOut(nsIRDFResource* source, 
02034                                           nsISimpleEnumerator** labels)
02035 {
02036   return mInner->ArcLabelsOut(source, labels);
02037 }
02038 
02039 NS_IMETHODIMP nsCharsetMenu::GetAllResources(nsISimpleEnumerator** aCursor)
02040 {
02041   return mInner->GetAllResources(aCursor);
02042 }
02043 
02044 NS_IMETHODIMP nsCharsetMenu::GetAllCmds(
02045                              nsIRDFResource* source,
02046                              nsISimpleEnumerator/*<nsIRDFResource>*/** commands)
02047 {
02048   NS_NOTYETIMPLEMENTED("write me!");
02049   return NS_ERROR_NOT_IMPLEMENTED;
02050 }
02051 
02052 NS_IMETHODIMP nsCharsetMenu::IsCommandEnabled(
02053                              nsISupportsArray/*<nsIRDFResource>*/* aSources,
02054                              nsIRDFResource*   aCommand,
02055                              nsISupportsArray/*<nsIRDFResource>*/* aArguments,
02056                              PRBool* aResult)
02057 {
02058   NS_NOTYETIMPLEMENTED("write me!");
02059   return NS_ERROR_NOT_IMPLEMENTED;
02060 }
02061 
02062 NS_IMETHODIMP nsCharsetMenu::DoCommand(nsISupportsArray* aSources,
02063                                        nsIRDFResource*   aCommand,
02064                                        nsISupportsArray* aArguments)
02065 {
02066   NS_NOTYETIMPLEMENTED("write me!");
02067   return NS_ERROR_NOT_IMPLEMENTED;
02068 }
02069 
02070 NS_IMETHODIMP nsCharsetMenu::BeginUpdateBatch()
02071 {
02072   return mInner->BeginUpdateBatch();
02073 }
02074 
02075 NS_IMETHODIMP nsCharsetMenu::EndUpdateBatch()
02076 {
02077   return mInner->EndUpdateBatch();
02078 }