1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Pierre Phaneuf <pp@ludusdesign.com>
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
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 "nsILocaleService.h"
53 : #include "nsIPrefService.h"
54 : #include "nsIPrefBranch.h"
55 : #include "nsIPrefLocalizedString.h"
56 : #include "nsICurrentCharsetListener.h"
57 : #include "nsQuickSort.h"
58 : #include "nsIObserver.h"
59 : #include "nsStringEnumerator.h"
60 : #include "nsTArray.h"
61 : #include "nsIObserverService.h"
62 : #include "nsIRequestObserver.h"
63 : #include "nsCRT.h"
64 : #include "prmem.h"
65 : #include "mozilla/ModuleUtils.h"
66 : #include "nsCycleCollectionParticipant.h"
67 :
68 : //----------------------------------------------------------------------------
69 : // Global functions and data [declaration]
70 :
71 : static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
72 : static NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID);
73 : static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
74 : static NS_DEFINE_CID(kRDFContainerCID, NS_RDFCONTAINER_CID);
75 :
76 : static const char kURINC_BrowserAutodetMenuRoot[] = "NC:BrowserAutodetMenuRoot";
77 : static const char kURINC_BrowserCharsetMenuRoot[] = "NC:BrowserCharsetMenuRoot";
78 : static const char kURINC_BrowserMoreCharsetMenuRoot[] = "NC:BrowserMoreCharsetMenuRoot";
79 : static const char kURINC_BrowserMore1CharsetMenuRoot[] = "NC:BrowserMore1CharsetMenuRoot";
80 : static const char kURINC_BrowserMore2CharsetMenuRoot[] = "NC:BrowserMore2CharsetMenuRoot";
81 : static const char kURINC_BrowserMore3CharsetMenuRoot[] = "NC:BrowserMore3CharsetMenuRoot";
82 : static const char kURINC_BrowserMore4CharsetMenuRoot[] = "NC:BrowserMore4CharsetMenuRoot";
83 : static const char kURINC_BrowserMore5CharsetMenuRoot[] = "NC:BrowserMore5CharsetMenuRoot";
84 : static const char kURINC_BrowserUnicodeCharsetMenuRoot[] = "NC:BrowserUnicodeCharsetMenuRoot";
85 : static const char kURINC_MaileditCharsetMenuRoot[] = "NC:MaileditCharsetMenuRoot";
86 : static const char kURINC_MailviewCharsetMenuRoot[] = "NC:MailviewCharsetMenuRoot";
87 : static const char kURINC_ComposerCharsetMenuRoot[] = "NC:ComposerCharsetMenuRoot";
88 : static const char kURINC_DecodersRoot[] = "NC:DecodersRoot";
89 : static const char kURINC_EncodersRoot[] = "NC:EncodersRoot";
90 : DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, Name);
91 : DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, BookmarkSeparator);
92 : DEFINE_RDF_VOCAB(NC_NAMESPACE_URI, NC, CharsetDetector);
93 : DEFINE_RDF_VOCAB(RDF_NAMESPACE_URI, NC, type);
94 :
95 : // Note here that browser and mailview have the same static area and cache
96 : // size but the cache itself is separate.
97 :
98 : #define kBrowserStaticPrefKey "intl.charsetmenu.browser.static"
99 : #define kBrowserCachePrefKey "intl.charsetmenu.browser.cache"
100 : #define kBrowserCacheSizePrefKey "intl.charsetmenu.browser.cache.size"
101 :
102 : #define kMailviewStaticPrefKey "intl.charsetmenu.browser.static"
103 : #define kMailviewCachePrefKey "intl.charsetmenu.mailview.cache"
104 : #define kMailviewCacheSizePrefKey "intl.charsetmenu.browser.cache.size"
105 :
106 : #define kComposerStaticPrefKey "intl.charsetmenu.browser.static"
107 : #define kComposerCachePrefKey "intl.charsetmenu.composer.cache"
108 : #define kComposerCacheSizePrefKey "intl.charsetmenu.browser.cache.size"
109 :
110 : #define kMaileditPrefKey "intl.charsetmenu.mailedit"
111 :
112 : //----------------------------------------------------------------------------
113 : // Class nsMenuEntry [declaration]
114 :
115 : /**
116 : * A little class holding all data needed for a menu item.
117 : *
118 : * @created 18/Apr/2000
119 : * @author Catalin Rotaru [CATA]
120 : */
121 : class nsMenuEntry
122 : {
123 : public:
124 : // memory & ref counting & leak prevention stuff
125 0 : nsMenuEntry() { MOZ_COUNT_CTOR(nsMenuEntry); }
126 0 : ~nsMenuEntry() { MOZ_COUNT_DTOR(nsMenuEntry); }
127 :
128 : nsCAutoString mCharset;
129 : nsAutoString mTitle;
130 : };
131 :
132 : //----------------------------------------------------------------------------
133 : // Class nsCharsetMenu [declaration]
134 :
135 : /**
136 : * The Charset Converter menu.
137 : *
138 : * God, our GUI programming disgusts me.
139 : *
140 : * @created 23/Nov/1999
141 : * @author Catalin Rotaru [CATA]
142 : */
143 : class nsCharsetMenu : public nsIRDFDataSource, public nsICurrentCharsetListener
144 : {
145 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
146 1464 : NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCharsetMenu, nsIRDFDataSource)
147 :
148 : private:
149 : static nsIRDFResource * kNC_BrowserAutodetMenuRoot;
150 : static nsIRDFResource * kNC_BrowserCharsetMenuRoot;
151 : static nsIRDFResource * kNC_BrowserMoreCharsetMenuRoot;
152 : static nsIRDFResource * kNC_BrowserMore1CharsetMenuRoot;
153 : static nsIRDFResource * kNC_BrowserMore2CharsetMenuRoot;
154 : static nsIRDFResource * kNC_BrowserMore3CharsetMenuRoot;
155 : static nsIRDFResource * kNC_BrowserMore4CharsetMenuRoot;
156 : static nsIRDFResource * kNC_BrowserMore5CharsetMenuRoot;
157 : static nsIRDFResource * kNC_BrowserUnicodeCharsetMenuRoot;
158 : static nsIRDFResource * kNC_MaileditCharsetMenuRoot;
159 : static nsIRDFResource * kNC_MailviewCharsetMenuRoot;
160 : static nsIRDFResource * kNC_ComposerCharsetMenuRoot;
161 : static nsIRDFResource * kNC_DecodersRoot;
162 : static nsIRDFResource * kNC_EncodersRoot;
163 : static nsIRDFResource * kNC_Name;
164 : static nsIRDFResource * kNC_CharsetDetector;
165 : static nsIRDFResource * kNC_BookmarkSeparator;
166 : static nsIRDFResource * kRDF_type;
167 :
168 : static nsIRDFDataSource * mInner;
169 :
170 : bool mInitialized;
171 : bool mBrowserMenuInitialized;
172 : bool mMailviewMenuInitialized;
173 : bool mComposerMenuInitialized;
174 : bool mMaileditMenuInitialized;
175 : bool mSecondaryTiersInitialized;
176 : bool mAutoDetectInitialized;
177 : bool mOthersInitialized;
178 :
179 : nsTArray<nsMenuEntry*> mBrowserMenu;
180 : PRInt32 mBrowserCacheStart;
181 : PRInt32 mBrowserCacheSize;
182 : PRInt32 mBrowserMenuRDFPosition;
183 :
184 : nsTArray<nsMenuEntry*> mMailviewMenu;
185 : PRInt32 mMailviewCacheStart;
186 : PRInt32 mMailviewCacheSize;
187 : PRInt32 mMailviewMenuRDFPosition;
188 :
189 : nsTArray<nsMenuEntry*> mComposerMenu;
190 : PRInt32 mComposerCacheStart;
191 : PRInt32 mComposerCacheSize;
192 : PRInt32 mComposerMenuRDFPosition;
193 :
194 : nsCOMPtr<nsIRDFService> mRDFService;
195 : nsCOMPtr<nsICharsetConverterManager> mCCManager;
196 : nsCOMPtr<nsIPrefBranch> mPrefs;
197 : nsCOMPtr<nsIObserver> mCharsetMenuObserver;
198 : nsTArray<nsCString> mDecoderList;
199 :
200 : nsresult Done();
201 :
202 : nsresult FreeResources();
203 :
204 : nsresult InitStaticMenu(nsTArray<nsCString>& aDecs,
205 : nsIRDFResource * aResource,
206 : const char * aKey,
207 : nsTArray<nsMenuEntry*> * aArray);
208 : nsresult InitCacheMenu(nsTArray<nsCString>& aDecs,
209 : nsIRDFResource * aResource,
210 : const char * aKey,
211 : nsTArray<nsMenuEntry*> * aArray);
212 :
213 : nsresult InitMoreMenu(nsTArray<nsCString>& aDecs,
214 : nsIRDFResource * aResource,
215 : const char * aFlag);
216 :
217 : nsresult InitMoreSubmenus(nsTArray<nsCString>& aDecs);
218 :
219 : static nsresult SetArrayFromEnumerator(nsIUTF8StringEnumerator* aEnumerator,
220 : nsTArray<nsCString>& aArray);
221 :
222 : nsresult AddCharsetToItemArray(nsTArray<nsMenuEntry*>* aArray,
223 : const nsAFlatCString& aCharset,
224 : nsMenuEntry ** aResult,
225 : PRInt32 aPlace);
226 : nsresult AddCharsetArrayToItemArray(nsTArray<nsMenuEntry*> &aArray,
227 : const nsTArray<nsCString>& aCharsets);
228 : nsresult AddMenuItemToContainer(nsIRDFContainer * aContainer,
229 : nsMenuEntry * aItem, nsIRDFResource * aType, const char * aIDPrefix,
230 : PRInt32 aPlace);
231 : nsresult AddMenuItemArrayToContainer(nsIRDFContainer * aContainer,
232 : nsTArray<nsMenuEntry*> * aArray, nsIRDFResource * aType);
233 : nsresult AddCharsetToContainer(nsTArray<nsMenuEntry*> * aArray,
234 : nsIRDFContainer * aContainer,
235 : const nsAFlatCString& aCharset,
236 : const char * aIDPrefix,
237 : PRInt32 aPlace, PRInt32 aRDFPlace);
238 :
239 : nsresult AddFromPrefsToMenu(nsTArray<nsMenuEntry*> * aArray,
240 : nsIRDFContainer * aContainer,
241 : const char * aKey,
242 : nsTArray<nsCString>& aDecs,
243 : const char * aIDPrefix);
244 : nsresult AddFromNolocPrefsToMenu(nsTArray<nsMenuEntry*> * aArray,
245 : nsIRDFContainer * aContainer,
246 : const char * aKey,
247 : nsTArray<nsCString>& aDecs,
248 : const char * aIDPrefix);
249 : nsresult AddFromStringToMenu(char * aCharsetList,
250 : nsTArray<nsMenuEntry*> * aArray,
251 : nsIRDFContainer * aContainer,
252 : nsTArray<nsCString>& aDecs,
253 : const char * aIDPrefix);
254 :
255 : nsresult AddSeparatorToContainer(nsIRDFContainer * aContainer);
256 : nsresult AddCharsetToCache(const nsAFlatCString& aCharset,
257 : nsTArray<nsMenuEntry*> * aArray,
258 : nsIRDFResource * aRDFResource,
259 : PRUint32 aCacheStart, PRUint32 aCacheSize,
260 : PRInt32 aRDFPlace);
261 :
262 : nsresult WriteCacheToPrefs(nsTArray<nsMenuEntry*> * aArray, PRInt32 aCacheStart,
263 : const char * aKey);
264 : nsresult UpdateCachePrefs(const char * aCacheKey, const char * aCacheSizeKey,
265 : const char * aStaticKey, const PRUnichar * aCharset);
266 :
267 : nsresult ClearMenu(nsIRDFContainer * aContainer, nsTArray<nsMenuEntry*> * aArray);
268 : nsresult RemoveLastMenuItem(nsIRDFContainer * aContainer,
269 : nsTArray<nsMenuEntry*> * aArray);
270 :
271 : nsresult RemoveFlaggedCharsets(nsTArray<nsCString>& aList, const nsString& aProp);
272 : nsresult NewRDFContainer(nsIRDFDataSource * aDataSource,
273 : nsIRDFResource * aResource, nsIRDFContainer ** aResult);
274 : void FreeMenuItemArray(nsTArray<nsMenuEntry*> * aArray);
275 : PRInt32 FindMenuItemInArray(const nsTArray<nsMenuEntry*>* aArray,
276 : const nsAFlatCString& aCharset,
277 : nsMenuEntry ** aResult);
278 : nsresult ReorderMenuItemArray(nsTArray<nsMenuEntry*> * aArray);
279 : nsresult GetCollation(nsICollation ** aCollation);
280 :
281 : public:
282 : nsCharsetMenu();
283 : virtual ~nsCharsetMenu();
284 :
285 : nsresult Init();
286 : nsresult InitBrowserMenu();
287 : nsresult InitMaileditMenu();
288 : nsresult InitMailviewMenu();
289 : nsresult InitComposerMenu();
290 : nsresult InitOthers();
291 : nsresult InitSecondaryTiers();
292 : nsresult InitAutodetMenu();
293 : nsresult RefreshBrowserMenu();
294 : nsresult RefreshMailviewMenu();
295 : nsresult RefreshMaileditMenu();
296 : nsresult RefreshComposerMenu();
297 :
298 : //--------------------------------------------------------------------------
299 : // Interface nsICurrentCharsetListener [declaration]
300 :
301 : NS_IMETHOD SetCurrentCharset(const PRUnichar * aCharset);
302 : NS_IMETHOD SetCurrentMailCharset(const PRUnichar * aCharset);
303 : NS_IMETHOD SetCurrentComposerCharset(const PRUnichar * aCharset);
304 :
305 : //--------------------------------------------------------------------------
306 : // Interface nsIRDFDataSource [declaration]
307 :
308 : NS_DECL_NSIRDFDATASOURCE
309 : };
310 :
311 : //----------------------------------------------------------------------------
312 : // Global functions and data [implementation]
313 :
314 : nsresult
315 0 : NS_NewCharsetMenu(nsISupports * aOuter, const nsIID & aIID,
316 : void ** aResult)
317 : {
318 0 : if (!aResult) {
319 0 : return NS_ERROR_NULL_POINTER;
320 : }
321 0 : if (aOuter) {
322 0 : *aResult = nsnull;
323 0 : return NS_ERROR_NO_AGGREGATION;
324 : }
325 0 : nsCharsetMenu* inst = new nsCharsetMenu();
326 0 : if (!inst) {
327 0 : *aResult = nsnull;
328 0 : return NS_ERROR_OUT_OF_MEMORY;
329 : }
330 0 : nsresult res = inst->QueryInterface(aIID, aResult);
331 0 : if (NS_FAILED(res)) {
332 0 : *aResult = nsnull;
333 0 : delete inst;
334 : }
335 0 : return res;
336 : }
337 :
338 : struct charsetMenuSortRecord {
339 : nsMenuEntry* item;
340 : PRUint8* key;
341 : PRUint32 len;
342 :
343 : };
344 :
345 0 : static int CompareMenuItems(const void* aArg1, const void* aArg2, void *data)
346 : {
347 : PRInt32 res;
348 0 : nsICollation * collation = (nsICollation *) data;
349 0 : charsetMenuSortRecord *rec1 = (charsetMenuSortRecord *) aArg1;
350 0 : charsetMenuSortRecord *rec2 = (charsetMenuSortRecord *) aArg2;
351 :
352 0 : collation->CompareRawSortKey(rec1->key, rec1->len, rec2->key, rec2->len, &res);
353 :
354 0 : return res;
355 : }
356 :
357 : nsresult
358 0 : nsCharsetMenu::SetArrayFromEnumerator(nsIUTF8StringEnumerator* aEnumerator,
359 : nsTArray<nsCString>& aArray)
360 : {
361 : nsresult rv;
362 :
363 : bool hasMore;
364 0 : rv = aEnumerator->HasMore(&hasMore);
365 :
366 0 : nsCAutoString value;
367 0 : while (NS_SUCCEEDED(rv) && hasMore) {
368 0 : rv = aEnumerator->GetNext(value);
369 0 : if (NS_SUCCEEDED(rv))
370 0 : aArray.AppendElement(value);
371 :
372 0 : rv = aEnumerator->HasMore(&hasMore);
373 : }
374 :
375 0 : return rv;
376 : }
377 :
378 :
379 : class nsIgnoreCaseCStringComparator
380 : {
381 : public:
382 0 : bool Equals(const nsACString& a, const nsACString& b) const
383 : {
384 0 : return nsCString(a).Equals(b, nsCaseInsensitiveCStringComparator());
385 : }
386 :
387 : bool LessThan(const nsACString& a, const nsACString& b) const
388 : {
389 : return a < b;
390 : }
391 : };
392 :
393 : //----------------------------------------------------------------------------
394 : // Class nsCharsetMenuObserver
395 :
396 : class nsCharsetMenuObserver : public nsIObserver {
397 :
398 : public:
399 : NS_DECL_ISUPPORTS
400 : NS_DECL_NSIOBSERVER
401 :
402 0 : nsCharsetMenuObserver(nsCharsetMenu * menu)
403 0 : : mCharsetMenu(menu)
404 : {
405 0 : }
406 :
407 0 : virtual ~nsCharsetMenuObserver() {}
408 :
409 : private:
410 : nsCharsetMenu* mCharsetMenu;
411 : };
412 :
413 0 : NS_IMPL_ISUPPORTS1(nsCharsetMenuObserver, nsIObserver)
414 :
415 0 : NS_IMETHODIMP nsCharsetMenuObserver::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
416 : {
417 0 : nsresult rv = NS_OK;
418 :
419 : //XUL event handler
420 0 : if (!nsCRT::strcmp(aTopic, "charsetmenu-selected")) {
421 0 : nsDependentString nodeName(someData);
422 0 : rv = mCharsetMenu->Init();
423 0 : if (nodeName.EqualsLiteral("browser")) {
424 0 : rv = mCharsetMenu->InitBrowserMenu();
425 : }
426 0 : if (nodeName.EqualsLiteral("composer")) {
427 0 : rv = mCharsetMenu->InitComposerMenu();
428 : }
429 0 : if (nodeName.EqualsLiteral("mailview")) {
430 0 : rv = mCharsetMenu->InitMailviewMenu();
431 : }
432 0 : if (nodeName.EqualsLiteral("mailedit")) {
433 0 : rv = mCharsetMenu->InitMaileditMenu();
434 0 : rv = mCharsetMenu->InitOthers();
435 : }
436 0 : if (nodeName.EqualsLiteral("more-menu")) {
437 0 : rv = mCharsetMenu->InitSecondaryTiers();
438 0 : rv = mCharsetMenu->InitAutodetMenu();
439 : }
440 0 : if (nodeName.EqualsLiteral("other")) {
441 0 : rv = mCharsetMenu->InitOthers();
442 0 : rv = mCharsetMenu->InitMaileditMenu();
443 : }
444 : }
445 :
446 : //pref event handler
447 0 : if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
448 0 : nsDependentString prefName(someData);
449 :
450 0 : if (prefName.EqualsLiteral(kBrowserStaticPrefKey)) {
451 : // refresh menus which share this pref
452 0 : rv = mCharsetMenu->RefreshBrowserMenu();
453 0 : NS_ENSURE_SUCCESS(rv, rv);
454 0 : rv = mCharsetMenu->RefreshMailviewMenu();
455 0 : NS_ENSURE_SUCCESS(rv, rv);
456 0 : rv = mCharsetMenu->RefreshComposerMenu();
457 : }
458 0 : else if (prefName.EqualsLiteral(kMaileditPrefKey)) {
459 0 : rv = mCharsetMenu->RefreshMaileditMenu();
460 : }
461 : }
462 :
463 0 : return rv;
464 : }
465 :
466 : //----------------------------------------------------------------------------
467 : // Class nsCharsetMenu [implementation]
468 :
469 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsCharsetMenu)
470 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsCharsetMenu)
471 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCharsetMenu)
472 0 : cb.NoteXPCOMChild(nsCharsetMenu::mInner);
473 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
474 :
475 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCharsetMenu)
476 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCharsetMenu)
477 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCharsetMenu)
478 0 : NS_INTERFACE_MAP_ENTRY(nsIRDFDataSource)
479 0 : NS_INTERFACE_MAP_ENTRY(nsICurrentCharsetListener)
480 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRDFDataSource)
481 0 : NS_INTERFACE_MAP_END
482 :
483 : nsIRDFDataSource * nsCharsetMenu::mInner = NULL;
484 : nsIRDFResource * nsCharsetMenu::kNC_BrowserAutodetMenuRoot = NULL;
485 : nsIRDFResource * nsCharsetMenu::kNC_BrowserCharsetMenuRoot = NULL;
486 : nsIRDFResource * nsCharsetMenu::kNC_BrowserMoreCharsetMenuRoot = NULL;
487 : nsIRDFResource * nsCharsetMenu::kNC_BrowserMore1CharsetMenuRoot = NULL;
488 : nsIRDFResource * nsCharsetMenu::kNC_BrowserMore2CharsetMenuRoot = NULL;
489 : nsIRDFResource * nsCharsetMenu::kNC_BrowserMore3CharsetMenuRoot = NULL;
490 : nsIRDFResource * nsCharsetMenu::kNC_BrowserMore4CharsetMenuRoot = NULL;
491 : nsIRDFResource * nsCharsetMenu::kNC_BrowserMore5CharsetMenuRoot = NULL;
492 : nsIRDFResource * nsCharsetMenu::kNC_BrowserUnicodeCharsetMenuRoot = NULL;
493 : nsIRDFResource * nsCharsetMenu::kNC_MaileditCharsetMenuRoot = NULL;
494 : nsIRDFResource * nsCharsetMenu::kNC_MailviewCharsetMenuRoot = NULL;
495 : nsIRDFResource * nsCharsetMenu::kNC_ComposerCharsetMenuRoot = NULL;
496 : nsIRDFResource * nsCharsetMenu::kNC_DecodersRoot = NULL;
497 : nsIRDFResource * nsCharsetMenu::kNC_EncodersRoot = NULL;
498 : nsIRDFResource * nsCharsetMenu::kNC_Name = NULL;
499 : nsIRDFResource * nsCharsetMenu::kNC_CharsetDetector = NULL;
500 : nsIRDFResource * nsCharsetMenu::kNC_BookmarkSeparator = NULL;
501 : nsIRDFResource * nsCharsetMenu::kRDF_type = NULL;
502 :
503 0 : nsCharsetMenu::nsCharsetMenu()
504 : : mInitialized(false),
505 : mBrowserMenuInitialized(false),
506 : mMailviewMenuInitialized(false),
507 : mComposerMenuInitialized(false),
508 : mMaileditMenuInitialized(false),
509 : mSecondaryTiersInitialized(false),
510 : mAutoDetectInitialized(false),
511 0 : mOthersInitialized(false)
512 : {
513 0 : nsresult res = NS_OK;
514 :
515 : //get charset manager
516 0 : mCCManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res);
517 :
518 : //initialize skeleton RDF source
519 0 : mRDFService = do_GetService(kRDFServiceCID, &res);
520 :
521 0 : if (NS_SUCCEEDED(res)) {
522 0 : mRDFService->RegisterDataSource(this, false);
523 :
524 0 : CallCreateInstance(kRDFInMemoryDataSourceCID, &mInner);
525 :
526 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserCharsetMenuRoot),
527 0 : &kNC_BrowserCharsetMenuRoot);
528 : }
529 :
530 : //get pref service
531 0 : nsCOMPtr<nsIPrefService> PrefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &res);
532 0 : if (NS_SUCCEEDED(res))
533 0 : res = PrefService->GetBranch(nsnull, getter_AddRefs(mPrefs));
534 :
535 : //register event listener
536 0 : mCharsetMenuObserver = new nsCharsetMenuObserver(this);
537 :
538 0 : if (mCharsetMenuObserver) {
539 : nsCOMPtr<nsIObserverService> observerService =
540 0 : do_GetService("@mozilla.org/observer-service;1", &res);
541 :
542 0 : if (NS_SUCCEEDED(res))
543 0 : res = observerService->AddObserver(mCharsetMenuObserver,
544 : "charsetmenu-selected",
545 0 : false);
546 : }
547 :
548 0 : NS_ASSERTION(NS_SUCCEEDED(res), "Failed to initialize nsCharsetMenu");
549 0 : }
550 :
551 0 : nsCharsetMenu::~nsCharsetMenu()
552 : {
553 0 : Done();
554 :
555 0 : FreeMenuItemArray(&mBrowserMenu);
556 0 : FreeMenuItemArray(&mMailviewMenu);
557 0 : FreeMenuItemArray(&mComposerMenu);
558 :
559 0 : FreeResources();
560 0 : }
561 :
562 : // XXX collapse these 2 in one
563 :
564 0 : nsresult nsCharsetMenu::RefreshBrowserMenu()
565 : {
566 0 : nsresult res = NS_OK;
567 :
568 0 : nsCOMPtr<nsIRDFContainer> container;
569 0 : res = NewRDFContainer(mInner, kNC_BrowserCharsetMenuRoot, getter_AddRefs(container));
570 0 : if (NS_FAILED(res)) return res;
571 :
572 : // clean the menu
573 0 : res = ClearMenu(container, &mBrowserMenu);
574 0 : if (NS_FAILED(res)) return res;
575 :
576 : // rebuild the menu
577 0 : nsCOMPtr<nsIUTF8StringEnumerator> decoders;
578 0 : res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
579 0 : if (NS_FAILED(res)) return res;
580 :
581 0 : nsTArray<nsCString> decs;
582 0 : SetArrayFromEnumerator(decoders, decs);
583 :
584 : res = AddFromPrefsToMenu(&mBrowserMenu, container, kBrowserStaticPrefKey,
585 0 : decs, "charset.");
586 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
587 :
588 : // mark the end of the static area, the rest is cache
589 0 : mBrowserCacheStart = mBrowserMenu.Length();
590 :
591 : // Remove "notForBrowser" entries before populating cache menu
592 0 : res = RemoveFlaggedCharsets(decs, NS_LITERAL_STRING(".notForBrowser"));
593 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error removing flagged charsets");
594 :
595 : res = InitCacheMenu(decs, kNC_BrowserCharsetMenuRoot, kBrowserCachePrefKey,
596 0 : &mBrowserMenu);
597 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing browser cache charset menu");
598 :
599 0 : return res;
600 : }
601 :
602 0 : nsresult nsCharsetMenu::RefreshMailviewMenu()
603 : {
604 0 : nsresult res = NS_OK;
605 :
606 0 : nsCOMPtr<nsIRDFContainer> container;
607 0 : res = NewRDFContainer(mInner, kNC_MailviewCharsetMenuRoot, getter_AddRefs(container));
608 0 : if (NS_FAILED(res)) return res;
609 :
610 : // clean the menu
611 0 : res = ClearMenu(container, &mMailviewMenu);
612 0 : if (NS_FAILED(res)) return res;
613 :
614 0 : nsCOMPtr<nsIUTF8StringEnumerator> decoders;
615 0 : res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
616 0 : if (NS_FAILED(res)) return res;
617 :
618 0 : nsTArray<nsCString> decs;
619 0 : SetArrayFromEnumerator(decoders, decs);
620 :
621 : res = AddFromPrefsToMenu(&mMailviewMenu, container, kMailviewStaticPrefKey,
622 0 : decs, "charset.");
623 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
624 :
625 : // mark the end of the static area, the rest is cache
626 0 : mMailviewCacheStart = mMailviewMenu.Length();
627 :
628 : res = InitCacheMenu(decs, kNC_MailviewCharsetMenuRoot,
629 0 : kMailviewCachePrefKey, &mMailviewMenu);
630 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailview cache charset menu");
631 :
632 0 : return res;
633 : }
634 :
635 0 : nsresult nsCharsetMenu::RefreshMaileditMenu()
636 : {
637 : nsresult res;
638 :
639 0 : nsCOMPtr<nsIRDFContainer> container;
640 0 : res = NewRDFContainer(mInner, kNC_MaileditCharsetMenuRoot, getter_AddRefs(container));
641 0 : NS_ENSURE_SUCCESS(res, res);
642 :
643 0 : nsCOMPtr<nsISimpleEnumerator> enumerator;
644 0 : res = container->GetElements(getter_AddRefs(enumerator));
645 0 : NS_ENSURE_SUCCESS(res, res);
646 :
647 : // clear the menu
648 0 : nsCOMPtr<nsIRDFNode> node;
649 0 : while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(node)))) {
650 :
651 0 : res = mInner->Unassert(kNC_MaileditCharsetMenuRoot, kNC_Name, node);
652 0 : NS_ENSURE_SUCCESS(res, res);
653 :
654 0 : res = container->RemoveElement(node, false);
655 0 : NS_ENSURE_SUCCESS(res, res);
656 : }
657 :
658 : // get a list of available encoders
659 0 : nsCOMPtr<nsIUTF8StringEnumerator> encoders;
660 0 : res = mCCManager->GetEncoderList(getter_AddRefs(encoders));
661 0 : NS_ENSURE_SUCCESS(res, res);
662 :
663 0 : nsTArray<nsCString> encs;
664 0 : SetArrayFromEnumerator(encoders, encs);
665 :
666 : // add menu items from pref
667 0 : res = AddFromPrefsToMenu(NULL, container, kMaileditPrefKey, encs, NULL);
668 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailedit charset menu from prefs");
669 :
670 0 : return res;
671 : }
672 :
673 0 : nsresult nsCharsetMenu::RefreshComposerMenu()
674 : {
675 0 : nsresult res = NS_OK;
676 :
677 0 : nsCOMPtr<nsIRDFContainer> container;
678 0 : res = NewRDFContainer(mInner, kNC_ComposerCharsetMenuRoot, getter_AddRefs(container));
679 0 : if (NS_FAILED(res)) return res;
680 :
681 : // clean the menu
682 0 : res = ClearMenu(container, &mComposerMenu);
683 0 : if (NS_FAILED(res)) return res;
684 :
685 0 : nsCOMPtr<nsIUTF8StringEnumerator> decoders;
686 0 : res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
687 0 : if (NS_FAILED(res)) return res;
688 :
689 0 : nsTArray<nsCString> decs;
690 0 : SetArrayFromEnumerator(decoders, decs);
691 :
692 : res = AddFromPrefsToMenu(&mComposerMenu, container, kComposerStaticPrefKey,
693 0 : decs, "charset.");
694 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
695 :
696 : // mark the end of the static area, the rest is cache
697 0 : mComposerCacheStart = mComposerMenu.Length();
698 :
699 : res = InitCacheMenu(decs, kNC_ComposerCharsetMenuRoot,
700 0 : kComposerCachePrefKey, &mComposerMenu);
701 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing composer cache charset menu");
702 :
703 0 : return res;
704 : }
705 :
706 0 : nsresult nsCharsetMenu::Init()
707 : {
708 0 : nsresult res = NS_OK;
709 :
710 0 : if (!mInitialized) {
711 :
712 : //enumerate decoders
713 0 : nsCOMPtr<nsIUTF8StringEnumerator> decoders;
714 0 : res = mCCManager->GetDecoderList(getter_AddRefs(decoders));
715 0 : if (NS_FAILED(res)) return res;
716 :
717 0 : SetArrayFromEnumerator(decoders, mDecoderList);
718 :
719 : //initialize all remaining RDF template nodes
720 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserAutodetMenuRoot),
721 0 : &kNC_BrowserAutodetMenuRoot);
722 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMoreCharsetMenuRoot),
723 0 : &kNC_BrowserMoreCharsetMenuRoot);
724 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore1CharsetMenuRoot),
725 0 : &kNC_BrowserMore1CharsetMenuRoot);
726 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore2CharsetMenuRoot),
727 0 : &kNC_BrowserMore2CharsetMenuRoot);
728 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore3CharsetMenuRoot),
729 0 : &kNC_BrowserMore3CharsetMenuRoot);
730 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore4CharsetMenuRoot),
731 0 : &kNC_BrowserMore4CharsetMenuRoot);
732 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserMore5CharsetMenuRoot),
733 0 : &kNC_BrowserMore5CharsetMenuRoot);
734 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BrowserUnicodeCharsetMenuRoot),
735 0 : &kNC_BrowserUnicodeCharsetMenuRoot);
736 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_MaileditCharsetMenuRoot),
737 0 : &kNC_MaileditCharsetMenuRoot);
738 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_MailviewCharsetMenuRoot),
739 0 : &kNC_MailviewCharsetMenuRoot);
740 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_ComposerCharsetMenuRoot),
741 0 : &kNC_ComposerCharsetMenuRoot);
742 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_DecodersRoot),
743 0 : &kNC_DecodersRoot);
744 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_EncodersRoot),
745 0 : &kNC_EncodersRoot);
746 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_Name),
747 0 : &kNC_Name);
748 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_CharsetDetector),
749 0 : &kNC_CharsetDetector);
750 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_BookmarkSeparator),
751 0 : &kNC_BookmarkSeparator);
752 0 : mRDFService->GetResource(NS_LITERAL_CSTRING(kURINC_type), &kRDF_type);
753 :
754 0 : nsIRDFContainerUtils * rdfUtil = NULL;
755 0 : res = CallGetService(kRDFContainerUtilsCID, &rdfUtil);
756 0 : if (NS_FAILED(res)) goto done;
757 :
758 0 : res = rdfUtil->MakeSeq(mInner, kNC_BrowserAutodetMenuRoot, NULL);
759 0 : if (NS_FAILED(res)) goto done;
760 0 : res = rdfUtil->MakeSeq(mInner, kNC_BrowserCharsetMenuRoot, NULL);
761 0 : if (NS_FAILED(res)) goto done;
762 0 : res = rdfUtil->MakeSeq(mInner, kNC_BrowserMoreCharsetMenuRoot, NULL);
763 0 : if (NS_FAILED(res)) goto done;
764 0 : res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore1CharsetMenuRoot, NULL);
765 0 : if (NS_FAILED(res)) goto done;
766 0 : res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore2CharsetMenuRoot, NULL);
767 0 : if (NS_FAILED(res)) goto done;
768 0 : res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore3CharsetMenuRoot, NULL);
769 0 : if (NS_FAILED(res)) goto done;
770 0 : res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore4CharsetMenuRoot, NULL);
771 0 : if (NS_FAILED(res)) goto done;
772 0 : res = rdfUtil->MakeSeq(mInner, kNC_BrowserMore5CharsetMenuRoot, NULL);
773 0 : if (NS_FAILED(res)) goto done;
774 0 : res = rdfUtil->MakeSeq(mInner, kNC_BrowserUnicodeCharsetMenuRoot, NULL);
775 0 : if (NS_FAILED(res)) goto done;
776 0 : res = rdfUtil->MakeSeq(mInner, kNC_MaileditCharsetMenuRoot, NULL);
777 0 : if (NS_FAILED(res)) goto done;
778 0 : res = rdfUtil->MakeSeq(mInner, kNC_MailviewCharsetMenuRoot, NULL);
779 0 : if (NS_FAILED(res)) goto done;
780 0 : res = rdfUtil->MakeSeq(mInner, kNC_ComposerCharsetMenuRoot, NULL);
781 0 : if (NS_FAILED(res)) goto done;
782 0 : res = rdfUtil->MakeSeq(mInner, kNC_DecodersRoot, NULL);
783 0 : if (NS_FAILED(res)) goto done;
784 0 : res = rdfUtil->MakeSeq(mInner, kNC_EncodersRoot, NULL);
785 0 : if (NS_FAILED(res)) goto done;
786 :
787 : done:
788 0 : NS_IF_RELEASE(rdfUtil);
789 0 : if (NS_FAILED(res)) return res;
790 : }
791 0 : mInitialized = NS_SUCCEEDED(res);
792 0 : return res;
793 : }
794 :
795 0 : nsresult nsCharsetMenu::Done()
796 : {
797 0 : nsresult res = NS_OK;
798 0 : res = mRDFService->UnregisterDataSource(this);
799 :
800 0 : NS_IF_RELEASE(kNC_BrowserAutodetMenuRoot);
801 0 : NS_IF_RELEASE(kNC_BrowserCharsetMenuRoot);
802 0 : NS_IF_RELEASE(kNC_BrowserMoreCharsetMenuRoot);
803 0 : NS_IF_RELEASE(kNC_BrowserMore1CharsetMenuRoot);
804 0 : NS_IF_RELEASE(kNC_BrowserMore2CharsetMenuRoot);
805 0 : NS_IF_RELEASE(kNC_BrowserMore3CharsetMenuRoot);
806 0 : NS_IF_RELEASE(kNC_BrowserMore4CharsetMenuRoot);
807 0 : NS_IF_RELEASE(kNC_BrowserMore5CharsetMenuRoot);
808 0 : NS_IF_RELEASE(kNC_BrowserUnicodeCharsetMenuRoot);
809 0 : NS_IF_RELEASE(kNC_MaileditCharsetMenuRoot);
810 0 : NS_IF_RELEASE(kNC_MailviewCharsetMenuRoot);
811 0 : NS_IF_RELEASE(kNC_ComposerCharsetMenuRoot);
812 0 : NS_IF_RELEASE(kNC_DecodersRoot);
813 0 : NS_IF_RELEASE(kNC_EncodersRoot);
814 0 : NS_IF_RELEASE(kNC_Name);
815 0 : NS_IF_RELEASE(kNC_CharsetDetector);
816 0 : NS_IF_RELEASE(kNC_BookmarkSeparator);
817 0 : NS_IF_RELEASE(kRDF_type);
818 0 : NS_IF_RELEASE(mInner);
819 :
820 0 : return res;
821 : }
822 :
823 : /**
824 : * Free the resources no longer needed by the component.
825 : */
826 0 : nsresult nsCharsetMenu::FreeResources()
827 : {
828 0 : nsresult res = NS_OK;
829 :
830 0 : if (mCharsetMenuObserver) {
831 0 : mPrefs->RemoveObserver(kBrowserStaticPrefKey, mCharsetMenuObserver);
832 0 : mPrefs->RemoveObserver(kMaileditPrefKey, mCharsetMenuObserver);
833 : /* nsIObserverService has to have released nsCharsetMenu already */
834 : }
835 :
836 0 : mRDFService = NULL;
837 0 : mCCManager = NULL;
838 0 : mPrefs = NULL;
839 :
840 0 : return res;
841 : }
842 :
843 0 : nsresult nsCharsetMenu::InitBrowserMenu()
844 : {
845 0 : nsresult res = NS_OK;
846 :
847 0 : if (!mBrowserMenuInitialized) {
848 0 : nsCOMPtr<nsIRDFContainer> container;
849 0 : res = NewRDFContainer(mInner, kNC_BrowserCharsetMenuRoot, getter_AddRefs(container));
850 0 : if (NS_FAILED(res)) return res;
851 :
852 :
853 : // how to clone mDecoderList??
854 0 : nsTArray<nsCString> browserDecoderList = mDecoderList;
855 :
856 : res = InitStaticMenu(browserDecoderList, kNC_BrowserCharsetMenuRoot,
857 0 : kBrowserStaticPrefKey, &mBrowserMenu);
858 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing browser static charset menu");
859 :
860 : // mark the end of the static area, the rest is cache
861 0 : mBrowserCacheStart = mBrowserMenu.Length();
862 0 : mPrefs->GetIntPref(kBrowserCacheSizePrefKey, &mBrowserCacheSize);
863 :
864 : // compute the position of the menu in the RDF container
865 0 : res = container->GetCount(&mBrowserMenuRDFPosition);
866 0 : if (NS_FAILED(res)) return res;
867 : // this "1" here is a correction necessary because the RDF container
868 : // elements are numbered from 1 (why god, WHY?!?!?!)
869 0 : mBrowserMenuRDFPosition -= mBrowserCacheStart - 1;
870 :
871 : // Remove "notForBrowser" entries before populating cache menu
872 0 : res = RemoveFlaggedCharsets(browserDecoderList, NS_LITERAL_STRING(".notForBrowser"));
873 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
874 :
875 : res = InitCacheMenu(browserDecoderList, kNC_BrowserCharsetMenuRoot, kBrowserCachePrefKey,
876 0 : &mBrowserMenu);
877 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing browser cache charset menu");
878 :
879 : // register prefs callback
880 0 : mPrefs->AddObserver(kBrowserStaticPrefKey, mCharsetMenuObserver, false);
881 : }
882 :
883 0 : mBrowserMenuInitialized = NS_SUCCEEDED(res);
884 :
885 0 : return res;
886 : }
887 :
888 0 : nsresult nsCharsetMenu::InitMaileditMenu()
889 : {
890 0 : nsresult res = NS_OK;
891 :
892 0 : if (!mMaileditMenuInitialized) {
893 0 : nsCOMPtr<nsIRDFContainer> container;
894 0 : res = NewRDFContainer(mInner, kNC_MaileditCharsetMenuRoot, getter_AddRefs(container));
895 0 : if (NS_FAILED(res)) return res;
896 :
897 : //enumerate encoders
898 : // this would bring in a whole bunch of 'font encoders' as well as genuine
899 : // charset encoders, but it's safe because we rely on prefs to filter
900 : // them out. Moreover, 'customize' menu lists only genuine charset
901 : // encoders further guarding against 'font encoders' sneaking in.
902 0 : nsCOMPtr<nsIUTF8StringEnumerator> encoders;
903 0 : res = mCCManager->GetEncoderList(getter_AddRefs(encoders));
904 0 : if (NS_FAILED(res)) return res;
905 :
906 0 : nsTArray<nsCString> maileditEncoderList;
907 0 : SetArrayFromEnumerator(encoders, maileditEncoderList);
908 :
909 0 : res = AddFromPrefsToMenu(NULL, container, kMaileditPrefKey, maileditEncoderList, NULL);
910 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailedit charset menu from prefs");
911 :
912 : // register prefs callback
913 0 : mPrefs->AddObserver(kMaileditPrefKey, mCharsetMenuObserver, false);
914 : }
915 :
916 0 : mMaileditMenuInitialized = NS_SUCCEEDED(res);
917 :
918 0 : return res;
919 : }
920 :
921 0 : nsresult nsCharsetMenu::InitMailviewMenu()
922 : {
923 0 : nsresult res = NS_OK;
924 :
925 0 : if (!mMailviewMenuInitialized) {
926 0 : nsCOMPtr<nsIRDFContainer> container;
927 0 : res = NewRDFContainer(mInner, kNC_MailviewCharsetMenuRoot, getter_AddRefs(container));
928 0 : if (NS_FAILED(res)) return res;
929 :
930 0 : nsTArray<nsCString> mailviewDecoderList = mDecoderList;
931 :
932 : res = InitStaticMenu(mailviewDecoderList, kNC_MailviewCharsetMenuRoot,
933 0 : kMailviewStaticPrefKey, &mMailviewMenu);
934 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailview static charset menu");
935 :
936 : // mark the end of the static area, the rest is cache
937 0 : mMailviewCacheStart = mMailviewMenu.Length();
938 0 : mPrefs->GetIntPref(kMailviewCacheSizePrefKey, &mMailviewCacheSize);
939 :
940 : // compute the position of the menu in the RDF container
941 0 : res = container->GetCount(&mMailviewMenuRDFPosition);
942 0 : if (NS_FAILED(res)) return res;
943 : // this "1" here is a correction necessary because the RDF container
944 : // elements are numbered from 1 (why god, WHY?!?!?!)
945 0 : mMailviewMenuRDFPosition -= mMailviewCacheStart - 1;
946 :
947 : res = InitCacheMenu(mailviewDecoderList, kNC_MailviewCharsetMenuRoot,
948 0 : kMailviewCachePrefKey, &mMailviewMenu);
949 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing mailview cache charset menu");
950 : }
951 :
952 0 : mMailviewMenuInitialized = NS_SUCCEEDED(res);
953 :
954 0 : return res;
955 : }
956 :
957 0 : nsresult nsCharsetMenu::InitComposerMenu()
958 : {
959 0 : nsresult res = NS_OK;
960 :
961 0 : if (!mComposerMenuInitialized) {
962 0 : nsCOMPtr<nsIRDFContainer> container;
963 0 : res = NewRDFContainer(mInner, kNC_ComposerCharsetMenuRoot, getter_AddRefs(container));
964 0 : if (NS_FAILED(res)) return res;
965 :
966 0 : nsTArray<nsCString> composerDecoderList = mDecoderList;
967 :
968 : // even if we fail, the show must go on
969 : res = InitStaticMenu(composerDecoderList, kNC_ComposerCharsetMenuRoot,
970 0 : kComposerStaticPrefKey, &mComposerMenu);
971 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing composer static charset menu");
972 :
973 : // mark the end of the static area, the rest is cache
974 0 : mComposerCacheStart = mComposerMenu.Length();
975 0 : mPrefs->GetIntPref(kComposerCacheSizePrefKey, &mComposerCacheSize);
976 :
977 : // compute the position of the menu in the RDF container
978 0 : res = container->GetCount(&mComposerMenuRDFPosition);
979 0 : if (NS_FAILED(res)) return res;
980 : // this "1" here is a correction necessary because the RDF container
981 : // elements are numbered from 1 (why god, WHY?!?!?!)
982 0 : mComposerMenuRDFPosition -= mComposerCacheStart - 1;
983 :
984 : res = InitCacheMenu(composerDecoderList, kNC_ComposerCharsetMenuRoot,
985 0 : kComposerCachePrefKey, &mComposerMenu);
986 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing composer cache charset menu");
987 : }
988 :
989 0 : mComposerMenuInitialized = NS_SUCCEEDED(res);
990 :
991 0 : return res;
992 : }
993 :
994 0 : nsresult nsCharsetMenu::InitOthers()
995 : {
996 0 : nsresult res = NS_OK;
997 :
998 0 : if (!mOthersInitialized) {
999 0 : nsTArray<nsCString> othersDecoderList = mDecoderList;
1000 :
1001 0 : res = InitMoreMenu(othersDecoderList, kNC_DecodersRoot, ".notForBrowser");
1002 0 : if (NS_FAILED(res)) return res;
1003 :
1004 : // Using mDecoderList instead of GetEncoderList(), we can avoid having to
1005 : // tag a whole bunch of 'font encoders' with '.notForOutgoing' in
1006 : // charsetData.properties file.
1007 0 : nsTArray<nsCString> othersEncoderList = mDecoderList;
1008 :
1009 0 : res = InitMoreMenu(othersEncoderList, kNC_EncodersRoot, ".notForOutgoing");
1010 0 : if (NS_FAILED(res)) return res;
1011 : }
1012 :
1013 0 : mOthersInitialized = NS_SUCCEEDED(res);
1014 :
1015 0 : return res;
1016 : }
1017 :
1018 : /**
1019 : * Inits the secondary tiers of the charset menu. Because currently all the CS
1020 : * menus are sharing the secondary tiers, one should call this method only
1021 : * once for all of them.
1022 : */
1023 0 : nsresult nsCharsetMenu::InitSecondaryTiers()
1024 : {
1025 0 : nsresult res = NS_OK;
1026 :
1027 0 : if (!mSecondaryTiersInitialized) {
1028 0 : nsTArray<nsCString> secondaryTiersDecoderList = mDecoderList;
1029 :
1030 0 : res = InitMoreSubmenus(secondaryTiersDecoderList);
1031 0 : NS_ASSERTION(NS_SUCCEEDED(res), "err init browser charset more submenus");
1032 :
1033 0 : res = InitMoreMenu(secondaryTiersDecoderList, kNC_BrowserMoreCharsetMenuRoot, ".notForBrowser");
1034 0 : NS_ASSERTION(NS_SUCCEEDED(res), "err init browser charset more menu");
1035 : }
1036 :
1037 0 : mSecondaryTiersInitialized = NS_SUCCEEDED(res);
1038 :
1039 0 : return res;
1040 : }
1041 :
1042 0 : nsresult nsCharsetMenu::InitStaticMenu(nsTArray<nsCString>& aDecs,
1043 : nsIRDFResource * aResource,
1044 : const char * aKey,
1045 : nsTArray<nsMenuEntry*> * aArray)
1046 : {
1047 0 : nsresult res = NS_OK;
1048 0 : nsCOMPtr<nsIRDFContainer> container;
1049 :
1050 0 : res = NewRDFContainer(mInner, aResource, getter_AddRefs(container));
1051 0 : if (NS_FAILED(res)) return res;
1052 :
1053 : // XXX work around bug that causes the submenus to be first instead of last
1054 0 : res = AddSeparatorToContainer(container);
1055 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error adding separator to container");
1056 :
1057 0 : res = AddFromPrefsToMenu(aArray, container, aKey, aDecs, "charset.");
1058 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing static charset menu from prefs");
1059 :
1060 0 : return res;
1061 : }
1062 :
1063 0 : nsresult nsCharsetMenu::InitCacheMenu(
1064 : nsTArray<nsCString>& aDecs,
1065 : nsIRDFResource * aResource,
1066 : const char * aKey,
1067 : nsTArray<nsMenuEntry*> * aArray)
1068 : {
1069 0 : nsresult res = NS_OK;
1070 0 : nsCOMPtr<nsIRDFContainer> container;
1071 :
1072 0 : res = NewRDFContainer(mInner, aResource, getter_AddRefs(container));
1073 0 : if (NS_FAILED(res)) return res;
1074 :
1075 0 : res = AddFromNolocPrefsToMenu(aArray, container, aKey, aDecs, "charset.");
1076 0 : NS_ASSERTION(NS_SUCCEEDED(res), "error initializing cache charset menu from prefs");
1077 :
1078 0 : return res;
1079 : }
1080 :
1081 0 : nsresult nsCharsetMenu::InitAutodetMenu()
1082 : {
1083 0 : nsresult res = NS_OK;
1084 :
1085 0 : if (!mAutoDetectInitialized) {
1086 0 : nsTArray<nsMenuEntry*> chardetArray;
1087 0 : nsCOMPtr<nsIRDFContainer> container;
1088 0 : nsTArray<nsCString> detectorArray;
1089 :
1090 0 : res = NewRDFContainer(mInner, kNC_BrowserAutodetMenuRoot, getter_AddRefs(container));
1091 0 : if (NS_FAILED(res)) return res;
1092 :
1093 0 : nsCOMPtr<nsIUTF8StringEnumerator> detectors;
1094 0 : res = mCCManager->GetCharsetDetectorList(getter_AddRefs(detectors));
1095 0 : if (NS_FAILED(res)) goto done;
1096 :
1097 0 : res = SetArrayFromEnumerator(detectors, detectorArray);
1098 0 : if (NS_FAILED(res)) goto done;
1099 :
1100 0 : res = AddCharsetArrayToItemArray(chardetArray, detectorArray);
1101 0 : if (NS_FAILED(res)) goto done;
1102 :
1103 : // reorder the array
1104 0 : res = ReorderMenuItemArray(&chardetArray);
1105 0 : if (NS_FAILED(res)) goto done;
1106 :
1107 : res = AddMenuItemArrayToContainer(container, &chardetArray,
1108 0 : kNC_CharsetDetector);
1109 0 : if (NS_FAILED(res)) goto done;
1110 :
1111 : done:
1112 : // free the elements in the nsTArray<nsMenuEntry*>
1113 0 : FreeMenuItemArray(&chardetArray);
1114 : }
1115 :
1116 0 : mAutoDetectInitialized = NS_SUCCEEDED(res);
1117 :
1118 0 : return res;
1119 : }
1120 :
1121 0 : nsresult nsCharsetMenu::InitMoreMenu(nsTArray<nsCString>& aDecs,
1122 : nsIRDFResource * aResource,
1123 : const char * aFlag)
1124 : {
1125 0 : nsresult res = NS_OK;
1126 0 : nsCOMPtr<nsIRDFContainer> container;
1127 0 : nsTArray<nsMenuEntry*> moreMenu;
1128 :
1129 0 : res = NewRDFContainer(mInner, aResource, getter_AddRefs(container));
1130 0 : if (NS_FAILED(res)) goto done;
1131 :
1132 : // remove charsets "not for browser"
1133 0 : res = RemoveFlaggedCharsets(aDecs, NS_ConvertASCIItoUTF16(aFlag));
1134 0 : if (NS_FAILED(res)) goto done;
1135 :
1136 0 : res = AddCharsetArrayToItemArray(moreMenu, aDecs);
1137 0 : if (NS_FAILED(res)) goto done;
1138 :
1139 : // reorder the array
1140 0 : res = ReorderMenuItemArray(&moreMenu);
1141 0 : if (NS_FAILED(res)) goto done;
1142 :
1143 0 : res = AddMenuItemArrayToContainer(container, &moreMenu, NULL);
1144 0 : if (NS_FAILED(res)) goto done;
1145 :
1146 : done:
1147 : // free the elements in the VoidArray
1148 0 : FreeMenuItemArray(&moreMenu);
1149 :
1150 0 : return res;
1151 : }
1152 :
1153 : // XXX please make this method more general; the cut&pasted code is laughable
1154 0 : nsresult nsCharsetMenu::InitMoreSubmenus(nsTArray<nsCString>& aDecs)
1155 : {
1156 0 : nsresult res = NS_OK;
1157 :
1158 0 : nsCOMPtr<nsIRDFContainer> container1;
1159 0 : nsCOMPtr<nsIRDFContainer> container2;
1160 0 : nsCOMPtr<nsIRDFContainer> container3;
1161 0 : nsCOMPtr<nsIRDFContainer> container4;
1162 0 : nsCOMPtr<nsIRDFContainer> container5;
1163 0 : nsCOMPtr<nsIRDFContainer> containerU;
1164 0 : const char key1[] = "intl.charsetmenu.browser.more1";
1165 0 : const char key2[] = "intl.charsetmenu.browser.more2";
1166 0 : const char key3[] = "intl.charsetmenu.browser.more3";
1167 0 : const char key4[] = "intl.charsetmenu.browser.more4";
1168 0 : const char key5[] = "intl.charsetmenu.browser.more5";
1169 0 : const char keyU[] = "intl.charsetmenu.browser.unicode";
1170 :
1171 : res = NewRDFContainer(mInner, kNC_BrowserMore1CharsetMenuRoot,
1172 0 : getter_AddRefs(container1));
1173 0 : if (NS_FAILED(res)) return res;
1174 0 : AddFromNolocPrefsToMenu(NULL, container1, key1, aDecs, NULL);
1175 :
1176 : res = NewRDFContainer(mInner, kNC_BrowserMore2CharsetMenuRoot,
1177 0 : getter_AddRefs(container2));
1178 0 : if (NS_FAILED(res)) return res;
1179 0 : AddFromNolocPrefsToMenu(NULL, container2, key2, aDecs, NULL);
1180 :
1181 : res = NewRDFContainer(mInner, kNC_BrowserMore3CharsetMenuRoot,
1182 0 : getter_AddRefs(container3));
1183 0 : if (NS_FAILED(res)) return res;
1184 0 : AddFromNolocPrefsToMenu(NULL, container3, key3, aDecs, NULL);
1185 :
1186 : res = NewRDFContainer(mInner, kNC_BrowserMore4CharsetMenuRoot,
1187 0 : getter_AddRefs(container4));
1188 0 : if (NS_FAILED(res)) return res;
1189 0 : AddFromNolocPrefsToMenu(NULL, container4, key4, aDecs, NULL);
1190 :
1191 : res = NewRDFContainer(mInner, kNC_BrowserMore5CharsetMenuRoot,
1192 0 : getter_AddRefs(container5));
1193 0 : if (NS_FAILED(res)) return res;
1194 0 : AddFromNolocPrefsToMenu(NULL, container5, key5, aDecs, NULL);
1195 :
1196 : res = NewRDFContainer(mInner, kNC_BrowserUnicodeCharsetMenuRoot,
1197 0 : getter_AddRefs(containerU));
1198 0 : if (NS_FAILED(res)) return res;
1199 0 : AddFromNolocPrefsToMenu(NULL, containerU, keyU, aDecs, NULL);
1200 :
1201 0 : return res;
1202 : }
1203 :
1204 0 : nsresult nsCharsetMenu::AddCharsetToItemArray(nsTArray<nsMenuEntry*> *aArray,
1205 : const nsAFlatCString& aCharset,
1206 : nsMenuEntry ** aResult,
1207 : PRInt32 aPlace)
1208 : {
1209 0 : nsresult res = NS_OK;
1210 0 : nsMenuEntry * item = NULL;
1211 :
1212 0 : if (aResult != NULL) *aResult = NULL;
1213 :
1214 0 : item = new nsMenuEntry();
1215 0 : if (item == NULL) {
1216 0 : res = NS_ERROR_OUT_OF_MEMORY;
1217 0 : goto done;
1218 : }
1219 :
1220 0 : item->mCharset = aCharset;
1221 :
1222 0 : res = mCCManager->GetCharsetTitle(aCharset.get(), item->mTitle);
1223 0 : if (NS_FAILED(res)) {
1224 0 : item->mTitle.AssignWithConversion(aCharset.get());
1225 : }
1226 :
1227 0 : if (aArray != NULL) {
1228 0 : if (aPlace < 0) {
1229 0 : res = aArray->AppendElement(item) != nsnull;
1230 0 : if (NS_FAILED(res)) goto done;
1231 : } else {
1232 0 : res = aArray->InsertElementsAt(aPlace, 1, item) != nsnull;
1233 0 : if (NS_FAILED(res)) goto done;
1234 : }
1235 : }
1236 :
1237 0 : if (aResult != NULL) *aResult = item;
1238 :
1239 : // if we have made another reference to "item", do not delete it
1240 0 : if ((aArray != NULL) || (aResult != NULL)) item = NULL;
1241 :
1242 : done:
1243 0 : if (item != NULL) delete item;
1244 :
1245 0 : return res;
1246 : }
1247 :
1248 : nsresult
1249 0 : nsCharsetMenu::AddCharsetArrayToItemArray(nsTArray<nsMenuEntry*>& aArray,
1250 : const nsTArray<nsCString>& aCharsets)
1251 : {
1252 0 : PRUint32 count = aCharsets.Length();
1253 :
1254 0 : for (PRUint32 i = 0; i < count; i++) {
1255 :
1256 0 : const nsCString& str = aCharsets[i];
1257 0 : nsresult res = AddCharsetToItemArray(&aArray, str, NULL, -1);
1258 :
1259 0 : if (NS_FAILED(res))
1260 0 : return res;
1261 : }
1262 :
1263 0 : return NS_OK;
1264 : }
1265 :
1266 : // aPlace < -1 for Remove
1267 : // aPlace < 0 for Append
1268 0 : nsresult nsCharsetMenu::AddMenuItemToContainer(
1269 : nsIRDFContainer * aContainer,
1270 : nsMenuEntry * aItem,
1271 : nsIRDFResource * aType,
1272 : const char * aIDPrefix,
1273 : PRInt32 aPlace)
1274 : {
1275 0 : nsresult res = NS_OK;
1276 0 : nsCOMPtr<nsIRDFResource> node;
1277 :
1278 0 : nsCAutoString id;
1279 0 : if (aIDPrefix != NULL) id.Assign(aIDPrefix);
1280 0 : id.Append(aItem->mCharset);
1281 :
1282 : // Make up a unique ID and create the RDF NODE
1283 0 : res = mRDFService->GetResource(id, getter_AddRefs(node));
1284 0 : if (NS_FAILED(res)) return res;
1285 :
1286 0 : const PRUnichar * title = aItem->mTitle.get();
1287 :
1288 : // set node's title
1289 0 : nsCOMPtr<nsIRDFLiteral> titleLiteral;
1290 0 : res = mRDFService->GetLiteral(title, getter_AddRefs(titleLiteral));
1291 0 : if (NS_FAILED(res)) return res;
1292 :
1293 0 : if (aPlace < -1) {
1294 0 : res = Unassert(node, kNC_Name, titleLiteral);
1295 0 : if (NS_FAILED(res)) return res;
1296 : } else {
1297 0 : res = Assert(node, kNC_Name, titleLiteral, true);
1298 0 : if (NS_FAILED(res)) return res;
1299 : }
1300 :
1301 0 : if (aType != NULL) {
1302 0 : if (aPlace < -1) {
1303 0 : res = Unassert(node, kRDF_type, aType);
1304 0 : if (NS_FAILED(res)) return res;
1305 : } else {
1306 0 : res = Assert(node, kRDF_type, aType, true);
1307 0 : if (NS_FAILED(res)) return res;
1308 : }
1309 : }
1310 :
1311 : // Add the element to the container
1312 0 : if (aPlace < -1) {
1313 0 : res = aContainer->RemoveElement(node, true);
1314 0 : if (NS_FAILED(res)) return res;
1315 0 : } else if (aPlace < 0) {
1316 0 : res = aContainer->AppendElement(node);
1317 0 : if (NS_FAILED(res)) return res;
1318 : } else {
1319 0 : res = aContainer->InsertElementAt(node, aPlace, true);
1320 0 : if (NS_FAILED(res)) return res;
1321 : }
1322 :
1323 0 : return res;
1324 : }
1325 :
1326 0 : nsresult nsCharsetMenu::AddMenuItemArrayToContainer(
1327 : nsIRDFContainer * aContainer,
1328 : nsTArray<nsMenuEntry*> * aArray,
1329 : nsIRDFResource * aType)
1330 : {
1331 0 : PRUint32 count = aArray->Length();
1332 0 : nsresult res = NS_OK;
1333 :
1334 0 : for (PRUint32 i = 0; i < count; i++) {
1335 0 : nsMenuEntry * item = aArray->ElementAt(i);
1336 0 : if (item == NULL) return NS_ERROR_UNEXPECTED;
1337 :
1338 0 : res = AddMenuItemToContainer(aContainer, item, aType, NULL, -1);
1339 0 : if (NS_FAILED(res)) return res;
1340 : }
1341 :
1342 0 : return NS_OK;
1343 : }
1344 :
1345 0 : nsresult nsCharsetMenu::AddCharsetToContainer(nsTArray<nsMenuEntry*> *aArray,
1346 : nsIRDFContainer * aContainer,
1347 : const nsAFlatCString& aCharset,
1348 : const char * aIDPrefix,
1349 : PRInt32 aPlace,
1350 : PRInt32 aRDFPlace)
1351 : {
1352 0 : nsresult res = NS_OK;
1353 0 : nsMenuEntry * item = NULL;
1354 :
1355 0 : res = AddCharsetToItemArray(aArray, aCharset, &item, aPlace);
1356 0 : if (NS_FAILED(res)) goto done;
1357 :
1358 : res = AddMenuItemToContainer(aContainer, item, NULL, aIDPrefix,
1359 0 : aPlace + aRDFPlace);
1360 0 : if (NS_FAILED(res)) goto done;
1361 :
1362 : // if we have made another reference to "item", do not delete it
1363 0 : if (aArray != NULL) item = NULL;
1364 :
1365 : done:
1366 0 : if (item != NULL) delete item;
1367 :
1368 0 : return res;
1369 : }
1370 :
1371 0 : nsresult nsCharsetMenu::AddFromPrefsToMenu(
1372 : nsTArray<nsMenuEntry*> * aArray,
1373 : nsIRDFContainer * aContainer,
1374 : const char * aKey,
1375 : nsTArray<nsCString>& aDecs,
1376 : const char * aIDPrefix)
1377 : {
1378 0 : nsresult res = NS_OK;
1379 :
1380 0 : nsCOMPtr<nsIPrefLocalizedString> pls;
1381 0 : res = mPrefs->GetComplexValue(aKey, NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(pls));
1382 0 : if (NS_FAILED(res)) return res;
1383 :
1384 0 : if (pls) {
1385 0 : nsXPIDLString ucsval;
1386 0 : pls->ToString(getter_Copies(ucsval));
1387 0 : NS_ConvertUTF16toUTF8 utf8val(ucsval);
1388 0 : if (ucsval)
1389 : res = AddFromStringToMenu(utf8val.BeginWriting(), aArray,
1390 0 : aContainer, aDecs, aIDPrefix);
1391 : }
1392 :
1393 0 : return res;
1394 : }
1395 :
1396 : nsresult
1397 0 : nsCharsetMenu::AddFromNolocPrefsToMenu(nsTArray<nsMenuEntry*> * aArray,
1398 : nsIRDFContainer * aContainer,
1399 : const char * aKey,
1400 : nsTArray<nsCString>& aDecs,
1401 : const char * aIDPrefix)
1402 : {
1403 0 : nsresult res = NS_OK;
1404 :
1405 0 : char * value = NULL;
1406 0 : res = mPrefs->GetCharPref(aKey, &value);
1407 0 : if (NS_FAILED(res)) return res;
1408 :
1409 0 : if (value != NULL) {
1410 0 : res = AddFromStringToMenu(value, aArray, aContainer, aDecs, aIDPrefix);
1411 0 : nsMemory::Free(value);
1412 : }
1413 :
1414 0 : return res;
1415 : }
1416 :
1417 0 : nsresult nsCharsetMenu::AddFromStringToMenu(
1418 : char * aCharsetList,
1419 : nsTArray<nsMenuEntry*> * aArray,
1420 : nsIRDFContainer * aContainer,
1421 : nsTArray<nsCString>& aDecs,
1422 : const char * aIDPrefix)
1423 : {
1424 0 : nsresult res = NS_OK;
1425 0 : char * p = aCharsetList;
1426 0 : char * q = p;
1427 0 : while (*p != 0) {
1428 0 : for (; (*q != ',') && (*q != ' ') && (*q != 0); q++) {;}
1429 0 : char temp = *q;
1430 0 : *q = 0;
1431 :
1432 : // if this charset is not on the accepted list of charsets, ignore it
1433 : PRInt32 index;
1434 0 : index = aDecs.IndexOf(nsCAutoString(p), 0, nsIgnoreCaseCStringComparator());
1435 0 : if (index >= 0) {
1436 :
1437 : // else, add it to the menu
1438 0 : res = AddCharsetToContainer(aArray, aContainer, nsDependentCString(p),
1439 0 : aIDPrefix, -1, 0);
1440 0 : NS_ASSERTION(NS_SUCCEEDED(res), "cannot add charset to menu");
1441 0 : if (NS_FAILED(res)) break;
1442 :
1443 0 : aDecs.RemoveElementAt(index);
1444 : }
1445 :
1446 0 : *q = temp;
1447 0 : for (; (*q == ',') || (*q == ' '); q++) {;}
1448 0 : p=q;
1449 : }
1450 :
1451 0 : return NS_OK;
1452 : }
1453 :
1454 0 : nsresult nsCharsetMenu::AddSeparatorToContainer(nsIRDFContainer * aContainer)
1455 : {
1456 0 : nsCAutoString str;
1457 0 : str.AssignLiteral("----");
1458 :
1459 : // hack to generate unique id's for separators
1460 : static PRInt32 u = 0;
1461 0 : u++;
1462 0 : str.AppendInt(u);
1463 :
1464 0 : nsMenuEntry item;
1465 0 : item.mCharset = str;
1466 0 : item.mTitle.AssignWithConversion(str.get());
1467 :
1468 : return AddMenuItemToContainer(aContainer, &item, kNC_BookmarkSeparator,
1469 0 : NULL, -1);
1470 : }
1471 :
1472 : nsresult
1473 0 : nsCharsetMenu::AddCharsetToCache(const nsAFlatCString& aCharset,
1474 : nsTArray<nsMenuEntry*> * aArray,
1475 : nsIRDFResource * aRDFResource,
1476 : PRUint32 aCacheStart,
1477 : PRUint32 aCacheSize,
1478 : PRInt32 aRDFPlace)
1479 : {
1480 : PRInt32 i;
1481 0 : nsresult res = NS_OK;
1482 :
1483 0 : i = FindMenuItemInArray(aArray, aCharset, NULL);
1484 0 : if (i >= 0) return res;
1485 :
1486 0 : nsCOMPtr<nsIRDFContainer> container;
1487 0 : res = NewRDFContainer(mInner, aRDFResource, getter_AddRefs(container));
1488 0 : if (NS_FAILED(res)) return res;
1489 :
1490 : // if too many items, remove last one
1491 0 : if (aArray->Length() - aCacheStart >= aCacheSize){
1492 0 : res = RemoveLastMenuItem(container, aArray);
1493 0 : if (NS_FAILED(res)) return res;
1494 : }
1495 :
1496 : res = AddCharsetToContainer(aArray, container, aCharset, "charset.",
1497 0 : aCacheStart, aRDFPlace);
1498 :
1499 0 : return res;
1500 : }
1501 :
1502 0 : nsresult nsCharsetMenu::WriteCacheToPrefs(nsTArray<nsMenuEntry*> * aArray,
1503 : PRInt32 aCacheStart,
1504 : const char * aKey)
1505 : {
1506 0 : nsresult res = NS_OK;
1507 :
1508 : // create together the cache string
1509 0 : nsCAutoString cache;
1510 0 : nsCAutoString sep(NS_LITERAL_CSTRING(", "));
1511 0 : PRUint32 count = aArray->Length();
1512 :
1513 0 : for (PRUint32 i = aCacheStart; i < count; i++) {
1514 0 : nsMenuEntry * item = aArray->ElementAt(i);
1515 0 : if (item != NULL) {
1516 0 : cache.Append(item->mCharset);
1517 0 : if (i < count - 1) {
1518 0 : cache.Append(sep);
1519 : }
1520 : }
1521 : }
1522 :
1523 : // write the pref
1524 0 : res = mPrefs->SetCharPref(aKey, cache.get());
1525 :
1526 0 : return res;
1527 : }
1528 :
1529 0 : nsresult nsCharsetMenu::UpdateCachePrefs(const char * aCacheKey,
1530 : const char * aCacheSizeKey,
1531 : const char * aStaticKey,
1532 : const PRUnichar * aCharset)
1533 : {
1534 0 : nsresult rv = NS_OK;
1535 0 : nsXPIDLCString cachePrefValue;
1536 0 : nsXPIDLCString staticPrefValue;
1537 0 : NS_LossyConvertUTF16toASCII currentCharset(aCharset);
1538 0 : PRInt32 cacheSize = 0;
1539 :
1540 0 : mPrefs->GetCharPref(aCacheKey, getter_Copies(cachePrefValue));
1541 0 : mPrefs->GetCharPref(aStaticKey, getter_Copies(staticPrefValue));
1542 0 : rv = mPrefs->GetIntPref(aCacheSizeKey, &cacheSize);
1543 :
1544 0 : if (NS_FAILED(rv) || cacheSize <= 0)
1545 0 : return NS_ERROR_UNEXPECTED;
1546 :
1547 0 : if ((cachePrefValue.Find(currentCharset) == kNotFound) &&
1548 0 : (staticPrefValue.Find(currentCharset) == kNotFound)) {
1549 :
1550 0 : if (!cachePrefValue.IsEmpty())
1551 0 : cachePrefValue.Insert(", ", 0);
1552 :
1553 0 : cachePrefValue.Insert(currentCharset, 0);
1554 0 : if (cacheSize < (PRInt32) cachePrefValue.CountChar(',') + 1)
1555 0 : cachePrefValue.Truncate(cachePrefValue.RFindChar(','));
1556 :
1557 0 : rv = mPrefs->SetCharPref(aCacheKey, cachePrefValue);
1558 : }
1559 :
1560 0 : return rv;
1561 : }
1562 :
1563 0 : nsresult nsCharsetMenu::ClearMenu(nsIRDFContainer * aContainer,
1564 : nsTArray<nsMenuEntry*> * aArray)
1565 : {
1566 0 : nsresult res = NS_OK;
1567 :
1568 : // clean the RDF data source
1569 0 : PRUint32 count = aArray->Length();
1570 0 : for (PRUint32 i = 0; i < count; i++) {
1571 0 : nsMenuEntry * item = aArray->ElementAt(i);
1572 0 : if (item != NULL) {
1573 0 : res = AddMenuItemToContainer(aContainer, item, NULL, "charset.", -2);
1574 0 : if (NS_FAILED(res)) return res;
1575 : }
1576 : }
1577 :
1578 : // clean the internal data structures
1579 0 : FreeMenuItemArray(aArray);
1580 :
1581 0 : return res;
1582 : }
1583 :
1584 0 : nsresult nsCharsetMenu::RemoveLastMenuItem(nsIRDFContainer * aContainer,
1585 : nsTArray<nsMenuEntry*> * aArray)
1586 : {
1587 0 : nsresult res = NS_OK;
1588 :
1589 0 : PRInt32 last = aArray->Length() - 1;
1590 0 : if (last >= 0) {
1591 0 : nsMenuEntry * item = aArray->ElementAt(last);
1592 0 : if (item != NULL) {
1593 0 : res = AddMenuItemToContainer(aContainer, item, NULL, "charset.", -2);
1594 0 : if (NS_FAILED(res)) return res;
1595 :
1596 0 : aArray->RemoveElementAt(last);
1597 : }
1598 : }
1599 :
1600 0 : return res;
1601 : }
1602 :
1603 0 : nsresult nsCharsetMenu::RemoveFlaggedCharsets(nsTArray<nsCString>& aList,
1604 : const nsString& aProp)
1605 : {
1606 0 : nsresult res = NS_OK;
1607 : PRUint32 count;
1608 :
1609 0 : count = aList.Length();
1610 0 : if (NS_FAILED(res)) return res;
1611 :
1612 0 : nsAutoString str;
1613 0 : for (PRUint32 i = 0; i < count; i++) {
1614 :
1615 0 : res = mCCManager->GetCharsetData(aList[i].get(), aProp.get(), str);
1616 0 : if (NS_FAILED(res)) continue;
1617 :
1618 0 : aList.RemoveElementAt(i);
1619 :
1620 0 : i--;
1621 0 : count--;
1622 : }
1623 :
1624 0 : return NS_OK;
1625 : }
1626 :
1627 0 : nsresult nsCharsetMenu::NewRDFContainer(nsIRDFDataSource * aDataSource,
1628 : nsIRDFResource * aResource,
1629 : nsIRDFContainer ** aResult)
1630 : {
1631 0 : nsresult res = CallCreateInstance(kRDFContainerCID, aResult);
1632 0 : if (NS_FAILED(res)) return res;
1633 :
1634 0 : res = (*aResult)->Init(aDataSource, aResource);
1635 0 : if (NS_FAILED(res)) NS_RELEASE(*aResult);
1636 :
1637 0 : return res;
1638 : }
1639 :
1640 0 : void nsCharsetMenu::FreeMenuItemArray(nsTArray<nsMenuEntry*> * aArray)
1641 : {
1642 0 : PRUint32 count = aArray->Length();
1643 0 : for (PRUint32 i = 0; i < count; i++) {
1644 0 : nsMenuEntry * item = aArray->ElementAt(i);
1645 0 : if (item != NULL) {
1646 0 : delete item;
1647 : }
1648 : }
1649 0 : aArray->Clear();
1650 0 : }
1651 :
1652 0 : PRInt32 nsCharsetMenu::FindMenuItemInArray(const nsTArray<nsMenuEntry*>* aArray,
1653 : const nsAFlatCString& aCharset,
1654 : nsMenuEntry ** aResult)
1655 : {
1656 0 : PRUint32 count = aArray->Length();
1657 :
1658 0 : for (PRUint32 i=0; i < count; i++) {
1659 0 : nsMenuEntry * item = aArray->ElementAt(i);
1660 0 : if (item->mCharset == aCharset) {
1661 0 : if (aResult != NULL) *aResult = item;
1662 0 : return i;
1663 : }
1664 : }
1665 :
1666 0 : if (aResult != NULL) *aResult = NULL;
1667 0 : return -1;
1668 : }
1669 :
1670 0 : nsresult nsCharsetMenu::ReorderMenuItemArray(nsTArray<nsMenuEntry*> * aArray)
1671 : {
1672 0 : nsresult res = NS_OK;
1673 0 : nsCOMPtr<nsICollation> collation;
1674 0 : PRUint32 count = aArray->Length();
1675 : PRUint32 i;
1676 :
1677 : // we need to use a temporary array
1678 0 : charsetMenuSortRecord *array = new charsetMenuSortRecord [count];
1679 0 : NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
1680 0 : for (i = 0; i < count; i++)
1681 0 : array[i].key = nsnull;
1682 :
1683 0 : res = GetCollation(getter_AddRefs(collation));
1684 0 : if (NS_FAILED(res))
1685 0 : goto done;
1686 :
1687 0 : for (i = 0; i < count && NS_SUCCEEDED(res); i++) {
1688 0 : array[i].item = aArray->ElementAt(i);
1689 :
1690 0 : res = collation->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive,
1691 0 : (array[i].item)->mTitle, &array[i].key, &array[i].len);
1692 : }
1693 :
1694 : // reorder the array
1695 0 : if (NS_SUCCEEDED(res)) {
1696 0 : NS_QuickSort(array, count, sizeof(*array), CompareMenuItems, collation);
1697 :
1698 : // move the elements from the temporary array into the the real one
1699 0 : aArray->Clear();
1700 0 : for (i = 0; i < count; i++) {
1701 0 : aArray->AppendElement(array[i].item);
1702 : }
1703 : }
1704 :
1705 : done:
1706 0 : for (i = 0; i < count; i++) {
1707 0 : PR_FREEIF(array[i].key);
1708 : }
1709 0 : delete [] array;
1710 0 : return res;
1711 : }
1712 :
1713 0 : nsresult nsCharsetMenu::GetCollation(nsICollation ** aCollation)
1714 : {
1715 0 : nsresult res = NS_OK;
1716 0 : nsCOMPtr<nsILocale> locale = nsnull;
1717 0 : nsICollationFactory * collationFactory = nsnull;
1718 :
1719 : nsCOMPtr<nsILocaleService> localeServ =
1720 0 : do_GetService(NS_LOCALESERVICE_CONTRACTID, &res);
1721 0 : if (NS_FAILED(res)) return res;
1722 :
1723 0 : res = localeServ->GetApplicationLocale(getter_AddRefs(locale));
1724 0 : if (NS_FAILED(res)) return res;
1725 :
1726 0 : res = CallCreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &collationFactory);
1727 0 : if (NS_FAILED(res)) return res;
1728 :
1729 0 : res = collationFactory->CreateCollation(locale, aCollation);
1730 0 : NS_RELEASE(collationFactory);
1731 0 : return res;
1732 : }
1733 :
1734 : //----------------------------------------------------------------------------
1735 : // Interface nsICurrentCharsetListener [implementation]
1736 :
1737 0 : NS_IMETHODIMP nsCharsetMenu::SetCurrentCharset(const PRUnichar * aCharset)
1738 : {
1739 0 : nsresult res = NS_OK;
1740 :
1741 0 : if (mBrowserMenuInitialized) {
1742 : // Don't add item to the cache if it's marked "notForBrowser"
1743 0 : nsAutoString str;
1744 0 : res = mCCManager->GetCharsetData(NS_LossyConvertUTF16toASCII(aCharset).get(),
1745 0 : NS_LITERAL_STRING(".notForBrowser").get(), str);
1746 0 : if (NS_SUCCEEDED(res)) // succeeded means attribute exists
1747 0 : return res; // don't throw
1748 :
1749 0 : res = AddCharsetToCache(NS_LossyConvertUTF16toASCII(aCharset),
1750 : &mBrowserMenu, kNC_BrowserCharsetMenuRoot,
1751 : mBrowserCacheStart, mBrowserCacheSize,
1752 0 : mBrowserMenuRDFPosition);
1753 0 : if (NS_FAILED(res)) {
1754 0 : return res;
1755 : }
1756 :
1757 : res = WriteCacheToPrefs(&mBrowserMenu, mBrowserCacheStart,
1758 0 : kBrowserCachePrefKey);
1759 : } else {
1760 : res = UpdateCachePrefs(kBrowserCachePrefKey, kBrowserCacheSizePrefKey,
1761 0 : kBrowserStaticPrefKey, aCharset);
1762 : }
1763 0 : return res;
1764 : }
1765 :
1766 0 : NS_IMETHODIMP nsCharsetMenu::SetCurrentMailCharset(const PRUnichar * aCharset)
1767 : {
1768 0 : nsresult res = NS_OK;
1769 :
1770 0 : if (mMailviewMenuInitialized) {
1771 0 : res = AddCharsetToCache(NS_LossyConvertUTF16toASCII(aCharset),
1772 : &mMailviewMenu, kNC_MailviewCharsetMenuRoot,
1773 : mMailviewCacheStart, mMailviewCacheSize,
1774 0 : mMailviewMenuRDFPosition);
1775 0 : if (NS_FAILED(res)) return res;
1776 :
1777 : res = WriteCacheToPrefs(&mMailviewMenu, mMailviewCacheStart,
1778 0 : kMailviewCachePrefKey);
1779 : } else {
1780 : res = UpdateCachePrefs(kMailviewCachePrefKey, kMailviewCacheSizePrefKey,
1781 0 : kMailviewStaticPrefKey, aCharset);
1782 : }
1783 0 : return res;
1784 : }
1785 :
1786 0 : NS_IMETHODIMP nsCharsetMenu::SetCurrentComposerCharset(const PRUnichar * aCharset)
1787 : {
1788 0 : nsresult res = NS_OK;
1789 :
1790 0 : if (mComposerMenuInitialized) {
1791 :
1792 0 : res = AddCharsetToCache(NS_LossyConvertUTF16toASCII(aCharset),
1793 : &mComposerMenu, kNC_ComposerCharsetMenuRoot,
1794 : mComposerCacheStart, mComposerCacheSize,
1795 0 : mComposerMenuRDFPosition);
1796 0 : if (NS_FAILED(res)) return res;
1797 :
1798 : res = WriteCacheToPrefs(&mComposerMenu, mComposerCacheStart,
1799 0 : kComposerCachePrefKey);
1800 : } else {
1801 : res = UpdateCachePrefs(kComposerCachePrefKey, kComposerCacheSizePrefKey,
1802 0 : kComposerStaticPrefKey, aCharset);
1803 : }
1804 0 : return res;
1805 : }
1806 :
1807 : //----------------------------------------------------------------------------
1808 : // Interface nsIRDFDataSource [implementation]
1809 :
1810 0 : NS_IMETHODIMP nsCharsetMenu::GetURI(char ** uri)
1811 : {
1812 0 : if (!uri) return NS_ERROR_NULL_POINTER;
1813 :
1814 0 : *uri = NS_strdup("rdf:charset-menu");
1815 0 : if (!(*uri)) return NS_ERROR_OUT_OF_MEMORY;
1816 :
1817 0 : return NS_OK;
1818 : }
1819 :
1820 0 : NS_IMETHODIMP nsCharsetMenu::GetSource(nsIRDFResource* property,
1821 : nsIRDFNode* target,
1822 : bool tv,
1823 : nsIRDFResource** source)
1824 : {
1825 0 : return mInner->GetSource(property, target, tv, source);
1826 : }
1827 :
1828 0 : NS_IMETHODIMP nsCharsetMenu::GetSources(nsIRDFResource* property,
1829 : nsIRDFNode* target,
1830 : bool tv,
1831 : nsISimpleEnumerator** sources)
1832 : {
1833 0 : return mInner->GetSources(property, target, tv, sources);
1834 : }
1835 :
1836 0 : NS_IMETHODIMP nsCharsetMenu::GetTarget(nsIRDFResource* source,
1837 : nsIRDFResource* property,
1838 : bool tv,
1839 : nsIRDFNode** target)
1840 : {
1841 0 : return mInner->GetTarget(source, property, tv, target);
1842 : }
1843 :
1844 0 : NS_IMETHODIMP nsCharsetMenu::GetTargets(nsIRDFResource* source,
1845 : nsIRDFResource* property,
1846 : bool tv,
1847 : nsISimpleEnumerator** targets)
1848 : {
1849 0 : return mInner->GetTargets(source, property, tv, targets);
1850 : }
1851 :
1852 0 : NS_IMETHODIMP nsCharsetMenu::Assert(nsIRDFResource* aSource,
1853 : nsIRDFResource* aProperty,
1854 : nsIRDFNode* aTarget,
1855 : bool aTruthValue)
1856 : {
1857 : // TODO: filter out asserts we don't care about
1858 0 : return mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
1859 : }
1860 :
1861 0 : NS_IMETHODIMP nsCharsetMenu::Unassert(nsIRDFResource* aSource,
1862 : nsIRDFResource* aProperty,
1863 : nsIRDFNode* aTarget)
1864 : {
1865 : // TODO: filter out unasserts we don't care about
1866 0 : return mInner->Unassert(aSource, aProperty, aTarget);
1867 : }
1868 :
1869 :
1870 0 : NS_IMETHODIMP nsCharsetMenu::Change(nsIRDFResource* aSource,
1871 : nsIRDFResource* aProperty,
1872 : nsIRDFNode* aOldTarget,
1873 : nsIRDFNode* aNewTarget)
1874 : {
1875 : // TODO: filter out changes we don't care about
1876 0 : return mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
1877 : }
1878 :
1879 0 : NS_IMETHODIMP nsCharsetMenu::Move(nsIRDFResource* aOldSource,
1880 : nsIRDFResource* aNewSource,
1881 : nsIRDFResource* aProperty,
1882 : nsIRDFNode* aTarget)
1883 : {
1884 : // TODO: filter out changes we don't care about
1885 0 : return mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
1886 : }
1887 :
1888 :
1889 0 : NS_IMETHODIMP nsCharsetMenu::HasAssertion(nsIRDFResource* source,
1890 : nsIRDFResource* property,
1891 : nsIRDFNode* target, bool tv,
1892 : bool* hasAssertion)
1893 : {
1894 0 : return mInner->HasAssertion(source, property, target, tv, hasAssertion);
1895 : }
1896 :
1897 0 : NS_IMETHODIMP nsCharsetMenu::AddObserver(nsIRDFObserver* n)
1898 : {
1899 0 : return mInner->AddObserver(n);
1900 : }
1901 :
1902 0 : NS_IMETHODIMP nsCharsetMenu::RemoveObserver(nsIRDFObserver* n)
1903 : {
1904 0 : return mInner->RemoveObserver(n);
1905 : }
1906 :
1907 : NS_IMETHODIMP
1908 0 : nsCharsetMenu::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, bool *result)
1909 : {
1910 0 : return mInner->HasArcIn(aNode, aArc, result);
1911 : }
1912 :
1913 : NS_IMETHODIMP
1914 0 : nsCharsetMenu::HasArcOut(nsIRDFResource *source, nsIRDFResource *aArc, bool *result)
1915 : {
1916 0 : return mInner->HasArcOut(source, aArc, result);
1917 : }
1918 :
1919 0 : NS_IMETHODIMP nsCharsetMenu::ArcLabelsIn(nsIRDFNode* node,
1920 : nsISimpleEnumerator** labels)
1921 : {
1922 0 : return mInner->ArcLabelsIn(node, labels);
1923 : }
1924 :
1925 0 : NS_IMETHODIMP nsCharsetMenu::ArcLabelsOut(nsIRDFResource* source,
1926 : nsISimpleEnumerator** labels)
1927 : {
1928 0 : return mInner->ArcLabelsOut(source, labels);
1929 : }
1930 :
1931 0 : NS_IMETHODIMP nsCharsetMenu::GetAllResources(nsISimpleEnumerator** aCursor)
1932 : {
1933 0 : return mInner->GetAllResources(aCursor);
1934 : }
1935 :
1936 0 : NS_IMETHODIMP nsCharsetMenu::GetAllCmds(
1937 : nsIRDFResource* source,
1938 : nsISimpleEnumerator/*<nsIRDFResource>*/** commands)
1939 : {
1940 0 : NS_NOTYETIMPLEMENTED("write me!");
1941 0 : return NS_ERROR_NOT_IMPLEMENTED;
1942 : }
1943 :
1944 0 : NS_IMETHODIMP nsCharsetMenu::IsCommandEnabled(
1945 : nsISupportsArray/*<nsIRDFResource>*/* aSources,
1946 : nsIRDFResource* aCommand,
1947 : nsISupportsArray/*<nsIRDFResource>*/* aArguments,
1948 : bool* aResult)
1949 : {
1950 0 : NS_NOTYETIMPLEMENTED("write me!");
1951 0 : return NS_ERROR_NOT_IMPLEMENTED;
1952 : }
1953 :
1954 0 : NS_IMETHODIMP nsCharsetMenu::DoCommand(nsISupportsArray* aSources,
1955 : nsIRDFResource* aCommand,
1956 : nsISupportsArray* aArguments)
1957 : {
1958 0 : NS_NOTYETIMPLEMENTED("write me!");
1959 0 : return NS_ERROR_NOT_IMPLEMENTED;
1960 : }
1961 :
1962 0 : NS_IMETHODIMP nsCharsetMenu::BeginUpdateBatch()
1963 : {
1964 0 : return mInner->BeginUpdateBatch();
1965 : }
1966 :
1967 0 : NS_IMETHODIMP nsCharsetMenu::EndUpdateBatch()
1968 : {
1969 0 : return mInner->EndUpdateBatch();
1970 4392 : }
|