Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / xpfe / components / intl / nsCharsetMenu.cpp
blob27596e5b48c410430c3d9022112d919caa60f62a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsIServiceManager.h"
40 #include "nsIComponentManager.h"
41 #include "rdf.h"
42 #include "nsIRDFDataSource.h"
43 #include "nsIRDFService.h"
44 #include "nsIRDFContainerUtils.h"
45 #include "nsRDFCID.h"
46 #include "nsXPIDLString.h"
47 #include "nsCharsetMenu.h"
48 #include "nsICharsetConverterManager.h"
49 #include "nsICollation.h"
50 #include "nsCollationCID.h"
51 #include "nsLocaleCID.h"
52 #include "nsIGenericFactory.h"
53 #include "nsILocaleService.h"
54 #include "nsIPrefService.h"
55 #include "nsIPrefBranch.h"
56 #include "nsIPrefBranch2.h"
57 #include "nsIPrefLocalizedString.h"
58 #include "nsICurrentCharsetListener.h"
59 #include "nsQuickSort.h"
60 #include "nsIObserver.h"
61 #include "nsStringEnumerator.h"
62 #include "nsVoidArray.h"
63 #include "nsIObserverService.h"
64 #include "nsIRequestObserver.h"
65 #include "nsITimelineService.h"
66 #include "nsCRT.h"
67 #include "prmem.h"
68 #include "nsCycleCollectionParticipant.h"
70 //----------------------------------------------------------------------------
71 // Global functions and data [declaration]
73 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
74 static NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID);
75 static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
76 static NS_DEFINE_CID(kRDFContainerCID, NS_RDFCONTAINER_CID);
78 static const char kURINC_BrowserAutodetMenuRoot[] = "NC:BrowserAutodetMenuRoot";
79 static const char kURINC_BrowserCharsetMenuRoot[] = "NC:BrowserCharsetMenuRoot";
80 static const char kURINC_BrowserMoreCharsetMenuRoot[] = "NC:BrowserMoreCharsetMenuRoot";
81 static const char kURINC_BrowserMore1CharsetMenuRoot[] = "NC:BrowserMore1CharsetMenuRoot";
82 static const char kURINC_BrowserMore2CharsetMenuRoot[] = "NC:BrowserMore2CharsetMenuRoot";
83 static const char kURINC_BrowserMore3CharsetMenuRoot[] = "NC:BrowserMore3CharsetMenuRoot";
84 static const char kURINC_BrowserMore4CharsetMenuRoot[] = "NC:BrowserMore4CharsetMenuRoot";
85 static const char kURINC_BrowserMore5CharsetMenuRoot[] = "NC:BrowserMore5CharsetMenuRoot";
86 static const char kURINC_BrowserUnicodeCharsetMenuRoot[] = "NC:BrowserUnicodeCharsetMenuRoot";
87 static const char kURINC_MaileditCharsetMenuRoot[] = "NC:MaileditCharsetMenuRoot";
88 static const char kURINC_MailviewCharsetMenuRoot[] = "NC:MailviewCharsetMenuRoot";
89 static const char kURINC_ComposerCharsetMenuRoot[] = "NC:ComposerCharsetMenuRoot";
90 static const char kURINC_DecodersRoot[] = "NC:DecodersRoot";
91 static const char kURINC_EncodersRoot[] = "NC:EncodersRoot";
92 DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, Name);
93 DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, Checked);
94 DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, BookmarkSeparator);
95 DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, CharsetDetector);
96 DEFINE_RDF_VOCAB(RDF_NAMESPACE_URI, NC, type);
98 // Note here that browser and mailview have the same static area and cache
99 // size but the cache itself is separate.
101 #define kBrowserStaticPrefKey "intl.charsetmenu.browser.static"
102 #define kBrowserCachePrefKey "intl.charsetmenu.browser.cache"
103 #define kBrowserCacheSizePrefKey "intl.charsetmenu.browser.cache.size"
105 #define kMailviewStaticPrefKey "intl.charsetmenu.browser.static"
106 #define kMailviewCachePrefKey "intl.charsetmenu.mailview.cache"
107 #define kMailviewCacheSizePrefKey "intl.charsetmenu.browser.cache.size"
109 #define kComposerStaticPrefKey "intl.charsetmenu.browser.static"
110 #define kComposerCachePrefKey "intl.charsetmenu.composer.cache"
111 #define kComposerCacheSizePrefKey "intl.charsetmenu.browser.cache.size"
113 #define kMaileditPrefKey "intl.charsetmenu.mailedit"
115 static void CloneCStringArray(const nsCStringArray& src, nsCStringArray& dest)
117 PRUint32 count = src.Count();
118 PRUint32 i;
119 for (i=0; i<count; i++) {
120 dest.AppendCString(*src.CStringAt(i));
124 //----------------------------------------------------------------------------
125 // Class nsMenuEntry [declaration]
128 * A little class holding all data needed for a menu item.
130 * @created 18/Apr/2000
131 * @author Catalin Rotaru [CATA]
133 class nsMenuEntry
135 public:
136 // memory & ref counting & leak prevention stuff
137 nsMenuEntry() { MOZ_COUNT_CTOR(nsMenuEntry); }
138 ~nsMenuEntry() { MOZ_COUNT_DTOR(nsMenuEntry); }
140 nsCAutoString mCharset;
141 nsAutoString mTitle;
144 //----------------------------------------------------------------------------
145 // Class nsCharsetMenu [declaration]
148 * The Charset Converter menu.
150 * God, our GUI programming disgusts me.
152 * @created 23/Nov/1999
153 * @author Catalin Rotaru [CATA]
155 class nsCharsetMenu : public nsIRDFDataSource, public nsICurrentCharsetListener
157 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
158 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCharsetMenu, nsIRDFDataSource)
160 private:
161 static nsIRDFResource * kNC_BrowserAutodetMenuRoot;
162 static nsIRDFResource * kNC_BrowserCharsetMenuRoot;
163 static nsIRDFResource * kNC_BrowserMoreCharsetMenuRoot;
164 static nsIRDFResource * kNC_BrowserMore1CharsetMenuRoot;
165 static nsIRDFResource * kNC_BrowserMore2CharsetMenuRoot;
166 static nsIRDFResource * kNC_BrowserMore3CharsetMenuRoot;
167 static nsIRDFResource * kNC_BrowserMore4CharsetMenuRoot;
168 static nsIRDFResource * kNC_BrowserMore5CharsetMenuRoot;
169 static nsIRDFResource * kNC_BrowserUnicodeCharsetMenuRoot;
170 static nsIRDFResource * kNC_MaileditCharsetMenuRoot;
171 static nsIRDFResource * kNC_MailviewCharsetMenuRoot;
172 static nsIRDFResource * kNC_ComposerCharsetMenuRoot;
173 static nsIRDFResource * kNC_DecodersRoot;
174 static nsIRDFResource * kNC_EncodersRoot;
175 static nsIRDFResource * kNC_Name;
176 static nsIRDFResource * kNC_Checked;
177 static nsIRDFResource * kNC_CharsetDetector;
178 static nsIRDFResource * kNC_BookmarkSeparator;
179 static nsIRDFResource * kRDF_type;
181 static nsIRDFDataSource * mInner;
183 PRPackedBool mInitialized;
184 PRPackedBool mBrowserMenuInitialized;
185 PRPackedBool mMailviewMenuInitialized;
186 PRPackedBool mComposerMenuInitialized;
187 PRPackedBool mMaileditMenuInitialized;
188 PRPackedBool mSecondaryTiersInitialized;
189 PRPackedBool mAutoDetectInitialized;
190 PRPackedBool mOthersInitialized;
192 nsVoidArray mBrowserMenu;
193 PRInt32 mBrowserCacheStart;
194 PRInt32 mBrowserCacheSize;
195 PRInt32 mBrowserMenuRDFPosition;
197 nsVoidArray mMailviewMenu;
198 PRInt32 mMailviewCacheStart;
199 PRInt32 mMailviewCacheSize;
200 PRInt32 mMailviewMenuRDFPosition;
202 nsVoidArray mComposerMenu;
203 PRInt32 mComposerCacheStart;
204 PRInt32 mComposerCacheSize;
205 PRInt32 mComposerMenuRDFPosition;
207 nsCOMPtr<nsIRDFService> mRDFService;
208 nsCOMPtr<nsICharsetConverterManager> mCCManager;
209 nsCOMPtr<nsIPrefBranch> mPrefs;
210 nsCOMPtr<nsIObserver> mCharsetMenuObserver;
211 nsCStringArray mDecoderList;
213 nsresult Done();
214 nsresult SetCharsetCheckmark(nsString * aCharset, PRBool aValue);
216 nsresult FreeResources();
218 nsresult InitStaticMenu(nsCStringArray& aDecs,
219 nsIRDFResource * aResource,
220 const char * aKey,
221 nsVoidArray * aArray);
222 nsresult InitCacheMenu(nsCStringArray& aDecs,
223 nsIRDFResource * aResource,
224 const char * aKey,
225 nsVoidArray * aArray);
227 nsresult InitMoreMenu(nsCStringArray& aDecs,
228 nsIRDFResource * aResource,
229 const char * aFlag);
231 nsresult InitMoreSubmenus(nsCStringArray& aDecs);
233 static nsresult SetArrayFromEnumerator(nsIUTF8StringEnumerator* aEnumerator,
234 nsCStringArray& aArray);
236 nsresult AddCharsetToItemArray(nsVoidArray* aArray,
237 const nsAFlatCString& aCharset,
238 nsMenuEntry ** aResult,
239 PRInt32 aPlace);
240 nsresult AddCharsetArrayToItemArray(nsVoidArray &aArray,
241 const nsCStringArray& aCharsets);
242 nsresult AddMenuItemToContainer(nsIRDFContainer * aContainer,
243 nsMenuEntry * aItem, nsIRDFResource * aType, const char * aIDPrefix,
244 PRInt32 aPlace);
245 nsresult AddMenuItemArrayToContainer(nsIRDFContainer * aContainer,
246 nsVoidArray * aArray, nsIRDFResource * aType);
247 nsresult AddCharsetToContainer(nsVoidArray * aArray,
248 nsIRDFContainer * aContainer,
249 const nsAFlatCString& aCharset,
250 const char * aIDPrefix,
251 PRInt32 aPlace, PRInt32 aRDFPlace);
253 nsresult AddFromPrefsToMenu(nsVoidArray * aArray,
254 nsIRDFContainer * aContainer,
255 const char * aKey,
256 nsCStringArray& aDecs,
257 const char * aIDPrefix);
258 nsresult AddFromNolocPrefsToMenu(nsVoidArray * aArray,
259 nsIRDFContainer * aContainer,
260 const char * aKey,
261 nsCStringArray& aDecs,
262 const char * aIDPrefix);
263 nsresult AddFromStringToMenu(char * aCharsetList,
264 nsVoidArray * aArray,
265 nsIRDFContainer * aContainer,
266 nsCStringArray& aDecs,
267 const char * aIDPrefix);
269 nsresult AddSeparatorToContainer(nsIRDFContainer * aContainer);
270 nsresult AddCharsetToCache(const nsAFlatCString& aCharset,
271 nsVoidArray * aArray,
272 nsIRDFResource * aRDFResource,
273 PRInt32 aCacheStart, PRInt32 aCacheSize,
274 PRInt32 aRDFPlace);
276 nsresult WriteCacheToPrefs(nsVoidArray * aArray, PRInt32 aCacheStart,
277 const char * aKey);
278 nsresult UpdateCachePrefs(const char * aCacheKey, const char * aCacheSizeKey,
279 const char * aStaticKey, const PRUnichar * aCharset);
281 nsresult ClearMenu(nsIRDFContainer * aContainer, nsVoidArray * aArray);
282 nsresult RemoveLastMenuItem(nsIRDFContainer * aContainer,
283 nsVoidArray * aArray);
285 nsresult RemoveFlaggedCharsets(nsCStringArray& aList, const nsString& aProp);
286 nsresult NewRDFContainer(nsIRDFDataSource * aDataSource,
287 nsIRDFResource * aResource, nsIRDFContainer ** aResult);
288 void FreeMenuItemArray(nsVoidArray * aArray);
289 PRInt32 FindMenuItemInArray(const nsVoidArray* aArray,
290 const nsAFlatCString& aCharset,
291 nsMenuEntry ** aResult);
292 nsresult ReorderMenuItemArray(nsVoidArray * aArray);
293 nsresult GetCollation(nsICollation ** aCollation);
295 public:
296 nsCharsetMenu();
297 virtual ~nsCharsetMenu();
299 nsresult Init();
300 nsresult InitBrowserMenu();
301 nsresult InitMaileditMenu();
302 nsresult InitMailviewMenu();
303 nsresult InitComposerMenu();
304 nsresult InitOthers();
305 nsresult InitSecondaryTiers();
306 nsresult InitAutodetMenu();
307 nsresult RefreshBrowserMenu();
308 nsresult RefreshMailviewMenu();
309 nsresult RefreshMaileditMenu();
310 nsresult RefreshComposerMenu();
312 //--------------------------------------------------------------------------
313 // Interface nsICurrentCharsetListener [declaration]
315 NS_IMETHOD SetCurrentCharset(const PRUnichar * aCharset);
316 NS_IMETHOD SetCurrentMailCharset(const PRUnichar * aCharset);
317 NS_IMETHOD SetCurrentComposerCharset(const PRUnichar * aCharset);
319 //--------------------------------------------------------------------------
320 // Interface nsIRDFDataSource [declaration]
322 NS_DECL_NSIRDFDATASOURCE
325 //----------------------------------------------------------------------------
326 // Global functions and data [implementation]
328 NS_IMETHODIMP NS_NewCharsetMenu(nsISupports * aOuter, const nsIID & aIID,
329 void ** aResult)
331 if (!aResult) {
332 return NS_ERROR_NULL_POINTER;
334 if (aOuter) {
335 *aResult = nsnull;
336 return NS_ERROR_NO_AGGREGATION;
338 nsCharsetMenu* inst = new nsCharsetMenu();
339 if (!inst) {
340 *aResult = nsnull;
341 return NS_ERROR_OUT_OF_MEMORY;
343 nsresult res = inst->QueryInterface(aIID, aResult);
344 if (NS_FAILED(res)) {
345 *aResult = nsnull;
346 delete inst;
348 return res;
351 struct charsetMenuSortRecord {
352 nsMenuEntry* item;
353 PRUint8* key;
354 PRUint32 len;
358 static int CompareMenuItems(const void* aArg1, const void* aArg2, void *data)
360 PRInt32 res;
361 nsICollation * collation = (nsICollation *) data;
362 charsetMenuSortRecord *rec1 = (charsetMenuSortRecord *) aArg1;
363 charsetMenuSortRecord *rec2 = (charsetMenuSortRecord *) aArg2;
365 collation->CompareRawSortKey(rec1->key, rec1->len, rec2->key, rec2->len, &res);
367 return res;
370 nsresult
371 nsCharsetMenu::SetArrayFromEnumerator(nsIUTF8StringEnumerator* aEnumerator,
372 nsCStringArray& aArray)
374 nsresult rv;
376 PRBool hasMore;
377 rv = aEnumerator->HasMore(&hasMore);
379 nsCAutoString value;
380 while (NS_SUCCEEDED(rv) && hasMore) {
381 rv = aEnumerator->GetNext(value);
382 if (NS_SUCCEEDED(rv))
383 aArray.AppendCString(value);
385 rv = aEnumerator->HasMore(&hasMore);
388 return rv;
391 //----------------------------------------------------------------------------
392 // Class nsCharsetMenuObserver
394 class nsCharsetMenuObserver : public nsIObserver {
396 public:
397 NS_DECL_ISUPPORTS
398 NS_DECL_NSIOBSERVER
400 nsCharsetMenuObserver(nsCharsetMenu * menu)
401 : mCharsetMenu(menu)
405 virtual ~nsCharsetMenuObserver() {}
407 private:
408 nsCharsetMenu* mCharsetMenu;
411 NS_IMPL_ISUPPORTS1(nsCharsetMenuObserver, nsIObserver)
413 NS_IMETHODIMP nsCharsetMenuObserver::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
415 NS_TIMELINE_START_TIMER("nsCharsetMenu:Observe");
416 nsresult rv = NS_OK;
418 //XUL event handler
419 if (!nsCRT::strcmp(aTopic, "charsetmenu-selected")) {
420 nsDependentString nodeName(someData);
421 rv = mCharsetMenu->Init();
422 if (nodeName.EqualsLiteral("browser")) {
423 rv = mCharsetMenu->InitBrowserMenu();
425 if (nodeName.EqualsLiteral("composer")) {
426 rv = mCharsetMenu->InitComposerMenu();
428 if (nodeName.EqualsLiteral("mailview")) {
429 rv = mCharsetMenu->InitMailviewMenu();
431 if (nodeName.EqualsLiteral("mailedit")) {
432 rv = mCharsetMenu->InitMaileditMenu();
433 rv = mCharsetMenu->InitOthers();
435 if (nodeName.EqualsLiteral("more-menu")) {
436 rv = mCharsetMenu->InitSecondaryTiers();
437 rv = mCharsetMenu->InitAutodetMenu();
439 if (nodeName.EqualsLiteral("other")) {
440 rv = mCharsetMenu->InitOthers();
441 rv = mCharsetMenu->InitMaileditMenu();
445 //pref event handler
446 if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
447 nsDependentString prefName(someData);
449 if (prefName.EqualsLiteral(kBrowserStaticPrefKey)) {
450 // refresh menus which share this pref
451 rv = mCharsetMenu->RefreshBrowserMenu();
452 NS_ENSURE_SUCCESS(rv, rv);
453 rv = mCharsetMenu->RefreshMailviewMenu();
454 NS_ENSURE_SUCCESS(rv, rv);
455 rv = mCharsetMenu->RefreshComposerMenu();
457 else if (prefName.EqualsLiteral(kMaileditPrefKey)) {
458 rv = mCharsetMenu->RefreshMaileditMenu();
462 NS_TIMELINE_STOP_TIMER("nsCharsetMenu:Observe");
463 NS_TIMELINE_MARK_TIMER("nsCharsetMenu:Observe");
464 return rv;
467 //----------------------------------------------------------------------------
468 // Class nsCharsetMenu [implementation]
470 NS_IMPL_CYCLE_COLLECTION_CLASS(nsCharsetMenu)
471 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsCharsetMenu)
472 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCharsetMenu)
473 cb.NoteXPCOMChild(nsCharsetMenu::mInner);
474 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
476 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsCharsetMenu, nsIRDFDataSource)
477 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsCharsetMenu, nsIRDFDataSource)
478 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCharsetMenu)
479 NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
480 NS_INTERFACE_MAP_ENTRY(nsICurrentCharsetListener)
481 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRDFDataSource)
482 NS_INTERFACE_MAP_END
484 nsIRDFDataSource * nsCharsetMenu::mInner = NULL;
485 nsIRDFResource * nsCharsetMenu::kNC_BrowserAutodetMenuRoot = NULL;
486 nsIRDFResource * nsCharsetMenu::kNC_BrowserCharsetMenuRoot = NULL;
487 nsIRDFResource * nsCharsetMenu::kNC_BrowserMoreCharsetMenuRoot = NULL;
488 nsIRDFResource * nsCharsetMenu::kNC_BrowserMore1CharsetMenuRoot = NULL;
489 nsIRDFResource * nsCharsetMenu::kNC_BrowserMore2CharsetMenuRoot = NULL;
490 nsIRDFResource * nsCharsetMenu::kNC_BrowserMore3CharsetMenuRoot = NULL;
491 nsIRDFResource * nsCharsetMenu::kNC_BrowserMore4CharsetMenuRoot = NULL;
492 nsIRDFResource * nsCharsetMenu::kNC_BrowserMore5CharsetMenuRoot = NULL;
493 nsIRDFResource * nsCharsetMenu::kNC_BrowserUnicodeCharsetMenuRoot = NULL;
494 nsIRDFResource * nsCharsetMenu::kNC_MaileditCharsetMenuRoot = NULL;
495 nsIRDFResource * nsCharsetMenu::kNC_MailviewCharsetMenuRoot = NULL;
496 nsIRDFResource * nsCharsetMenu::kNC_ComposerCharsetMenuRoot = NULL;
497 nsIRDFResource * nsCharsetMenu::kNC_DecodersRoot = NULL;
498 nsIRDFResource * nsCharsetMenu::kNC_EncodersRoot = NULL;
499 nsIRDFResource * nsCharsetMenu::kNC_Name = NULL;
500 nsIRDFResource * nsCharsetMenu::kNC_Checked = NULL;
501 nsIRDFResource * nsCharsetMenu::kNC_CharsetDetector = NULL;
502 nsIRDFResource * nsCharsetMenu::kNC_BookmarkSeparator = NULL;
503 nsIRDFResource * nsCharsetMenu::kRDF_type = NULL;
505 nsCharsetMenu::nsCharsetMenu()
506 : mInitialized(PR_FALSE),
507 mBrowserMenuInitialized(PR_FALSE),
508 mMailviewMenuInitialized(PR_FALSE),
509 mComposerMenuInitialized(PR_FALSE),
510 mMaileditMenuInitialized(PR_FALSE),
511 mSecondaryTiersInitialized(PR_FALSE),
512 mAutoDetectInitialized(PR_FALSE),
513 mOthersInitialized(PR_FALSE)
515 NS_TIMELINE_START_TIMER("nsCharsetMenu::nsCharsetMenu");
516 nsresult res = NS_OK;
518 //get charset manager
519 mCCManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res);
521 //initialize skeleton RDF source
522 mRDFService = do_GetService(kRDFServiceCID, &res);
524 if (NS_SUCCEEDED(res)) {
525 mRDFService->RegisterDataSource(this, PR_FALSE);
527 CallCreateInstance(kRDFInMemoryDataSourceCID, &mInner);
529 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserCharsetMenuRoot),
530 &kNC_BrowserCharsetMenuRoot);
533 //get pref service
534 nsCOMPtr<nsIPrefService> PrefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &res);
535 if (NS_SUCCEEDED(res))
536 res = PrefService->GetBranch(nsnull, getter_AddRefs(mPrefs));
538 //register event listener
539 mCharsetMenuObserver = new nsCharsetMenuObserver(this);
541 if (mCharsetMenuObserver) {
542 nsCOMPtr<nsIObserverService> observerService =
543 do_GetService("@mozilla.org/observer-service;1", &res);
545 if (NS_SUCCEEDED(res))
546 res = observerService->AddObserver(mCharsetMenuObserver,
547 "charsetmenu-selected",
548 PR_FALSE);
551 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to initialize nsCharsetMenu");
552 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::nsCharsetMenu");
553 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::nsCharsetMenu");
556 nsCharsetMenu::~nsCharsetMenu()
558 Done();
560 FreeMenuItemArray(&mBrowserMenu);
561 FreeMenuItemArray(&mMailviewMenu);
562 FreeMenuItemArray(&mComposerMenu);
564 FreeResources();
567 // XXX collapse these 2 in one
569 nsresult nsCharsetMenu::RefreshBrowserMenu()
571 nsresult res = NS_OK;
573 nsCOMPtr<nsIRDFContainer> container;
574 res = NewRDFContainer(mInner, kNC_BrowserCharsetMenuRoot, getter_AddRefs(container));
575 if (NS_FAILED(res)) return res;
577 // clean the menu
578 res = ClearMenu(container, &mBrowserMenu);
579 if (NS_FAILED(res)) return res;
581 // rebuild the menu
582 nsCOMPtr<nsIUTF8StringEnumerator> decoders;
583 res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
584 if (NS_FAILED(res)) return res;
586 nsCStringArray decs;
587 SetArrayFromEnumerator(decoders, decs);
589 res = AddFromPrefsToMenu(&mBrowserMenu, container, kBrowserStaticPrefKey,
590 decs, "charset.");
591 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
593 // mark the end of the static area, the rest is cache
594 mBrowserCacheStart = mBrowserMenu.Count();
596 // Remove "notForBrowser" entries before populating cache menu
597 res = RemoveFlaggedCharsets(decs, NS_LITERAL_STRING(".notForBrowser"));
598 NS_ASSERTION(NS_SUCCEEDED(res), "error removing flagged charsets");
600 res = InitCacheMenu(decs, kNC_BrowserCharsetMenuRoot, kBrowserCachePrefKey,
601 &mBrowserMenu);
602 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing browser cache charset menu");
604 return res;
607 nsresult nsCharsetMenu::RefreshMailviewMenu()
609 nsresult res = NS_OK;
611 nsCOMPtr<nsIRDFContainer> container;
612 res = NewRDFContainer(mInner, kNC_MailviewCharsetMenuRoot, getter_AddRefs(container));
613 if (NS_FAILED(res)) return res;
615 // clean the menu
616 res = ClearMenu(container, &mMailviewMenu);
617 if (NS_FAILED(res)) return res;
619 nsCOMPtr<nsIUTF8StringEnumerator> decoders;
620 res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
621 if (NS_FAILED(res)) return res;
623 nsCStringArray decs;
624 SetArrayFromEnumerator(decoders, decs);
626 res = AddFromPrefsToMenu(&mMailviewMenu, container, kMailviewStaticPrefKey,
627 decs, "charset.");
628 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
630 // mark the end of the static area, the rest is cache
631 mMailviewCacheStart = mMailviewMenu.Count();
633 res = InitCacheMenu(decs, kNC_MailviewCharsetMenuRoot,
634 kMailviewCachePrefKey, &mMailviewMenu);
635 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailview cache charset menu");
637 return res;
640 nsresult nsCharsetMenu::RefreshMaileditMenu()
642 nsresult res;
644 nsCOMPtr<nsIRDFContainer> container;
645 res = NewRDFContainer(mInner, kNC_MaileditCharsetMenuRoot, getter_AddRefs(container));
646 NS_ENSURE_SUCCESS(res, res);
648 nsCOMPtr<nsISimpleEnumerator> enumerator;
649 res = container->GetElements(getter_AddRefs(enumerator));
650 NS_ENSURE_SUCCESS(res, res);
652 // clear the menu
653 nsCOMPtr<nsIRDFNode> node;
654 while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(node)))) {
656 res = mInner->Unassert(kNC_MaileditCharsetMenuRoot, kNC_Name, node);
657 NS_ENSURE_SUCCESS(res, res);
659 res = container->RemoveElement(node, PR_FALSE);
660 NS_ENSURE_SUCCESS(res, res);
663 // get a list of available encoders
664 nsCOMPtr<nsIUTF8StringEnumerator> encoders;
665 res = mCCManager->GetEncoderList(getter_AddRefs(encoders));
666 NS_ENSURE_SUCCESS(res, res);
668 nsCStringArray encs;
669 SetArrayFromEnumerator(encoders, encs);
671 // add menu items from pref
672 res = AddFromPrefsToMenu(NULL, container, kMaileditPrefKey, encs, NULL);
673 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailedit charset menu from prefs");
675 return res;
678 nsresult nsCharsetMenu::RefreshComposerMenu()
680 nsresult res = NS_OK;
682 nsCOMPtr<nsIRDFContainer> container;
683 res = NewRDFContainer(mInner, kNC_ComposerCharsetMenuRoot, getter_AddRefs(container));
684 if (NS_FAILED(res)) return res;
686 // clean the menu
687 res = ClearMenu(container, &mComposerMenu);
688 if (NS_FAILED(res)) return res;
690 nsCOMPtr<nsIUTF8StringEnumerator> decoders;
691 res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
692 if (NS_FAILED(res)) return res;
694 nsCStringArray decs;
695 SetArrayFromEnumerator(decoders, decs);
697 res = AddFromPrefsToMenu(&mComposerMenu, container, kComposerStaticPrefKey,
698 decs, "charset.");
699 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
701 // mark the end of the static area, the rest is cache
702 mComposerCacheStart = mComposerMenu.Count();
704 res = InitCacheMenu(decs, kNC_ComposerCharsetMenuRoot,
705 kComposerCachePrefKey, &mComposerMenu);
706 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing composer cache charset menu");
708 return res;
711 nsresult nsCharsetMenu::Init()
713 nsresult res = NS_OK;
715 if (!mInitialized) {
717 //enumerate decoders
718 nsCOMPtr<nsIUTF8StringEnumerator> decoders;
719 res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
720 if (NS_FAILED(res)) return res;
722 SetArrayFromEnumerator(decoders, mDecoderList);
724 //initialize all remaining RDF template nodes
725 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserAutodetMenuRoot),
726 &kNC_BrowserAutodetMenuRoot);
727 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMoreCharsetMenuRoot),
728 &kNC_BrowserMoreCharsetMenuRoot);
729 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore1CharsetMenuRoot),
730 &kNC_BrowserMore1CharsetMenuRoot);
731 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore2CharsetMenuRoot),
732 &kNC_BrowserMore2CharsetMenuRoot);
733 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore3CharsetMenuRoot),
734 &kNC_BrowserMore3CharsetMenuRoot);
735 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore4CharsetMenuRoot),
736 &kNC_BrowserMore4CharsetMenuRoot);
737 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore5CharsetMenuRoot),
738 &kNC_BrowserMore5CharsetMenuRoot);
739 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserUnicodeCharsetMenuRoot),
740 &kNC_BrowserUnicodeCharsetMenuRoot);
741 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_MaileditCharsetMenuRoot),
742 &kNC_MaileditCharsetMenuRoot);
743 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_MailviewCharsetMenuRoot),
744 &kNC_MailviewCharsetMenuRoot);
745 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_ComposerCharsetMenuRoot),
746 &kNC_ComposerCharsetMenuRoot);
747 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_DecodersRoot),
748 &kNC_DecodersRoot);
749 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_EncodersRoot),
750 &kNC_EncodersRoot);
751 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_Name),
752 &kNC_Name);
753 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_Checked),
754 &kNC_Checked);
755 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_CharsetDetector),
756 &kNC_CharsetDetector);
757 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BookmarkSeparator),
758 &kNC_BookmarkSeparator);
759 mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_type), &kRDF_type);
761 nsIRDFContainerUtils * rdfUtil = NULL;
762 res = CallGetService(kRDFContainerUtilsCID, &rdfUtil);
763 if (NS_FAILED(res)) goto done;
765 res = rdfUtil->MakeSeq(mInner, kNC_BrowserAutodetMenuRoot, NULL);
766 if (NS_FAILED(res)) goto done;
767 res = rdfUtil->MakeSeq(mInner, kNC_BrowserCharsetMenuRoot, NULL);
768 if (NS_FAILED(res)) goto done;
769 res = rdfUtil->MakeSeq(mInner, kNC_BrowserMoreCharsetMenuRoot, NULL);
770 if (NS_FAILED(res)) goto done;
771 res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore1CharsetMenuRoot, NULL);
772 if (NS_FAILED(res)) goto done;
773 res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore2CharsetMenuRoot, NULL);
774 if (NS_FAILED(res)) goto done;
775 res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore3CharsetMenuRoot, NULL);
776 if (NS_FAILED(res)) goto done;
777 res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore4CharsetMenuRoot, NULL);
778 if (NS_FAILED(res)) goto done;
779 res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore5CharsetMenuRoot, NULL);
780 if (NS_FAILED(res)) goto done;
781 res = rdfUtil->MakeSeq(mInner, kNC_BrowserUnicodeCharsetMenuRoot, NULL);
782 if (NS_FAILED(res)) goto done;
783 res = rdfUtil->MakeSeq(mInner, kNC_MaileditCharsetMenuRoot, NULL);
784 if (NS_FAILED(res)) goto done;
785 res = rdfUtil->MakeSeq(mInner, kNC_MailviewCharsetMenuRoot, NULL);
786 if (NS_FAILED(res)) goto done;
787 res = rdfUtil->MakeSeq(mInner, kNC_ComposerCharsetMenuRoot, NULL);
788 if (NS_FAILED(res)) goto done;
789 res = rdfUtil->MakeSeq(mInner, kNC_DecodersRoot, NULL);
790 if (NS_FAILED(res)) goto done;
791 res = rdfUtil->MakeSeq(mInner, kNC_EncodersRoot, NULL);
792 if (NS_FAILED(res)) goto done;
794 done:
795 NS_IF_RELEASE(rdfUtil);
796 if (NS_FAILED(res)) return res;
798 mInitialized = NS_SUCCEEDED(res);
799 return res;
802 nsresult nsCharsetMenu::Done()
804 nsresult res = NS_OK;
805 res = mRDFService->UnregisterDataSource(this);
807 NS_IF_RELEASE(kNC_BrowserAutodetMenuRoot);
808 NS_IF_RELEASE(kNC_BrowserCharsetMenuRoot);
809 NS_IF_RELEASE(kNC_BrowserMoreCharsetMenuRoot);
810 NS_IF_RELEASE(kNC_BrowserMore1CharsetMenuRoot);
811 NS_IF_RELEASE(kNC_BrowserMore2CharsetMenuRoot);
812 NS_IF_RELEASE(kNC_BrowserMore3CharsetMenuRoot);
813 NS_IF_RELEASE(kNC_BrowserMore4CharsetMenuRoot);
814 NS_IF_RELEASE(kNC_BrowserMore5CharsetMenuRoot);
815 NS_IF_RELEASE(kNC_BrowserUnicodeCharsetMenuRoot);
816 NS_IF_RELEASE(kNC_MaileditCharsetMenuRoot);
817 NS_IF_RELEASE(kNC_MailviewCharsetMenuRoot);
818 NS_IF_RELEASE(kNC_ComposerCharsetMenuRoot);
819 NS_IF_RELEASE(kNC_DecodersRoot);
820 NS_IF_RELEASE(kNC_EncodersRoot);
821 NS_IF_RELEASE(kNC_Name);
822 NS_IF_RELEASE(kNC_Checked);
823 NS_IF_RELEASE(kNC_CharsetDetector);
824 NS_IF_RELEASE(kNC_BookmarkSeparator);
825 NS_IF_RELEASE(kRDF_type);
826 NS_IF_RELEASE(mInner);
828 return res;
831 nsresult nsCharsetMenu::SetCharsetCheckmark(nsString * aCharset,
832 PRBool aValue)
834 nsresult res = NS_OK;
835 nsCOMPtr<nsIRDFContainer> container;
836 nsCOMPtr<nsIRDFResource> node;
838 res = NewRDFContainer(mInner, kNC_BrowserCharsetMenuRoot, getter_AddRefs(container));
839 if (NS_FAILED(res)) return res;
841 // find RDF node for given charset
842 res = mRDFService->GetUnicodeResource(*aCharset, getter_AddRefs(node));
843 if (NS_FAILED(res)) return res;
845 // set checkmark value
846 nsCOMPtr<nsIRDFLiteral> checkedLiteral;
847 nsAutoString checked; checked.AssignWithConversion((aValue == PR_TRUE) ? "true" : "false");
848 res = mRDFService->GetLiteral(checked.get(), getter_AddRefs(checkedLiteral));
849 if (NS_FAILED(res)) return res;
850 res = Assert(node, kNC_Checked, checkedLiteral, PR_TRUE);
851 if (NS_FAILED(res)) return res;
853 return res;
857 * Free the resources no longer needed by the component.
859 nsresult nsCharsetMenu::FreeResources()
861 nsresult res = NS_OK;
863 if (mCharsetMenuObserver) {
864 nsCOMPtr<nsIPrefBranch2> pbi = do_QueryInterface(mPrefs);
865 if (pbi) {
866 pbi->RemoveObserver(kBrowserStaticPrefKey, mCharsetMenuObserver);
867 pbi->RemoveObserver(kMaileditPrefKey, mCharsetMenuObserver);
869 /* nsIObserverService has to have released nsCharsetMenu already */
872 mRDFService = NULL;
873 mCCManager = NULL;
874 mPrefs = NULL;
876 return res;
879 nsresult nsCharsetMenu::InitBrowserMenu()
881 NS_TIMELINE_START_TIMER("nsCharsetMenu::InitBrowserMenu");
883 nsresult res = NS_OK;
885 if (!mBrowserMenuInitialized) {
886 nsCOMPtr<nsIRDFContainer> container;
887 res = NewRDFContainer(mInner, kNC_BrowserCharsetMenuRoot, getter_AddRefs(container));
888 if (NS_FAILED(res)) return res;
891 // how to clone mDecoderList??
892 nsCStringArray browserDecoderList;
893 CloneCStringArray(mDecoderList, browserDecoderList);
895 res = InitStaticMenu(browserDecoderList, kNC_BrowserCharsetMenuRoot,
896 kBrowserStaticPrefKey, &mBrowserMenu);
897 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing browser static charset menu");
899 // mark the end of the static area, the rest is cache
900 mBrowserCacheStart = mBrowserMenu.Count();
901 mPrefs->GetIntPref(kBrowserCacheSizePrefKey, &mBrowserCacheSize);
903 // compute the position of the menu in the RDF container
904 res = container->GetCount(&mBrowserMenuRDFPosition);
905 if (NS_FAILED(res)) return res;
906 // this "1" here is a correction necessary because the RDF container
907 // elements are numbered from 1 (why god, WHY?!?!?!)
908 mBrowserMenuRDFPosition -= mBrowserCacheStart - 1;
910 // Remove "notForBrowser" entries before populating cache menu
911 res = RemoveFlaggedCharsets(browserDecoderList, NS_LITERAL_STRING(".notForBrowser"));
912 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
914 res = InitCacheMenu(browserDecoderList, kNC_BrowserCharsetMenuRoot, kBrowserCachePrefKey,
915 &mBrowserMenu);
916 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing browser cache charset menu");
918 // register prefs callback
919 nsCOMPtr<nsIPrefBranch2> pbi = do_QueryInterface(mPrefs);
920 if (pbi)
921 res = pbi->AddObserver(kBrowserStaticPrefKey, mCharsetMenuObserver, PR_FALSE);
924 mBrowserMenuInitialized = NS_SUCCEEDED(res);
926 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitBrowserMenu");
927 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitBrowserMenu");
929 return res;
932 nsresult nsCharsetMenu::InitMaileditMenu()
934 NS_TIMELINE_START_TIMER("nsCharsetMenu::InitMaileditMenu");
936 nsresult res = NS_OK;
938 if (!mMaileditMenuInitialized) {
939 nsCOMPtr<nsIRDFContainer> container;
940 res = NewRDFContainer(mInner, kNC_MaileditCharsetMenuRoot, getter_AddRefs(container));
941 if (NS_FAILED(res)) return res;
943 //enumerate encoders
944 // this would bring in a whole bunch of 'font encoders' as well as genuine
945 // charset encoders, but it's safe because we rely on prefs to filter
946 // them out. Moreover, 'customize' menu lists only genuine charset
947 // encoders further guarding against 'font encoders' sneaking in.
948 nsCOMPtr<nsIUTF8StringEnumerator> encoders;
949 res = mCCManager->GetEncoderList(getter_AddRefs(encoders));
950 if (NS_FAILED(res)) return res;
952 nsCStringArray maileditEncoderList;
953 SetArrayFromEnumerator(encoders, maileditEncoderList);
955 res = AddFromPrefsToMenu(NULL, container, kMaileditPrefKey, maileditEncoderList, NULL);
956 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailedit charset menu from prefs");
958 // register prefs callback
959 nsCOMPtr<nsIPrefBranch2> pbi = do_QueryInterface(mPrefs);
960 if (pbi)
961 res = pbi->AddObserver(kMaileditPrefKey, mCharsetMenuObserver, PR_FALSE);
964 mMaileditMenuInitialized = NS_SUCCEEDED(res);
966 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitMaileditMenu");
967 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitMaileditMenu");
969 return res;
972 nsresult nsCharsetMenu::InitMailviewMenu()
974 NS_TIMELINE_START_TIMER("nsCharsetMenu::InitMailviewMenu");
976 nsresult res = NS_OK;
978 if (!mMailviewMenuInitialized) {
979 nsCOMPtr<nsIRDFContainer> container;
980 res = NewRDFContainer(mInner, kNC_MailviewCharsetMenuRoot, getter_AddRefs(container));
981 if (NS_FAILED(res)) return res;
983 nsCStringArray mailviewDecoderList;
984 CloneCStringArray(mDecoderList, mailviewDecoderList);
986 res = InitStaticMenu(mailviewDecoderList, kNC_MailviewCharsetMenuRoot,
987 kMailviewStaticPrefKey, &mMailviewMenu);
988 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailview static charset menu");
990 // mark the end of the static area, the rest is cache
991 mMailviewCacheStart = mMailviewMenu.Count();
992 mPrefs->GetIntPref(kMailviewCacheSizePrefKey, &mMailviewCacheSize);
994 // compute the position of the menu in the RDF container
995 res = container->GetCount(&mMailviewMenuRDFPosition);
996 if (NS_FAILED(res)) return res;
997 // this "1" here is a correction necessary because the RDF container
998 // elements are numbered from 1 (why god, WHY?!?!?!)
999 mMailviewMenuRDFPosition -= mMailviewCacheStart - 1;
1001 res = InitCacheMenu(mailviewDecoderList, kNC_MailviewCharsetMenuRoot,
1002 kMailviewCachePrefKey, &mMailviewMenu);
1003 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailview cache charset menu");
1006 mMailviewMenuInitialized = NS_SUCCEEDED(res);
1008 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitMailviewMenu");
1009 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitMailviewMenu");
1011 return res;
1014 nsresult nsCharsetMenu::InitComposerMenu()
1016 NS_TIMELINE_START_TIMER("nsCharsetMenu::InitComposerMenu");
1018 nsresult res = NS_OK;
1020 if (!mComposerMenuInitialized) {
1021 nsCOMPtr<nsIRDFContainer> container;
1022 res = NewRDFContainer(mInner, kNC_ComposerCharsetMenuRoot, getter_AddRefs(container));
1023 if (NS_FAILED(res)) return res;
1025 nsCStringArray composerDecoderList;
1026 CloneCStringArray(mDecoderList, composerDecoderList);
1028 // even if we fail, the show must go on
1029 res = InitStaticMenu(composerDecoderList, kNC_ComposerCharsetMenuRoot,
1030 kComposerStaticPrefKey, &mComposerMenu);
1031 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing composer static charset menu");
1033 // mark the end of the static area, the rest is cache
1034 mComposerCacheStart = mComposerMenu.Count();
1035 mPrefs->GetIntPref(kComposerCacheSizePrefKey, &mComposerCacheSize);
1037 // compute the position of the menu in the RDF container
1038 res = container->GetCount(&mComposerMenuRDFPosition);
1039 if (NS_FAILED(res)) return res;
1040 // this "1" here is a correction necessary because the RDF container
1041 // elements are numbered from 1 (why god, WHY?!?!?!)
1042 mComposerMenuRDFPosition -= mComposerCacheStart - 1;
1044 res = InitCacheMenu(composerDecoderList, kNC_ComposerCharsetMenuRoot,
1045 kComposerCachePrefKey, &mComposerMenu);
1046 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing composer cache charset menu");
1049 mComposerMenuInitialized = NS_SUCCEEDED(res);
1051 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitComposerMenu");
1052 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitComposerMenu");
1054 return res;
1057 nsresult nsCharsetMenu::InitOthers()
1059 NS_TIMELINE_START_TIMER("nsCharsetMenu::InitOthers");
1061 nsresult res = NS_OK;
1063 if (!mOthersInitialized) {
1064 nsCStringArray othersDecoderList;
1065 CloneCStringArray(mDecoderList, othersDecoderList);
1067 res = InitMoreMenu(othersDecoderList, kNC_DecodersRoot, ".notForBrowser");
1068 if (NS_FAILED(res)) return res;
1070 // Using mDecoderList instead of GetEncoderList(), we can avoid having to
1071 // tag a whole bunch of 'font encoders' with '.notForOutgoing' in
1072 // charsetData.properties file.
1073 nsCStringArray othersEncoderList;
1074 CloneCStringArray(mDecoderList, othersEncoderList);
1076 res = InitMoreMenu(othersEncoderList, kNC_EncodersRoot, ".notForOutgoing");
1077 if (NS_FAILED(res)) return res;
1080 mOthersInitialized = NS_SUCCEEDED(res);
1082 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitOthers");
1083 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitOthers");
1085 return res;
1089 * Inits the secondary tiers of the charset menu. Because currently all the CS
1090 * menus are sharing the secondary tiers, one should call this method only
1091 * once for all of them.
1093 nsresult nsCharsetMenu::InitSecondaryTiers()
1095 NS_TIMELINE_START_TIMER("nsCharsetMenu::InitSecondaryTiers");
1097 nsresult res = NS_OK;
1099 if (!mSecondaryTiersInitialized) {
1100 nsCStringArray secondaryTiersDecoderList;
1101 CloneCStringArray(mDecoderList, secondaryTiersDecoderList);
1103 res = InitMoreSubmenus(secondaryTiersDecoderList);
1104 NS_ASSERTION(NS_SUCCEEDED(res), "err init browser charset more submenus");
1106 res = InitMoreMenu(secondaryTiersDecoderList, kNC_BrowserMoreCharsetMenuRoot, ".notForBrowser");
1107 NS_ASSERTION(NS_SUCCEEDED(res), "err init browser charset more menu");
1110 mSecondaryTiersInitialized = NS_SUCCEEDED(res);
1112 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitSecondaryTiers");
1113 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitSecondaryTiers");
1115 return res;
1118 nsresult nsCharsetMenu::InitStaticMenu(nsCStringArray& aDecs,
1119 nsIRDFResource * aResource,
1120 const char * aKey,
1121 nsVoidArray * aArray)
1123 NS_TIMELINE_START_TIMER("nsCharsetMenu::InitStaticMenu");
1125 nsresult res = NS_OK;
1126 nsCOMPtr<nsIRDFContainer> container;
1128 res = NewRDFContainer(mInner, aResource, getter_AddRefs(container));
1129 if (NS_FAILED(res)) return res;
1131 // XXX work around bug that causes the submenus to be first instead of last
1132 res = AddSeparatorToContainer(container);
1133 NS_ASSERTION(NS_SUCCEEDED(res), "error adding separator to container");
1135 res = AddFromPrefsToMenu(aArray, container, aKey, aDecs, "charset.");
1136 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
1138 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitStaticMenu");
1139 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitStaticMenu");
1141 return res;
1144 nsresult nsCharsetMenu::InitCacheMenu(
1145 nsCStringArray& aDecs,
1146 nsIRDFResource * aResource,
1147 const char * aKey,
1148 nsVoidArray * aArray)
1150 NS_TIMELINE_START_TIMER("nsCharsetMenu::InitCacheMenu");
1152 nsresult res = NS_OK;
1153 nsCOMPtr<nsIRDFContainer> container;
1155 res = NewRDFContainer(mInner, aResource, getter_AddRefs(container));
1156 if (NS_FAILED(res)) return res;
1158 res = AddFromNolocPrefsToMenu(aArray, container, aKey, aDecs, "charset.");
1159 NS_ASSERTION(NS_SUCCEEDED(res), "error initializing cache charset menu from prefs");
1161 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitCacheMenu");
1162 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitCacheMenu");
1164 return res;
1167 nsresult nsCharsetMenu::InitAutodetMenu()
1169 NS_TIMELINE_START_TIMER("nsCharsetMenu::InitAutodetMenu");
1171 nsresult res = NS_OK;
1173 if (!mAutoDetectInitialized) {
1174 nsVoidArray chardetArray;
1175 nsCOMPtr<nsIRDFContainer> container;
1176 nsCStringArray detectorArray;
1178 res = NewRDFContainer(mInner, kNC_BrowserAutodetMenuRoot, getter_AddRefs(container));
1179 if (NS_FAILED(res)) return res;
1181 nsCOMPtr<nsIUTF8StringEnumerator> detectors;
1182 res = mCCManager->GetCharsetDetectorList(getter_AddRefs(detectors));
1183 if (NS_FAILED(res)) goto done;
1185 res = SetArrayFromEnumerator(detectors, detectorArray);
1186 if (NS_FAILED(res)) goto done;
1188 res = AddCharsetArrayToItemArray(chardetArray, detectorArray);
1189 if (NS_FAILED(res)) goto done;
1191 // reorder the array
1192 res = ReorderMenuItemArray(&chardetArray);
1193 if (NS_FAILED(res)) goto done;
1195 res = AddMenuItemArrayToContainer(container, &chardetArray,
1196 kNC_CharsetDetector);
1197 if (NS_FAILED(res)) goto done;
1199 done:
1200 // free the elements in the VoidArray
1201 FreeMenuItemArray(&chardetArray);
1204 mAutoDetectInitialized = NS_SUCCEEDED(res);
1206 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitAutodetMenu");
1207 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitAutodetMenu");
1209 return res;
1212 nsresult nsCharsetMenu::InitMoreMenu(nsCStringArray& aDecs,
1213 nsIRDFResource * aResource,
1214 const char * aFlag)
1216 NS_TIMELINE_START_TIMER("nsCharsetMenu::InitMoreMenu");
1218 nsresult res = NS_OK;
1219 nsCOMPtr<nsIRDFContainer> container;
1220 nsVoidArray moreMenu;
1222 res = NewRDFContainer(mInner, aResource, getter_AddRefs(container));
1223 if (NS_FAILED(res)) goto done;
1225 // remove charsets "not for browser"
1226 res = RemoveFlaggedCharsets(aDecs, NS_ConvertASCIItoUTF16(aFlag));
1227 if (NS_FAILED(res)) goto done;
1229 res = AddCharsetArrayToItemArray(moreMenu, aDecs);
1230 if (NS_FAILED(res)) goto done;
1232 // reorder the array
1233 res = ReorderMenuItemArray(&moreMenu);
1234 if (NS_FAILED(res)) goto done;
1236 res = AddMenuItemArrayToContainer(container, &moreMenu, NULL);
1237 if (NS_FAILED(res)) goto done;
1239 done:
1240 // free the elements in the VoidArray
1241 FreeMenuItemArray(&moreMenu);
1243 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitMoreMenu");
1244 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitMoreMenu");
1246 return res;
1249 // XXX please make this method more general; the cut&pasted code is laughable
1250 nsresult nsCharsetMenu::InitMoreSubmenus(nsCStringArray& aDecs)
1252 NS_TIMELINE_START_TIMER("nsCharsetMenu::InitMoreSubmenus");
1254 nsresult res = NS_OK;
1256 nsCOMPtr<nsIRDFContainer> container1;
1257 nsCOMPtr<nsIRDFContainer> container2;
1258 nsCOMPtr<nsIRDFContainer> container3;
1259 nsCOMPtr<nsIRDFContainer> container4;
1260 nsCOMPtr<nsIRDFContainer> container5;
1261 nsCOMPtr<nsIRDFContainer> containerU;
1262 const char key1[] = "intl.charsetmenu.browser.more1";
1263 const char key2[] = "intl.charsetmenu.browser.more2";
1264 const char key3[] = "intl.charsetmenu.browser.more3";
1265 const char key4[] = "intl.charsetmenu.browser.more4";
1266 const char key5[] = "intl.charsetmenu.browser.more5";
1267 const char keyU[] = "intl.charsetmenu.browser.unicode";
1269 res = NewRDFContainer(mInner, kNC_BrowserMore1CharsetMenuRoot,
1270 getter_AddRefs(container1));
1271 if (NS_FAILED(res)) return res;
1272 AddFromPrefsToMenu(NULL, container1, key1, aDecs, NULL);
1274 res = NewRDFContainer(mInner, kNC_BrowserMore2CharsetMenuRoot,
1275 getter_AddRefs(container2));
1276 if (NS_FAILED(res)) return res;
1277 AddFromPrefsToMenu(NULL, container2, key2, aDecs, NULL);
1279 res = NewRDFContainer(mInner, kNC_BrowserMore3CharsetMenuRoot,
1280 getter_AddRefs(container3));
1281 if (NS_FAILED(res)) return res;
1282 AddFromPrefsToMenu(NULL, container3, key3, aDecs, NULL);
1284 res = NewRDFContainer(mInner, kNC_BrowserMore4CharsetMenuRoot,
1285 getter_AddRefs(container4));
1286 if (NS_FAILED(res)) return res;
1287 AddFromPrefsToMenu(NULL, container4, key4, aDecs, NULL);
1289 res = NewRDFContainer(mInner, kNC_BrowserMore5CharsetMenuRoot,
1290 getter_AddRefs(container5));
1291 if (NS_FAILED(res)) return res;
1292 AddFromPrefsToMenu(NULL, container5, key5, aDecs, NULL);
1294 res = NewRDFContainer(mInner, kNC_BrowserUnicodeCharsetMenuRoot,
1295 getter_AddRefs(containerU));
1296 if (NS_FAILED(res)) return res;
1297 AddFromPrefsToMenu(NULL, containerU, keyU, aDecs, NULL);
1299 NS_TIMELINE_STOP_TIMER("nsCharsetMenu::InitMoreSubmenus");
1300 NS_TIMELINE_MARK_TIMER("nsCharsetMenu::InitMoreSubmenus");
1302 return res;
1305 nsresult nsCharsetMenu::AddCharsetToItemArray(nsVoidArray *aArray,
1306 const nsAFlatCString& aCharset,
1307 nsMenuEntry ** aResult,
1308 PRInt32 aPlace)
1310 nsresult res = NS_OK;
1311 nsMenuEntry * item = NULL;
1313 if (aResult != NULL) *aResult = NULL;
1315 item = new nsMenuEntry();
1316 if (item == NULL) {
1317 res = NS_ERROR_OUT_OF_MEMORY;
1318 goto done;
1321 item->mCharset = aCharset;
1323 res = mCCManager->GetCharsetTitle(aCharset.get(), item->mTitle);
1324 if (NS_FAILED(res)) {
1325 item->mTitle.AssignWithConversion(aCharset.get());
1328 if (aArray != NULL) {
1329 if (aPlace < 0) {
1330 res = aArray->AppendElement(item);
1331 if (NS_FAILED(res)) goto done;
1332 } else {
1333 res = aArray->InsertElementAt(item, aPlace);
1334 if (NS_FAILED(res)) goto done;
1338 if (aResult != NULL) *aResult = item;
1340 // if we have made another reference to "item", do not delete it
1341 if ((aArray != NULL) || (aResult != NULL)) item = NULL;
1343 done:
1344 if (item != NULL) delete item;
1346 return res;
1349 nsresult
1350 nsCharsetMenu::AddCharsetArrayToItemArray(nsVoidArray& aArray,
1351 const nsCStringArray& aCharsets)
1353 PRUint32 count = aCharsets.Count();
1355 for (PRUint32 i = 0; i < count; i++) {
1357 nsCString* str = aCharsets.CStringAt(i);
1358 if (str) {
1359 nsresult res = AddCharsetToItemArray(&aArray, *str, NULL, -1);
1361 if (NS_FAILED(res)) return res;
1365 return NS_OK;
1368 // aPlace < -1 for Remove
1369 // aPlace < 0 for Append
1370 nsresult nsCharsetMenu::AddMenuItemToContainer(
1371 nsIRDFContainer * aContainer,
1372 nsMenuEntry * aItem,
1373 nsIRDFResource * aType,
1374 const char * aIDPrefix,
1375 PRInt32 aPlace)
1377 nsresult res = NS_OK;
1378 nsCOMPtr<nsIRDFResource> node;
1380 nsCAutoString id;
1381 if (aIDPrefix != NULL) id.Assign(aIDPrefix);
1382 id.Append(aItem->mCharset);
1384 // Make up a unique ID and create the RDF NODE
1385 res = mRDFService->GetResource(id, getter_AddRefs(node));
1386 if (NS_FAILED(res)) return res;
1388 const PRUnichar * title = aItem->mTitle.get();
1390 // set node's title
1391 nsCOMPtr<nsIRDFLiteral> titleLiteral;
1392 res = mRDFService->GetLiteral(title, getter_AddRefs(titleLiteral));
1393 if (NS_FAILED(res)) return res;
1395 if (aPlace < -1) {
1396 res = Unassert(node, kNC_Name, titleLiteral);
1397 if (NS_FAILED(res)) return res;
1398 } else {
1399 res = Assert(node, kNC_Name, titleLiteral, PR_TRUE);
1400 if (NS_FAILED(res)) return res;
1403 if (aType != NULL) {
1404 if (aPlace < -1) {
1405 res = Unassert(node, kRDF_type, aType);
1406 if (NS_FAILED(res)) return res;
1407 } else {
1408 res = Assert(node, kRDF_type, aType, PR_TRUE);
1409 if (NS_FAILED(res)) return res;
1413 // Add the element to the container
1414 if (aPlace < -1) {
1415 res = aContainer->RemoveElement(node, PR_TRUE);
1416 if (NS_FAILED(res)) return res;
1417 } else if (aPlace < 0) {
1418 res = aContainer->AppendElement(node);
1419 if (NS_FAILED(res)) return res;
1420 } else {
1421 res = aContainer->InsertElementAt(node, aPlace, PR_TRUE);
1422 if (NS_FAILED(res)) return res;
1425 return res;
1428 nsresult nsCharsetMenu::AddMenuItemArrayToContainer(
1429 nsIRDFContainer * aContainer,
1430 nsVoidArray * aArray,
1431 nsIRDFResource * aType)
1433 PRUint32 count = aArray->Count();
1434 nsresult res = NS_OK;
1436 for (PRUint32 i = 0; i < count; i++) {
1437 nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(i);
1438 if (item == NULL) return NS_ERROR_UNEXPECTED;
1440 res = AddMenuItemToContainer(aContainer, item, aType, NULL, -1);
1441 if (NS_FAILED(res)) return res;
1444 return NS_OK;
1447 nsresult nsCharsetMenu::AddCharsetToContainer(nsVoidArray *aArray,
1448 nsIRDFContainer * aContainer,
1449 const nsAFlatCString& aCharset,
1450 const char * aIDPrefix,
1451 PRInt32 aPlace,
1452 PRInt32 aRDFPlace)
1454 nsresult res = NS_OK;
1455 nsMenuEntry * item = NULL;
1457 res = AddCharsetToItemArray(aArray, aCharset, &item, aPlace);
1458 if (NS_FAILED(res)) goto done;
1460 res = AddMenuItemToContainer(aContainer, item, NULL, aIDPrefix,
1461 aPlace + aRDFPlace);
1462 if (NS_FAILED(res)) goto done;
1464 // if we have made another reference to "item", do not delete it
1465 if (aArray != NULL) item = NULL;
1467 done:
1468 if (item != NULL) delete item;
1470 return res;
1473 nsresult nsCharsetMenu::AddFromPrefsToMenu(
1474 nsVoidArray * aArray,
1475 nsIRDFContainer * aContainer,
1476 const char * aKey,
1477 nsCStringArray& aDecs,
1478 const char * aIDPrefix)
1480 nsresult res = NS_OK;
1482 nsCOMPtr<nsIPrefLocalizedString> pls;
1483 res = mPrefs->GetComplexValue(aKey, NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(pls));
1484 if (NS_FAILED(res)) return res;
1486 if (pls) {
1487 nsXPIDLString ucsval;
1488 pls->ToString(getter_Copies(ucsval));
1489 NS_ConvertUTF16toUTF8 utf8val(ucsval);
1490 if (ucsval)
1491 res = AddFromStringToMenu(utf8val.BeginWriting(), aArray,
1492 aContainer, aDecs, aIDPrefix);
1495 return res;
1498 nsresult
1499 nsCharsetMenu::AddFromNolocPrefsToMenu(nsVoidArray * aArray,
1500 nsIRDFContainer * aContainer,
1501 const char * aKey,
1502 nsCStringArray& aDecs,
1503 const char * aIDPrefix)
1505 nsresult res = NS_OK;
1507 char * value = NULL;
1508 res = mPrefs->GetCharPref(aKey, &value);
1509 if (NS_FAILED(res)) return res;
1511 if (value != NULL) {
1512 res = AddFromStringToMenu(value, aArray, aContainer, aDecs, aIDPrefix);
1513 nsMemory::Free(value);
1516 return res;
1519 nsresult nsCharsetMenu::AddFromStringToMenu(
1520 char * aCharsetList,
1521 nsVoidArray * aArray,
1522 nsIRDFContainer * aContainer,
1523 nsCStringArray& aDecs,
1524 const char * aIDPrefix)
1526 nsresult res = NS_OK;
1527 char * p = aCharsetList;
1528 char * q = p;
1529 while (*p != 0) {
1530 for (; (*q != ',') && (*q != ' ') && (*q != 0); q++) {;}
1531 char temp = *q;
1532 *q = 0;
1534 // if this charset is not on the accepted list of charsets, ignore it
1535 PRInt32 index;
1536 index = aDecs.IndexOfIgnoreCase(nsCAutoString(p));
1537 if (index >= 0) {
1539 // else, add it to the menu
1540 res = AddCharsetToContainer(aArray, aContainer, nsDependentCString(p),
1541 aIDPrefix, -1, 0);
1542 NS_ASSERTION(NS_SUCCEEDED(res), "cannot add charset to menu");
1543 if (NS_FAILED(res)) break;
1545 res = aDecs.RemoveCStringAt(index);
1546 NS_ASSERTION(NS_SUCCEEDED(res), "cannot remove atom from array");
1549 *q = temp;
1550 for (; (*q == ',') || (*q == ' '); q++) {;}
1551 p=q;
1554 return NS_OK;
1557 nsresult nsCharsetMenu::AddSeparatorToContainer(nsIRDFContainer * aContainer)
1559 nsCAutoString str;
1560 str.AssignLiteral("----");
1562 // hack to generate unique id's for separators
1563 static PRInt32 u = 0;
1564 u++;
1565 str.AppendInt(u);
1567 nsMenuEntry item;
1568 item.mCharset = str;
1569 item.mTitle.AssignWithConversion(str.get());
1571 return AddMenuItemToContainer(aContainer, &item, kNC_BookmarkSeparator,
1572 NULL, -1);
1575 nsresult
1576 nsCharsetMenu::AddCharsetToCache(const nsAFlatCString& aCharset,
1577 nsVoidArray * aArray,
1578 nsIRDFResource * aRDFResource,
1579 PRInt32 aCacheStart,
1580 PRInt32 aCacheSize,
1581 PRInt32 aRDFPlace)
1583 PRInt32 i;
1584 nsresult res = NS_OK;
1586 i = FindMenuItemInArray(aArray, aCharset, NULL);
1587 if (i >= 0) return res;
1589 nsCOMPtr<nsIRDFContainer> container;
1590 res = NewRDFContainer(mInner, aRDFResource, getter_AddRefs(container));
1591 if (NS_FAILED(res)) return res;
1593 // iff too many items, remove last one
1594 if (aArray->Count() - aCacheStart >= aCacheSize){
1595 res = RemoveLastMenuItem(container, aArray);
1596 if (NS_FAILED(res)) return res;
1599 res = AddCharsetToContainer(aArray, container, aCharset, "charset.",
1600 aCacheStart, aRDFPlace);
1602 return res;
1605 nsresult nsCharsetMenu::WriteCacheToPrefs(nsVoidArray * aArray,
1606 PRInt32 aCacheStart,
1607 const char * aKey)
1609 nsresult res = NS_OK;
1611 // create together the cache string
1612 nsCAutoString cache;
1613 nsCAutoString sep(NS_LITERAL_CSTRING(", "));
1614 PRInt32 count = aArray->Count();
1616 for (PRInt32 i = aCacheStart; i < count; i++) {
1617 nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(i);
1618 if (item != NULL) {
1619 cache.Append(item->mCharset);
1620 if (i < count - 1) {
1621 cache.Append(sep);
1626 // write the pref
1627 res = mPrefs->SetCharPref(aKey, cache.get());
1629 return res;
1632 nsresult nsCharsetMenu::UpdateCachePrefs(const char * aCacheKey,
1633 const char * aCacheSizeKey,
1634 const char * aStaticKey,
1635 const PRUnichar * aCharset)
1637 nsresult rv = NS_OK;
1638 nsXPIDLCString cachePrefValue;
1639 nsXPIDLCString staticPrefValue;
1640 NS_LossyConvertUTF16toASCII currentCharset(aCharset);
1641 PRInt32 cacheSize = 0;
1643 mPrefs->GetCharPref(aCacheKey, getter_Copies(cachePrefValue));
1644 mPrefs->GetCharPref(aStaticKey, getter_Copies(staticPrefValue));
1645 rv = mPrefs->GetIntPref(aCacheSizeKey, &cacheSize);
1647 if (NS_FAILED(rv) || cacheSize <= 0)
1648 return NS_ERROR_UNEXPECTED;
1650 if ((cachePrefValue.Find(currentCharset) == kNotFound) &&
1651 (staticPrefValue.Find(currentCharset) == kNotFound)) {
1653 if (!cachePrefValue.IsEmpty())
1654 cachePrefValue.Insert(", ", 0);
1656 cachePrefValue.Insert(currentCharset, 0);
1657 if (cacheSize < (PRInt32) cachePrefValue.CountChar(',') + 1)
1658 cachePrefValue.Truncate(cachePrefValue.RFindChar(','));
1660 rv = mPrefs->SetCharPref(aCacheKey, cachePrefValue);
1663 return rv;
1666 nsresult nsCharsetMenu::ClearMenu(nsIRDFContainer * aContainer,
1667 nsVoidArray * aArray)
1669 nsresult res = NS_OK;
1671 // clean the RDF data source
1672 PRInt32 count = aArray->Count();
1673 for (PRInt32 i = 0; i < count; i++) {
1674 nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(i);
1675 if (item != NULL) {
1676 res = AddMenuItemToContainer(aContainer, item, NULL, "charset.", -2);
1677 if (NS_FAILED(res)) return res;
1681 // clean the internal data structures
1682 FreeMenuItemArray(aArray);
1684 return res;
1687 nsresult nsCharsetMenu::RemoveLastMenuItem(nsIRDFContainer * aContainer,
1688 nsVoidArray * aArray)
1690 nsresult res = NS_OK;
1692 PRInt32 last = aArray->Count() - 1;
1693 if (last >= 0) {
1694 nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(last);
1695 if (item != NULL) {
1696 res = AddMenuItemToContainer(aContainer, item, NULL, "charset.", -2);
1697 if (NS_FAILED(res)) return res;
1699 res = aArray->RemoveElementAt(last);
1700 if (NS_FAILED(res)) return res;
1704 return res;
1707 nsresult nsCharsetMenu::RemoveFlaggedCharsets(nsCStringArray& aList,
1708 const nsString& aProp)
1710 nsresult res = NS_OK;
1711 PRUint32 count;
1713 count = aList.Count();
1714 if (NS_FAILED(res)) return res;
1716 nsCString* charset;
1717 nsAutoString str;
1718 for (PRUint32 i = 0; i < count; i++) {
1720 charset = aList.CStringAt(i);
1721 if (!charset) continue;
1723 res = mCCManager->GetCharsetData(charset->get(), aProp.get(), str);
1724 if (NS_FAILED(res)) continue;
1726 aList.RemoveCStringAt(i);
1728 i--;
1729 count--;
1732 return NS_OK;
1735 nsresult nsCharsetMenu::NewRDFContainer(nsIRDFDataSource * aDataSource,
1736 nsIRDFResource * aResource,
1737 nsIRDFContainer ** aResult)
1739 nsresult res = CallCreateInstance(kRDFContainerCID, aResult);
1740 if (NS_FAILED(res)) return res;
1742 res = (*aResult)->Init(aDataSource, aResource);
1743 if (NS_FAILED(res)) NS_RELEASE(*aResult);
1745 return res;
1748 void nsCharsetMenu::FreeMenuItemArray(nsVoidArray * aArray)
1750 PRUint32 count = aArray->Count();
1751 for (PRUint32 i = 0; i < count; i++) {
1752 nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(i);
1753 if (item != NULL) {
1754 delete item;
1757 aArray->Clear();
1760 PRInt32 nsCharsetMenu::FindMenuItemInArray(const nsVoidArray* aArray,
1761 const nsAFlatCString& aCharset,
1762 nsMenuEntry ** aResult)
1764 PRUint32 count = aArray->Count();
1766 for (PRUint32 i=0; i < count; i++) {
1767 nsMenuEntry * item = (nsMenuEntry *) aArray->ElementAt(i);
1768 if (item->mCharset == aCharset) {
1769 if (aResult != NULL) *aResult = item;
1770 return i;
1774 if (aResult != NULL) *aResult = NULL;
1775 return -1;
1778 nsresult nsCharsetMenu::ReorderMenuItemArray(nsVoidArray * aArray)
1780 nsresult res = NS_OK;
1781 nsCOMPtr<nsICollation> collation;
1782 PRUint32 count = aArray->Count();
1783 PRUint32 i;
1785 // we need to use a temporary array
1786 charsetMenuSortRecord *array = new charsetMenuSortRecord [count];
1787 NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
1788 for (i = 0; i < count; i++)
1789 array[i].key = nsnull;
1791 res = GetCollation(getter_AddRefs(collation));
1792 if (NS_FAILED(res))
1793 goto done;
1795 for (i = 0; i < count && NS_SUCCEEDED(res); i++) {
1796 array[i].item = (nsMenuEntry *)aArray->ElementAt(i);
1798 res = collation->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive,
1799 (array[i].item)->mTitle, &array[i].key, &array[i].len);
1802 // reorder the array
1803 if (NS_SUCCEEDED(res)) {
1804 NS_QuickSort(array, count, sizeof(*array), CompareMenuItems, collation);
1806 // move the elements from the temporary array into the the real one
1807 aArray->Clear();
1808 for (i = 0; i < count; i++) {
1809 aArray->AppendElement(array[i].item);
1813 done:
1814 for (i = 0; i < count; i++) {
1815 PR_FREEIF(array[i].key);
1817 delete [] array;
1818 return res;
1821 nsresult nsCharsetMenu::GetCollation(nsICollation ** aCollation)
1823 nsresult res = NS_OK;
1824 nsCOMPtr<nsILocale> locale = nsnull;
1825 nsICollationFactory * collationFactory = nsnull;
1827 nsCOMPtr<nsILocaleService> localeServ =
1828 do_GetService(NS_LOCALESERVICE_CONTRACTID, &res);
1829 if (NS_FAILED(res)) return res;
1831 res = localeServ->GetApplicationLocale(getter_AddRefs(locale));
1832 if (NS_FAILED(res)) return res;
1834 res = CallCreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &collationFactory);
1835 if (NS_FAILED(res)) return res;
1837 res = collationFactory->CreateCollation(locale, aCollation);
1838 NS_RELEASE(collationFactory);
1839 return res;
1842 //----------------------------------------------------------------------------
1843 // Interface nsICurrentCharsetListener [implementation]
1845 NS_IMETHODIMP nsCharsetMenu::SetCurrentCharset(const PRUnichar * aCharset)
1847 NS_TIMELINE_START_TIMER("nsCharsetMenu:SetCurrentCharset");
1848 nsresult res = NS_OK;
1850 if (mBrowserMenuInitialized) {
1851 // Don't add item to the cache if it's marked "notForBrowser"
1852 nsAutoString str;
1853 res = mCCManager->GetCharsetData(NS_LossyConvertUTF16toASCII(aCharset).get(),
1854 NS_LITERAL_STRING(".notForBrowser").get(), str);
1855 if (NS_SUCCEEDED(res)) // succeeded means attribute exists
1856 return res; // don't throw
1858 res = AddCharsetToCache(NS_LossyConvertUTF16toASCII(aCharset),
1859 &mBrowserMenu, kNC_BrowserCharsetMenuRoot,
1860 mBrowserCacheStart, mBrowserCacheSize,
1861 mBrowserMenuRDFPosition);
1862 if (NS_FAILED(res)) {
1863 NS_TIMELINE_LEAVE("nsCharsetMenu:SetCurrentCharset");
1864 return res;
1867 res = WriteCacheToPrefs(&mBrowserMenu, mBrowserCacheStart,
1868 kBrowserCachePrefKey);
1869 } else {
1870 res = UpdateCachePrefs(kBrowserCachePrefKey, kBrowserCacheSizePrefKey,
1871 kBrowserStaticPrefKey, aCharset);
1873 NS_TIMELINE_STOP_TIMER("nsCharsetMenu:SetCurrentCharset");
1874 NS_TIMELINE_MARK_TIMER("nsCharsetMenu:SetCurrentCharset");
1875 return res;
1878 NS_IMETHODIMP nsCharsetMenu::SetCurrentMailCharset(const PRUnichar * aCharset)
1880 NS_TIMELINE_START_TIMER("nsCharsetMenu:SetCurrentMailCharset");
1881 nsresult res = NS_OK;
1883 if (mMailviewMenuInitialized) {
1884 res = AddCharsetToCache(NS_LossyConvertUTF16toASCII(aCharset),
1885 &mMailviewMenu, kNC_MailviewCharsetMenuRoot,
1886 mMailviewCacheStart, mMailviewCacheSize,
1887 mMailviewMenuRDFPosition);
1888 if (NS_FAILED(res)) return res;
1890 res = WriteCacheToPrefs(&mMailviewMenu, mMailviewCacheStart,
1891 kMailviewCachePrefKey);
1892 } else {
1893 res = UpdateCachePrefs(kMailviewCachePrefKey, kMailviewCacheSizePrefKey,
1894 kMailviewStaticPrefKey, aCharset);
1896 NS_TIMELINE_STOP_TIMER("nsCharsetMenu:SetCurrentMailCharset");
1897 NS_TIMELINE_MARK_TIMER("nsCharsetMenu:SetCurrentMailCharset");
1898 return res;
1901 NS_IMETHODIMP nsCharsetMenu::SetCurrentComposerCharset(const PRUnichar * aCharset)
1903 NS_TIMELINE_START_TIMER("nsCharsetMenu:SetCurrentComposerCharset");
1904 nsresult res = NS_OK;
1906 if (mComposerMenuInitialized) {
1908 res = AddCharsetToCache(NS_LossyConvertUTF16toASCII(aCharset),
1909 &mComposerMenu, kNC_ComposerCharsetMenuRoot,
1910 mComposerCacheStart, mComposerCacheSize,
1911 mComposerMenuRDFPosition);
1912 if (NS_FAILED(res)) return res;
1914 res = WriteCacheToPrefs(&mComposerMenu, mComposerCacheStart,
1915 kComposerCachePrefKey);
1916 } else {
1917 res = UpdateCachePrefs(kComposerCachePrefKey, kComposerCacheSizePrefKey,
1918 kComposerStaticPrefKey, aCharset);
1920 NS_TIMELINE_STOP_TIMER("nsCharsetMenu:SetCurrentComposerCharset");
1921 NS_TIMELINE_MARK_TIMER("nsCharsetMenu:SetCurrentComposerCharset");
1922 return res;
1925 //----------------------------------------------------------------------------
1926 // Interface nsIRDFDataSource [implementation]
1928 NS_IMETHODIMP nsCharsetMenu::GetURI(char ** uri)
1930 if (!uri) return NS_ERROR_NULL_POINTER;
1932 *uri = nsCRT::strdup("rdf:charset-menu");
1933 if (!(*uri)) return NS_ERROR_OUT_OF_MEMORY;
1935 return NS_OK;
1938 NS_IMETHODIMP nsCharsetMenu::GetSource(nsIRDFResource* property,
1939 nsIRDFNode* target,
1940 PRBool tv,
1941 nsIRDFResource** source)
1943 return mInner->GetSource(property, target, tv, source);
1946 NS_IMETHODIMP nsCharsetMenu::GetSources(nsIRDFResource* property,
1947 nsIRDFNode* target,
1948 PRBool tv,
1949 nsISimpleEnumerator** sources)
1951 return mInner->GetSources(property, target, tv, sources);
1954 NS_IMETHODIMP nsCharsetMenu::GetTarget(nsIRDFResource* source,
1955 nsIRDFResource* property,
1956 PRBool tv,
1957 nsIRDFNode** target)
1959 return mInner->GetTarget(source, property, tv, target);
1962 NS_IMETHODIMP nsCharsetMenu::GetTargets(nsIRDFResource* source,
1963 nsIRDFResource* property,
1964 PRBool tv,
1965 nsISimpleEnumerator** targets)
1967 return mInner->GetTargets(source, property, tv, targets);
1970 NS_IMETHODIMP nsCharsetMenu::Assert(nsIRDFResource* aSource,
1971 nsIRDFResource* aProperty,
1972 nsIRDFNode* aTarget,
1973 PRBool aTruthValue)
1975 // TODO: filter out asserts we don't care about
1976 return mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
1979 NS_IMETHODIMP nsCharsetMenu::Unassert(nsIRDFResource* aSource,
1980 nsIRDFResource* aProperty,
1981 nsIRDFNode* aTarget)
1983 // TODO: filter out unasserts we don't care about
1984 return mInner->Unassert(aSource, aProperty, aTarget);
1988 NS_IMETHODIMP nsCharsetMenu::Change(nsIRDFResource* aSource,
1989 nsIRDFResource* aProperty,
1990 nsIRDFNode* aOldTarget,
1991 nsIRDFNode* aNewTarget)
1993 // TODO: filter out changes we don't care about
1994 return mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
1997 NS_IMETHODIMP nsCharsetMenu::Move(nsIRDFResource* aOldSource,
1998 nsIRDFResource* aNewSource,
1999 nsIRDFResource* aProperty,
2000 nsIRDFNode* aTarget)
2002 // TODO: filter out changes we don't care about
2003 return mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
2007 NS_IMETHODIMP nsCharsetMenu::HasAssertion(nsIRDFResource* source,
2008 nsIRDFResource* property,
2009 nsIRDFNode* target, PRBool tv,
2010 PRBool* hasAssertion)
2012 return mInner->HasAssertion(source, property, target, tv, hasAssertion);
2015 NS_IMETHODIMP nsCharsetMenu::AddObserver(nsIRDFObserver* n)
2017 return mInner->AddObserver(n);
2020 NS_IMETHODIMP nsCharsetMenu::RemoveObserver(nsIRDFObserver* n)
2022 return mInner->RemoveObserver(n);
2025 NS_IMETHODIMP
2026 nsCharsetMenu::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, PRBool *result)
2028 return mInner->HasArcIn(aNode, aArc, result);
2031 NS_IMETHODIMP
2032 nsCharsetMenu::HasArcOut(nsIRDFResource *source, nsIRDFResource *aArc, PRBool *result)
2034 return mInner->HasArcOut(source, aArc, result);
2037 NS_IMETHODIMP nsCharsetMenu::ArcLabelsIn(nsIRDFNode* node,
2038 nsISimpleEnumerator** labels)
2040 return mInner->ArcLabelsIn(node, labels);
2043 NS_IMETHODIMP nsCharsetMenu::ArcLabelsOut(nsIRDFResource* source,
2044 nsISimpleEnumerator** labels)
2046 return mInner->ArcLabelsOut(source, labels);
2049 NS_IMETHODIMP nsCharsetMenu::GetAllResources(nsISimpleEnumerator** aCursor)
2051 return mInner->GetAllResources(aCursor);
2054 NS_IMETHODIMP nsCharsetMenu::GetAllCmds(
2055 nsIRDFResource* source,
2056 nsISimpleEnumerator/*<nsIRDFResource>*/** commands)
2058 NS_NOTYETIMPLEMENTED("write me!");
2059 return NS_ERROR_NOT_IMPLEMENTED;
2062 NS_IMETHODIMP nsCharsetMenu::IsCommandEnabled(
2063 nsISupportsArray/*<nsIRDFResource>*/* aSources,
2064 nsIRDFResource* aCommand,
2065 nsISupportsArray/*<nsIRDFResource>*/* aArguments,
2066 PRBool* aResult)
2068 NS_NOTYETIMPLEMENTED("write me!");
2069 return NS_ERROR_NOT_IMPLEMENTED;
2072 NS_IMETHODIMP nsCharsetMenu::DoCommand(nsISupportsArray* aSources,
2073 nsIRDFResource* aCommand,
2074 nsISupportsArray* aArguments)
2076 NS_NOTYETIMPLEMENTED("write me!");
2077 return NS_ERROR_NOT_IMPLEMENTED;
2080 NS_IMETHODIMP nsCharsetMenu::BeginUpdateBatch()
2082 return mInner->BeginUpdateBatch();
2085 NS_IMETHODIMP nsCharsetMenu::EndUpdateBatch()
2087 return mInner->EndUpdateBatch();
2090 // Module definitions
2092 static const nsModuleComponentInfo components[] = {
2093 { "nsCharsetMenu", NS_CHARSETMENU_CID,
2094 NS_RDF_DATASOURCE_CONTRACTID_PREFIX NS_CHARSETMENU_PID,
2095 NS_NewCharsetMenu },
2098 NS_IMPL_NSGETMODULE(nsXPIntlModule, components)