1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2 /* AbiSource Application Framework
3 * Copyright (C) 1998 AbiSource, Inc.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA.
19 */
20
21 // TODO add code to do an auto save anytime anything is changed.
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <time.h>
27
28 #include <vector>
29
30 #include "ut_debugmsg.h"
31 #include "ut_growbuf.h"
32 #include "ut_string.h"
33 #include "ut_string_class.h"
34 #ifdef WIN32
35 #include <ut_Win32LocaleString.h>
36 #endif
37 #include "ut_go_file.h"
38 #include "xap_Prefs.h"
39 #include "xap_App.h"
40
41 struct xmlToIdMapping {
42 const char *m_name;
43 int m_type;
44 };
45
compareStrings(const void * ppS1,const void * ppS2)46 static UT_sint32 compareStrings(const void * ppS1, const void * ppS2)
47 {
48 const char * sz1 = static_cast<const char *>(ppS1);
49 const char * sz2 = static_cast<const char *>(ppS2);
50 return g_ascii_strcasecmp(sz1, sz2);
51 }
52
53 enum
54 {
55 TT_ABIPREFERENCES,
56 TT_GEOMETRY,
57 TT_FACE,
58 TT_FONTS,
59 TT_LOG,
60 TT_PLUGIN,
61 TT_RECENT,
62 TT_SCHEME,
63 TT_SELECT
64 };
65
66 // keep these in alphabetical order
67 static struct xmlToIdMapping s_Tokens[] =
68 {
69 { "AbiPreferences", TT_ABIPREFERENCES },
70 { "Face", TT_FACE },
71 { "Fonts", TT_FONTS },
72 { "Geometry", TT_GEOMETRY },
73 { "Log", TT_LOG},
74 { "Plugin", TT_PLUGIN },
75 { "Recent", TT_RECENT },
76 { "Scheme", TT_SCHEME },
77 { "Select", TT_SELECT }
78 };
79
80 /*****************************************************************/
81
XAP_PrefsScheme(XAP_Prefs * pPrefs,const gchar * szSchemeName)82 XAP_PrefsScheme::XAP_PrefsScheme( XAP_Prefs *pPrefs, const gchar * szSchemeName)
83 : m_hash(41)
84 {
85 m_pPrefs = pPrefs;
86 m_uTick = 0;
87 m_bValidSortedKeys = false;
88
89 if (szSchemeName && *szSchemeName)
90 m_szName = g_strdup(szSchemeName);
91 else
92 m_szName = NULL;
93 }
94
~XAP_PrefsScheme(void)95 XAP_PrefsScheme::~XAP_PrefsScheme(void)
96 {
97 FREEP(m_szName);
98
99 // loop through and g_free the values
100 UT_GenericVector<gchar*> * pVec = m_hash.enumerate ();
101
102 UT_uint32 cnt = pVec->size();
103
104 for (UT_uint32 i = 0 ; i < cnt; i++)
105 {
106 char * val = pVec->getNthItem (i);
107 FREEP(val);
108 }
109
110 DELETEP(pVec);
111 }
112
getSchemeName(void) const113 const gchar * XAP_PrefsScheme::getSchemeName(void) const
114 {
115 return m_szName;
116 }
117
setSchemeName(const gchar * szNewSchemeName)118 bool XAP_PrefsScheme::setSchemeName(const gchar * szNewSchemeName)
119 {
120 FREEP(m_szName);
121 return (NULL != (m_szName = g_strdup(szNewSchemeName)));
122 }
123
setValue(const gchar * szKey,const gchar * szValue)124 bool XAP_PrefsScheme::setValue(const gchar * szKey, const gchar * szValue)
125 {
126 ++m_uTick;
127 gchar * pEntry = m_hash.pick(szKey);
128 if (pEntry)
129 {
130 if (strcmp(szValue,pEntry) == 0)
131 return true; // equal values, no changes required
132
133 m_hash.set (szKey, g_strdup (szValue));
134 FREEP(pEntry);
135 }
136 else
137 {
138 // otherwise, need to add a new entry
139 m_hash.insert(szKey, g_strdup(szValue));
140 m_bValidSortedKeys = false;
141 }
142
143 m_pPrefs->_markPrefChange( szKey );
144
145 return true;
146 }
147
setValueBool(const gchar * szKey,bool bValue)148 bool XAP_PrefsScheme::setValueBool(const gchar * szKey, bool bValue)
149 {
150 return setValue(szKey, reinterpret_cast<const gchar*>((bValue) ? "1" : "0"));
151 }
152
setValueInt(const gchar * szKey,const int nValue)153 bool XAP_PrefsScheme::setValueInt(const gchar * szKey, const int nValue)
154 {
155 gchar szValue[32];
156 sprintf(szValue, "%d", nValue);
157 return setValue(szKey, szValue);
158 }
159
getValue(const gchar * szKey,const gchar ** pszValue) const160 bool XAP_PrefsScheme::getValue(const gchar * szKey, const gchar ** pszValue) const
161 {
162 gchar *pEntry = m_hash.pick(szKey);
163 if (!pEntry)
164 return false;
165
166 if (pszValue)
167 *pszValue = pEntry;
168 return true;
169 }
170
getValue(const UT_String & stKey,UT_String & stValue) const171 bool XAP_PrefsScheme::getValue(const UT_String &stKey, UT_String &stValue) const
172 {
173 gchar *pEntry = m_hash.pick(stKey);
174 if (!pEntry)
175 return false;
176
177 stValue = pEntry;
178 return true;
179 }
180
getValue(const char * szKey,std::string & stValue) const181 bool XAP_PrefsScheme::getValue(const char* szKey, std::string &stValue) const
182 {
183 const char *pEntry = m_hash.pick(szKey);
184 if (!pEntry)
185 return false;
186
187 stValue = pEntry;
188 return true;
189 }
190
getValueInt(const gchar * szKey,int & nValue) const191 bool XAP_PrefsScheme::getValueInt(const gchar * szKey, int& nValue) const
192 {
193 const gchar * szValue = NULL;
194 if (!getValue(szKey,&szValue))
195 return false; // bogus keyword ??
196
197 if (!szValue || !*szValue)
198 return false; // no value for known keyword ??
199
200 nValue = atoi(szValue);
201 return true;
202 }
203
getValueBool(const gchar * szKey,bool * pbValue) const204 bool XAP_PrefsScheme::getValueBool(const gchar * szKey, bool * pbValue) const
205 {
206 *pbValue = false; // assume something
207
208 const gchar * szValue = NULL;
209 if (!getValue(szKey,&szValue))
210 return false; // bogus keyword ??
211
212 if (!szValue || !*szValue)
213 return false; // no value for known keyword ??
214
215 switch (szValue[0])
216 {
217 case '1':
218 case 't':
219 case 'T':
220 case 'y':
221 case 'Y':
222 *pbValue = true;
223 return true;
224
225 default:
226 *pbValue = false;
227 return true;
228 }
229 }
230
getNthValue(UT_uint32 k,const gchar ** pszKey,const gchar ** pszValue)231 bool XAP_PrefsScheme::getNthValue(UT_uint32 k, const gchar ** pszKey, const gchar ** pszValue)
232 {
233 if (k >= static_cast<UT_uint32>(m_hash.size()))
234 return false;
235 //
236 // Output prefs in alphabetic Order.
237 //
238
239 if (!m_bValidSortedKeys) {
240 UT_GenericVector<const UT_String*> * vecD = m_hash.keys();
241 UT_GenericVector<const char*> vecKeys(vecD->getItemCount(), 4, true);
242 UT_sint32 i=0;
243 m_sortedKeys.clear();
244 for(i=0; i< vecD->getItemCount(); i++)
245 {
246 m_sortedKeys.addItem(vecD->getNthItem(i)->c_str());
247 }
248 m_sortedKeys.qsort(compareStrings);
249 m_bValidSortedKeys = true;
250 delete vecD;
251 }
252
253 const char * szKey = NULL;
254 const char * szValue = NULL;
255 szKey = m_sortedKeys.getNthItem(k);
256 szValue = m_hash.pick(szKey);
257 if(szValue && *szValue)
258 {
259 *pszKey = szKey;
260 *pszValue = szValue;
261 return true;
262 }
263 else
264 {
265 *pszKey = NULL;
266 *pszValue = NULL;
267 return false;
268 }
269 return false;
270 }
271
272 /*****************************************************************/
273
getAutoSavePrefs(void) const274 bool XAP_Prefs::getAutoSavePrefs(void) const
275 {
276 return m_bAutoSavePrefs;
277 }
278
setAutoSavePrefs(bool bAuto)279 void XAP_Prefs::setAutoSavePrefs(bool bAuto)
280 {
281 m_bAutoSavePrefs = bAuto;
282
283 // TODO if turning autosave on, we should do a save now....
284 // TODO if was on and turning off, should we save it now ??
285 }
286
287 /*****************************************************************/
288
getUseEnvLocale(void) const289 bool XAP_Prefs::getUseEnvLocale(void) const
290 {
291 return m_bUseEnvLocale;
292 }
293
setUseEnvLocale(bool bUse)294 void XAP_Prefs::setUseEnvLocale(bool bUse)
295 {
296 m_bUseEnvLocale = bUse;
297 }
298
299 /*****************************************************************/
300
getMaxRecent(void) const301 UT_sint32 XAP_Prefs::getMaxRecent(void) const
302 {
303 return m_iMaxRecent;
304 }
305
setMaxRecent(UT_sint32 k)306 void XAP_Prefs::setMaxRecent(UT_sint32 k)
307 {
308 UT_ASSERT_HARMLESS(k<=XAP_PREF_LIMIT_MaxRecent);
309
310 if (k > XAP_PREF_LIMIT_MaxRecent)
311 k = XAP_PREF_LIMIT_MaxRecent;
312
313 m_iMaxRecent = k;
314 }
315
getRecentCount(void) const316 UT_sint32 XAP_Prefs::getRecentCount(void) const
317 {
318 return m_vecRecent.getItemCount();
319 }
320
getRecent(UT_sint32 k) const321 const char * XAP_Prefs::getRecent(UT_sint32 k) const
322 {
323 // NB: k is one-based
324 UT_return_val_if_fail(k <= m_iMaxRecent, NULL);
325
326 const char * pRecent = NULL;
327
328 if (k <= m_vecRecent.getItemCount())
329 {
330 pRecent = reinterpret_cast<const char *>(m_vecRecent.getNthItem(k - 1));
331 }
332
333 return pRecent;
334 }
335
addRecent(const char * szRecent)336 void XAP_Prefs::addRecent(const char * szRecent)
337 {
338 char * sz;
339 bool bFound = false;
340
341 UT_return_if_fail(szRecent);
342
343 if (m_iMaxRecent == 0)
344 return; // NOOP
345
346 if(m_bIgnoreThisOne)
347 {
348 m_bIgnoreThisOne = false;
349 return;
350 }
351 // was it already here?
352 for (UT_sint32 i=0; i<m_vecRecent.getItemCount(); i++)
353 {
354 sz = m_vecRecent.getNthItem(i);
355 UT_continue_if_fail(sz);
356
357 if ((sz==szRecent) || !strcmp(sz, szRecent))
358 {
359 // yep, we're gonna move it up
360 m_vecRecent.deleteNthItem(i);
361 bFound = true;
362 break;
363 }
364 }
365
366 if (!bFound)
367 {
368 // nope. make a new copy to store
369 sz = g_strdup(szRecent);
370 }
371
372 m_vecRecent.insertItemAt(sz, 0);
373 _pruneRecent();
374 }
375
removeRecent(UT_sint32 k)376 void XAP_Prefs::removeRecent(UT_sint32 k)
377 {
378 UT_return_if_fail(k>0);
379 UT_return_if_fail(k<=getRecentCount());
380
381 char * sz = m_vecRecent.getNthItem(k-1);
382 FREEP(sz);
383
384 m_vecRecent.deleteNthItem(k-1);
385 }
386
_pruneRecent(void)387 void XAP_Prefs::_pruneRecent(void)
388 {
389 UT_sint32 i;
390 UT_sint32 count = getRecentCount();
391
392 if (m_iMaxRecent == 0)
393 {
394 // nuke the whole thing
395 for (i = count; i > 0 ; i--)
396 {
397 char * sz = m_vecRecent.getNthItem(i-1);
398 FREEP(sz);
399 }
400
401 m_vecRecent.clear();
402 }
403 else if (count > m_iMaxRecent)
404 {
405 // prune entries past m_iMaxRecent
406 for (i = count; i > m_iMaxRecent; i--)
407 removeRecent(i);
408 }
409 }
410 /*****************************************************************/
411
setGeometry(UT_sint32 posx,UT_sint32 posy,UT_uint32 width,UT_uint32 height,UT_uint32 flags)412 bool XAP_Prefs::setGeometry(UT_sint32 posx, UT_sint32 posy, UT_uint32 width, UT_uint32 height, UT_uint32 flags)
413 {
414 m_parserState.m_bFoundGeometry = true;
415 m_geom.m_width = width;
416 m_geom.m_height = height;
417 m_geom.m_posx = posx;
418 m_geom.m_posy = posy;
419 m_geom.m_flags = flags;
420
421 //For now we turn on the autosave of prefs so that we can save this setting ... is this bad?
422 setAutoSavePrefs(true);
423
424 return true;
425 }
426
getGeometry(UT_sint32 * posx,UT_sint32 * posy,UT_uint32 * width,UT_uint32 * height,UT_uint32 * flags)427 bool XAP_Prefs::getGeometry(UT_sint32 *posx, UT_sint32 *posy, UT_uint32 *width, UT_uint32 *height, UT_uint32 *flags)
428 {
429 if (m_parserState.m_bFoundGeometry == false) {
430 return false;
431 }
432 if (width) {
433 *width = m_geom.m_width;
434 }
435 if (height) {
436 *height = m_geom.m_height;
437 }
438 if (posx) {
439 *posx = m_geom.m_posx;
440 }
441 if (posy) {
442 *posy = m_geom.m_posy;
443 }
444 if (flags) {
445 *flags = m_geom.m_flags;
446 }
447 return true;
448 }
449
log(const char * where,const char * what,XAPPrefsLog_Level level)450 void XAP_Prefs::log(const char * where, const char * what, XAPPrefsLog_Level level)
451 {
452 UT_return_if_fail(where && what);
453 char b[50];
454
455 time_t t = time(NULL);
456
457 // we are inserting the log entries as comments, so we have to
458 // ensure we have no "--" in there (it anoys the parser)
459 UT_UTF8String sWhere(where);
460 UT_UTF8String sWhat(what);
461 UT_UTF8String sDashdash = "--";
462 UT_UTF8String sDash = "-";
463
464 while(strstr(sWhat.utf8_str(), "--"))
465 {
466 sWhat.escape(sDashdash, sDash);
467 }
468
469 while(strstr(sWhere.utf8_str(), "--"))
470 {
471 sWhere.escape(sDashdash, sDash);
472 }
473
474 strftime(b, 50, "<!-- [%c] ", localtime(&t));
475
476 UT_UTF8String * s = new UT_UTF8String(b);
477 UT_return_if_fail(s);
478
479 switch(level)
480 {
481 case Warning:
482 *s += "warning: ";
483 break;
484
485 case Error:
486 *s += "error: ";
487 break;
488 case Log:
489 default:
490 *s += "message: ";
491 }
492
493 sWhere.escapeXML();
494 sWhat.escapeXML();
495
496 *s += sWhere;
497 *s += " - ";
498 *s += sWhat;
499 *s += " -->";
500
501 m_vecLog.addItem(s);
502 }
503
504
505 /*****************************************************************/
506
XAP_Prefs()507 XAP_Prefs::XAP_Prefs()
508 : m_ahashChanges( 20 )
509 {
510 m_bAutoSavePrefs = (atoi(XAP_PREF_DEFAULT_AutoSavePrefs) ? true : false);
511 m_bUseEnvLocale = (atoi(XAP_PREF_DEFAULT_UseEnvLocale) ? true : false);
512 m_currentScheme = NULL;
513 m_builtinScheme = NULL;
514 m_iMaxRecent = atoi(XAP_PREF_DEFAULT_MaxRecent);
515 m_bInChangeBlock = false;
516 m_bIgnoreThisOne = false;
517 memset(&m_geom, 0, sizeof(m_geom));
518
519 // NOTE: since constructors cannot report g_try_malloc
520 // NOTE: failures (and since it is virtual back
521 // NOTE: to the application), our creator must call
522 // NOTE: loadBuiltinPrefs().
523
524 // NOTE: we do not initialize the values in m_parserState
525 // NOTE: since they are only used by the parser, it can
526 // NOTE: initialize them.
527 }
528
~XAP_Prefs(void)529 XAP_Prefs::~XAP_Prefs(void)
530 {
531 UT_VECTOR_PURGEALL(XAP_PrefsScheme *, m_vecSchemes);
532 UT_VECTOR_PURGEALL(XAP_PrefsScheme *, m_vecPluginSchemes);
533 UT_VECTOR_FREEALL(char *, m_vecRecent);
534 UT_VECTOR_PURGEALL(tPrefsListenersPair *, m_vecPrefsListeners);
535 UT_VECTOR_PURGEALL(UT_UTF8String *, m_vecLog);
536 }
537
538 /*****************************************************************/
539
_getNthScheme(UT_uint32 k,const UT_GenericVector<XAP_PrefsScheme * > & vecSchemes) const540 XAP_PrefsScheme * XAP_Prefs::_getNthScheme(UT_uint32 k, const UT_GenericVector<XAP_PrefsScheme *> &vecSchemes) const
541 {
542 UT_uint32 kLimit = vecSchemes.getItemCount();
543 if (k < kLimit)
544 return vecSchemes.getNthItem(k);
545 else
546 return NULL;
547 }
548
getNthScheme(UT_uint32 k) const549 XAP_PrefsScheme * XAP_Prefs::getNthScheme(UT_uint32 k) const
550 {
551 return _getNthScheme(k, m_vecSchemes);
552 }
553
getNthPluginScheme(UT_uint32 k) const554 XAP_PrefsScheme * XAP_Prefs::getNthPluginScheme(UT_uint32 k) const
555 {
556 return _getNthScheme(k, m_vecPluginSchemes);
557 }
558
getScheme(const gchar * szSchemeName) const559 XAP_PrefsScheme * XAP_Prefs::getScheme(const gchar * szSchemeName) const
560 {
561 UT_uint32 kLimit = m_vecSchemes.getItemCount();
562 UT_uint32 k;
563
564 for (k=0; k<kLimit; k++)
565 {
566 XAP_PrefsScheme * p = getNthScheme(k);
567 if(!p)
568 {
569 UT_ASSERT_HARMLESS(p);
570 continue;
571 }
572 if (strcmp(static_cast<const char*>(szSchemeName),static_cast<const char*>(p->getSchemeName())) == 0)
573 return p;
574 }
575
576 return NULL;
577 }
578
getPluginScheme(const gchar * szSchemeName) const579 XAP_PrefsScheme * XAP_Prefs::getPluginScheme(const gchar * szSchemeName) const
580 {
581 UT_uint32 kLimit = m_vecPluginSchemes.getItemCount();
582 UT_uint32 k;
583
584 for (k=0; k<kLimit; k++)
585 {
586 XAP_PrefsScheme * p = getNthPluginScheme(k);
587 if(!p)
588 {
589 UT_ASSERT_HARMLESS(p);
590 continue;
591 }
592 if (strcmp(static_cast<const char*>(szSchemeName),static_cast<const char*>(p->getSchemeName())) == 0)
593 return p;
594 }
595
596 return NULL;
597 }
598
addScheme(XAP_PrefsScheme * pNewScheme)599 bool XAP_Prefs::addScheme(XAP_PrefsScheme * pNewScheme)
600 {
601 const gchar * szBuiltinSchemeName = getBuiltinSchemeName();
602 const gchar * szThisSchemeName = pNewScheme->getSchemeName();
603
604 if (strcmp(static_cast<const char*>(szThisSchemeName), static_cast<const char*>(szBuiltinSchemeName)) == 0)
605 {
606 UT_ASSERT(m_builtinScheme == NULL);
607 m_builtinScheme = pNewScheme;
608 }
609
610 return (m_vecSchemes.addItem(pNewScheme) == 0);
611 }
612
addPluginScheme(XAP_PrefsScheme * pNewScheme)613 bool XAP_Prefs::addPluginScheme(XAP_PrefsScheme * pNewScheme)
614 {
615 return (m_vecPluginSchemes.addItem(pNewScheme) == 0);
616 }
617
getCurrentScheme(bool bCreate)618 XAP_PrefsScheme * XAP_Prefs::getCurrentScheme(bool bCreate)
619 {
620 if (bCreate)
621 {
622 // the builtin scheme is not updatable,
623 // so we may need to create one that is
624 if ( !strcmp(static_cast<const char*>(m_currentScheme->getSchemeName()), "_builtin_") )
625 {
626
627
628 const gchar new_name[] = "_custom_";
629
630 if (setCurrentScheme(new_name))
631 {
632 // unused _custom_ scheme is lying around, so recycle it
633 UT_ASSERT_HARMLESS(UT_TODO);
634
635 // HYP: reset the current scheme's hash table contents?
636 // ALT: replace the existing scheme with new empty one
637 }
638 else
639 {
640 // we need to create it
641 XAP_PrefsScheme * pNewScheme = new XAP_PrefsScheme(this, new_name);
642 UT_ASSERT(pNewScheme);
643 addScheme(pNewScheme);
644 setCurrentScheme(new_name);
645 }
646 }
647 }
648
649 return m_currentScheme;
650 }
651
setCurrentScheme(const gchar * szSchemeName)652 bool XAP_Prefs::setCurrentScheme(const gchar * szSchemeName)
653 {
654 // set the current scheme.
655
656 // TODO notify the application that the scheme has changed
657
658 XAP_PrefsScheme * p = getScheme(szSchemeName);
659 if (!p)
660 return false;
661
662 UT_DEBUGMSG(("Preferences::setCurrentScheme [%s].\n",szSchemeName));
663
664 m_currentScheme = p;
665 return true;
666 }
667
668 /*****************************************************************/
669 static const gchar DEBUG_PREFIX[] = "DeBuG"; // case insensitive
670 static const gchar NO_PREF_VALUE[] = "";
671
getPrefsValue(const gchar * szKey,const gchar ** pszValue,bool bAllowBuiltin) const672 bool XAP_Prefs::getPrefsValue(const gchar * szKey, const gchar ** pszValue, bool bAllowBuiltin) const
673 {
674 // a convenient routine to get a name/value pair from the current scheme
675
676 UT_return_val_if_fail(m_currentScheme,false);
677
678 if (m_currentScheme->getValue(szKey,pszValue))
679 return true;
680 if (bAllowBuiltin && m_builtinScheme->getValue(szKey,pszValue))
681 return true;
682 // It is legal for there to be arbitrary preference tags that start with
683 // "Debug", and Abi apps won't choke. The idea is that developers can use
684 // these to selectively trigger development-time behaviors.
685 if (g_ascii_strncasecmp(szKey, DEBUG_PREFIX, sizeof(DEBUG_PREFIX) - 1) == 0)
686 {
687 *pszValue = NO_PREF_VALUE;
688 return true;
689 }
690
691 return false;
692 }
693
getPrefsValue(const UT_String & stKey,UT_String & stValue,bool bAllowBuiltin) const694 bool XAP_Prefs::getPrefsValue(const UT_String &stKey, UT_String &stValue, bool bAllowBuiltin) const
695 {
696 UT_return_val_if_fail(m_currentScheme,false);
697
698 if (m_currentScheme->getValue(stKey, stValue))
699 return true;
700 if (bAllowBuiltin && m_builtinScheme->getValue(stKey, stValue))
701 return true;
702
703 // It is legal for there to be arbitrary preference tags that start with
704 // "Debug", and Abi apps won't choke. The idea is that developers can use
705 // these to selectively trigger development-time behaviors.
706 if (g_ascii_strncasecmp(stKey.c_str(), DEBUG_PREFIX, sizeof(DEBUG_PREFIX) - 1) == 0)
707 {
708 stValue = NO_PREF_VALUE;
709 return true;
710 }
711
712 return false;
713 }
714
getPrefsValueBool(const gchar * szKey,bool * pbValue,bool bAllowBuiltin) const715 bool XAP_Prefs::getPrefsValueBool(const gchar * szKey, bool * pbValue, bool bAllowBuiltin) const
716 {
717 // a convenient routine to get a name/value pair from the current scheme
718
719 UT_return_val_if_fail(m_currentScheme,false);
720
721 if (m_currentScheme->getValueBool(szKey,pbValue))
722 return true;
723 if (bAllowBuiltin && m_builtinScheme->getValueBool(szKey,pbValue))
724 return true;
725 // It is legal for there to be arbitrary preference tags that start with
726 // "Debug", and Abi apps won't choke. The idea is that developers can use
727 // these to selectively trigger development-time behaviors.
728 if (g_ascii_strncasecmp(szKey, DEBUG_PREFIX, sizeof(DEBUG_PREFIX) - 1) == 0)
729 {
730 *pbValue = false;
731 return true;
732 }
733
734 return false;
735 }
736
getPrefsValueInt(const gchar * szKey,int & nValue,bool bAllowBuiltin) const737 bool XAP_Prefs::getPrefsValueInt(const gchar * szKey, int& nValue, bool bAllowBuiltin) const
738 {
739 // a convenient routine to get a name/value pair from the current scheme
740
741 UT_return_val_if_fail(m_currentScheme,false);
742
743 if (m_currentScheme->getValueInt(szKey,nValue))
744 return true;
745 if (bAllowBuiltin && m_builtinScheme->getValueInt(szKey,nValue))
746 return true;
747 // It is legal for there to be arbitrary preference tags that start with
748 // "Debug", and Abi apps won't choke. The idea is that developers can use
749 // these to selectively trigger development-time behaviors.
750 if (g_ascii_strncasecmp(szKey, DEBUG_PREFIX, sizeof(DEBUG_PREFIX) - 1) == 0)
751 {
752 nValue = -1;
753 return true;
754 }
755
756 return false;
757 }
758
759
n_compare(const char * name,const xmlToIdMapping * id)760 static int n_compare (const char *name, const xmlToIdMapping *id)
761 {
762 return strcmp (name, id->m_name);
763 }
764
765
766 /*****************************************************************/
767
startElement(const gchar * name,const gchar ** atts)768 void XAP_Prefs::startElement(const gchar *name, const gchar **atts)
769 {
770 if (m_bLoadSystemDefaultFile) /* redirection - used to happen earlier */
771 {
772 _startElement_SystemDefaultFile (name, atts);
773 return;
774 }
775 UT_DEBUGMSG(("Looking for %s \n",name));
776 XAP_PrefsScheme * pNewScheme = NULL; // must be freed
777
778 if (!m_parserState.m_parserStatus) // eat if already had an error
779 return;
780
781 xmlToIdMapping * id = NULL;
782 id = static_cast<xmlToIdMapping *>(bsearch (static_cast<const void*>(name), static_cast<const void*>(s_Tokens),
783 sizeof(s_Tokens)/sizeof(xmlToIdMapping),
784 sizeof (xmlToIdMapping),
785 (int (*)(const void*, const void*))
786 n_compare));
787 if (!id)
788 {
789 UT_DEBUGMSG(("Didin't find it! Abort! \n"));
790 UT_ASSERT_HARMLESS(0);
791 return;
792 }
793 switch (id->m_type)
794 {
795 case TT_LOG: // ignore
796 break;
797
798 case TT_FONTS:
799 {
800 m_parserState.m_bFoundFonts = true;
801 const gchar ** a = atts;
802
803 while (a && *a)
804 {
805 if (!strcmp (a[0], "include"))
806 {
807 if (!strcmp (a[1], "1") || !strcmp (a[1], "true"))
808 m_fonts.setIncludeFlag(true);
809 else
810 m_fonts.setIncludeFlag(false);
811 }
812 else
813 {
814 UT_DEBUGMSG(("Preferences: unknown attribute [%s] "
815 "for <Fontface>\n",
816 a[0]));
817 }
818
819 a += 2;
820 }
821 }
822 break;
823
824 case TT_FACE:
825 {
826 if (!m_parserState.m_bFoundFonts)
827 {
828 UT_ASSERT_HARMLESS (UT_SHOULD_NOT_HAPPEN);
829 break;
830 }
831
832 const gchar ** a = atts;
833 const gchar * pName = NULL;
834
835 while (a && *a)
836 {
837 if (!strcmp (a[0], "name"))
838 {
839 pName = a[1];
840 }
841 else
842 {
843 UT_DEBUGMSG(("Preferences: unknown attribute [%s] "
844 "for <Fontface>\n",
845 a[0]));
846 }
847
848 a += 2;
849 }
850
851 if (pName)
852 m_fonts.addFont (pName);
853
854 }
855 break;
856
857 case TT_ABIPREFERENCES:
858 {
859 m_parserState.m_bFoundAbiPreferences = true;
860
861 // we expect something of the form:
862 // <AbiPreferences app="AbiWord" ver="1.0">...</AbiPreferences>
863
864 const gchar ** a = atts;
865 while (a && *a)
866 {
867 UT_ASSERT(a[1] && *a[1]); // require a value for each attribute keyword
868
869 if (strcmp(static_cast<const char*>(a[0]), "app") == 0)
870 {
871 // TODO the following test will fail if you are running
872 // TODO both an AbiWord (release) build and an AbiWord
873 // TODO Personal (development/personal) build. That is,
874 // TODO you'll lose your MRU list if you alternate between
875 // TODO the two types of executables.
876
877 const char * szThisApp = XAP_App::getApp()->getApplicationName();
878 UT_DEBUGMSG(("Found preferences for application [%s] (this is [%s]).\n",
879 a[1],szThisApp));
880 if (strcmp(static_cast<const char*>(a[1]),szThisApp) != 0)
881 {
882 UT_DEBUGMSG(("Preferences file does not match this application.\n"));
883 goto InvalidFileError;
884 }
885 }
886 else if (strcmp(static_cast<const char*>(a[0]), "ver") == 0)
887 {
888 // TODO test version number
889 }
890
891 a += 2;
892 }
893 break;
894 }
895 case TT_SELECT:
896 {
897 m_parserState.m_bFoundSelect = true;
898
899 // we expect something of the form:
900 // <Select
901 // scheme="myScheme"
902 // autosaveprefs="1"
903 // useenvlocale="1"
904 // />
905
906 const gchar ** a = atts;
907 while (a && *a)
908 {
909 UT_ASSERT(a[1] && *a[1]); // require a value for each attribute keyword
910
911 if (strcmp(static_cast<const char*>(a[0]), "scheme") == 0)
912 {
913 FREEP(m_parserState.m_szSelectedSchemeName);
914 if (!(m_parserState.m_szSelectedSchemeName = g_strdup(static_cast<const char*>(a[1]))))
915 goto MemoryError;
916 }
917 else if (strcmp(static_cast<const char*>(a[0]), "autosaveprefs") == 0)
918 {
919 // m_bAutoSavePrefs controls whether we automatically
920 // save any changes in the preferences during
921 // interactive use/manipulation of the UI or if
922 // we wait until the user explicitly does a save.
923 // MSFT has this annoying tendency to session-persist
924 // almost everything in the UI -- we can do that,
925 // but lets not make it the default....
926
927 m_bAutoSavePrefs = (*a[1] == '1');
928 }
929 else if (strcmp(static_cast<const char*>(a[0]), "useenvlocale") == 0)
930 {
931 m_bUseEnvLocale = (*a[1] == '1');
932 }
933
934 a += 2;
935 }
936
937 if (!m_parserState.m_szSelectedSchemeName)
938 {
939 UT_DEBUGMSG(("No scheme selected in <Select...>\n"));
940 goto InvalidFileError;
941 }
942 break;
943 }
944 case TT_SCHEME:
945 {
946 // we found a preferences scheme. we expect something of the form:
947 // <Scheme name="myScheme" n0="v0" n1="v1" ... />
948 // where the [nk,vk] are arbitrary name/value pairs that mean
949 // something to the application.
950 //
951 // if there are duplicated name/value pairs our behavior is
952 // undefined -- we remember the last one that the XML parser
953 // give us.
954
955 // bool bIsNamed = false;
956
957 pNewScheme = new XAP_PrefsScheme(this, NULL);
958 if (!pNewScheme)
959 goto MemoryError;
960
961 const gchar ** a = atts;
962 while (*a)
963 {
964 UT_ASSERT(a[1] && *a[1]); // require a value for each attribute keyword
965
966 if (strcmp(static_cast<const char*>(a[0]), "name") == 0)
967 {
968 // bIsNamed = true;
969
970 const gchar * szBuiltinSchemeName = getBuiltinSchemeName();
971
972 if (strcmp(static_cast<const char*>(a[1]), static_cast<const char*>(szBuiltinSchemeName)) == 0)
973 {
974 UT_DEBUGMSG(("Reserved scheme name [%s] found in file; ignoring.\n",a[1]));
975 goto IgnoreThisScheme;
976 }
977
978 if (getScheme(a[1]))
979 {
980 UT_DEBUGMSG(("Duplicate scheme [%s]; ignoring latter instance.\n",a[1]));
981 goto IgnoreThisScheme;
982 }
983
984 if (!pNewScheme->setSchemeName(a[1]))
985 goto MemoryError;
986
987 UT_DEBUGMSG(("Found Preferences scheme [%s].\n",a[1]));
988 }
989 else
990 {
991 if (!pNewScheme->setValue(a[0],a[1]))
992 goto MemoryError;
993 }
994
995 a += 2;
996 }
997
998 if (!addScheme(pNewScheme))
999 goto MemoryError;
1000 pNewScheme = NULL; // we don't own it anymore
1001 break;
1002 }
1003 case TT_PLUGIN:
1004 {
1005 // Almost the same as TT_SCHEME, except is denoted by <Plugin ... />
1006 // instead of <Scheme ... /> and has no builtin to deal with
1007
1008 // bool bIsNamed = false;
1009
1010 pNewScheme = new XAP_PrefsScheme(this, NULL);
1011 if (!pNewScheme)
1012 goto MemoryError;
1013
1014 const gchar ** a = atts;
1015 while (*a)
1016 {
1017 UT_ASSERT(a[1] && *a[1]); // require a value for each attribute keyword
1018
1019 if (strcmp(static_cast<const char*>(a[0]), "name") == 0)
1020 {
1021 // bIsNamed = true;
1022
1023 if (getPluginScheme(a[1]))
1024 {
1025 UT_DEBUGMSG(("Duplicate Plugin scheme [%s]; ignoring latter instance.\n",a[1]));
1026 goto IgnoreThisScheme;
1027 }
1028
1029 if (!pNewScheme->setSchemeName(a[1]))
1030 goto MemoryError;
1031
1032 UT_DEBUGMSG(("Found Preferences Plugin scheme [%s].\n",a[1]));
1033 }
1034 else
1035 {
1036 if (!pNewScheme->setValue(a[0],a[1]))
1037 goto MemoryError;
1038 }
1039
1040 a += 2;
1041 }
1042
1043 if (!addPluginScheme(pNewScheme))
1044 goto MemoryError;
1045 pNewScheme = NULL; // we don't own it anymore
1046 break;
1047 }
1048 case TT_RECENT:
1049 {
1050 m_parserState.m_bFoundRecent = true;
1051
1052 // we expect something of the form:
1053 // <Recent max="4" name1="v1" name2="v2" ... />
1054
1055 const gchar ** a = atts;
1056 while (*a)
1057 {
1058 UT_ASSERT(a[1] && *a[1]); // require a value for each attribute keyword
1059
1060 if (strcmp(static_cast<const char*>(a[0]), "max") == 0)
1061 {
1062 m_iMaxRecent = atoi(static_cast<const char*>(a[1]));
1063 }
1064 else if (strncmp(static_cast<const char*>(a[0]), "name", 4) == 0)
1065 {
1066 // NOTE: taking advantage of the fact that gchar == char
1067 UT_ASSERT((sizeof(gchar) == sizeof(char)));
1068 gchar * sz;
1069
1070 // see bug 10709 - Non-URI paths aren't displayed correctly in the file menu
1071 // this provides a seamless migration
1072
1073 char * uri;
1074 if (UT_go_path_is_uri (a[1]))
1075 uri = g_strdup (a[1]);
1076 else
1077 uri = UT_go_filename_to_uri (a[1]);
1078
1079 sz = g_strdup(uri);
1080
1081 g_free (uri);
1082
1083 // NOTE: we keep the copied string in the vector
1084 m_vecRecent.addItem(sz);
1085 }
1086
1087 a += 2;
1088 }
1089
1090 _pruneRecent();
1091 break;
1092 }
1093 case TT_GEOMETRY:
1094 {
1095 if (m_geom.m_flags & PREF_FLAG_GEOMETRY_NOUPDATE) break;
1096 m_parserState.m_bFoundGeometry = true;
1097
1098 // we expect something of the form:
1099 // <Geometry width="xxx" height="xxx" posx="xxx" posy="xxx" />
1100
1101 // These are the fall-back defaults which will be applied
1102 // if the user does NOT specify a geometry argument or
1103 // have set preference values. We only want to obey the
1104 // size not a position.
1105
1106 // (Both m_geom and the temporary variables are
1107 // initialized so partial/invalid preference data from the
1108 // file will still have sensible fall-back defaults).
1109 UT_uint32 width = 800, height = 600, flags = PREF_FLAG_GEOMETRY_SIZE;
1110 UT_sint32 posx = 0, posy = 0;
1111
1112 XAP_App::getApp()->getDefaultGeometry(width,height, flags);
1113
1114 m_geom.m_width = width;
1115 m_geom.m_height = height;
1116 m_geom.m_posx = posx;
1117 m_geom.m_posy = posy;
1118 m_geom.m_flags = flags;
1119
1120 const gchar ** a = atts;
1121 while (*a)
1122 {
1123 UT_ASSERT(a[1] && *a[1]); // require a value for each attribute keyword
1124
1125 if (strcmp(static_cast<const char*>(a[0]), "width") == 0)
1126 {
1127 width = atoi(static_cast<const char*>(a[1]));
1128 }
1129 else if (strcmp(static_cast<const char*>(a[0]), "height") == 0)
1130 {
1131 height = atoi(static_cast<const char*>(a[1]));
1132 }
1133 else if (strcmp(static_cast<const char*>(a[0]), "posx") == 0)
1134 {
1135 posx = atoi(static_cast<const char*>(a[1]));
1136 }
1137 else if (strcmp(static_cast<const char*>(a[0]), "posy") == 0)
1138 {
1139 posy = atoi(static_cast<const char*>(a[1]));
1140 }
1141 else if (strcmp(static_cast<const char*>(a[0]), "flags") == 0)
1142 {
1143 flags = atoi(static_cast<const char*>(a[1])) & ~PREF_FLAG_GEOMETRY_NOUPDATE;
1144 }
1145
1146 a += 2;
1147 }
1148
1149 // Override the fall-backs as appropriate with preference
1150 // settings.
1151 if (flags & PREF_FLAG_GEOMETRY_SIZE)
1152 {
1153 m_geom.m_width = width;
1154 m_geom.m_height = height;
1155 m_geom.m_flags |= PREF_FLAG_GEOMETRY_SIZE;
1156 }
1157 if (flags & PREF_FLAG_GEOMETRY_POS)
1158 {
1159 m_geom.m_posx = posx;
1160 m_geom.m_posy = posy;
1161 m_geom.m_flags |= PREF_FLAG_GEOMETRY_POS;
1162 }
1163
1164 if (!(flags & PREF_FLAG_GEOMETRY_MAXIMIZED))
1165 m_geom.m_flags &= ~PREF_FLAG_GEOMETRY_MAXIMIZED;
1166
1167
1168 }
1169 }
1170 // successful parse of tag...
1171 IgnoreThisScheme:
1172 DELETEP(pNewScheme);
1173 return; // success
1174
1175 MemoryError:
1176 UT_DEBUGMSG(("Memory error parsing preferences file.\n"));
1177 InvalidFileError:
1178 m_parserState.m_parserStatus = false; // cause parser driver to bail
1179 DELETEP(pNewScheme);
1180 return;
1181 }
1182
endElement(const gchar *)1183 void XAP_Prefs::endElement(const gchar * /* name */)
1184 {
1185 // everything in this file is contained in start-tags
1186 return;
1187 }
1188
charData(const gchar *,int)1189 void XAP_Prefs::charData(const gchar * /* s */, int /* len */)
1190 {
1191 // everything in this file is contained in start-tags
1192 return;
1193 }
1194
1195 /*****************************************************************/
1196
1197
loadPrefsFile(void)1198 bool XAP_Prefs::loadPrefsFile(void)
1199 {
1200 bool bResult = false; // assume failure
1201 const char * szFilename;
1202
1203 m_parserState.m_parserStatus = true;
1204 m_parserState.m_bFoundAbiPreferences = false;
1205 m_parserState.m_bFoundSelect = false;
1206 m_parserState.m_szSelectedSchemeName = NULL;
1207 m_parserState.m_bFoundRecent = false;
1208 m_parserState.m_bFoundGeometry = false;
1209 m_parserState.m_bFoundFonts = false;
1210 m_bLoadSystemDefaultFile = false;
1211
1212 UT_XML parser;
1213
1214 szFilename = getPrefsPathname();
1215 if (!szFilename)
1216 {
1217 UT_DEBUGMSG(("could not get pathname for preferences file.\n"));
1218 goto Cleanup;
1219 }
1220
1221 parser.setListener (this);
1222 if ((parser.parse (szFilename) != UT_OK) || (!m_parserState.m_parserStatus))
1223 {
1224 UT_DEBUGMSG(("Problem reading (Preferences) document\n"));
1225 goto Cleanup;
1226 }
1227
1228 // we succeeded in parsing the file,
1229 // now check for higher-level consistency.
1230
1231 if (!m_parserState.m_bFoundAbiPreferences)
1232 {
1233 UT_DEBUGMSG(("Did not find <AbiPreferences...>\n"));
1234 goto Cleanup;
1235 }
1236 if (!m_parserState.m_bFoundSelect)
1237 {
1238 UT_DEBUGMSG(("Did not find <Select...>\n"));
1239 goto Cleanup;
1240 }
1241 if (!m_parserState.m_bFoundRecent)
1242 {
1243 UT_DEBUGMSG(("Did not find <Recent...>\n"));
1244 // Note: it's ok if we didn't find it...
1245 }
1246 if (!m_parserState.m_bFoundGeometry)
1247 {
1248 UT_DEBUGMSG(("Did not find <Geometry...>\n"));
1249 // Note: it's ok if we didn't find it...
1250 }
1251
1252 UT_ASSERT(m_parserState.m_szSelectedSchemeName);
1253 if (!setCurrentScheme(m_parserState.m_szSelectedSchemeName))
1254 {
1255 UT_DEBUGMSG(("Selected scheme [%s] not found in preferences file.\n",
1256 m_parserState.m_szSelectedSchemeName));
1257 goto Cleanup;
1258 }
1259
1260 bResult = true;
1261 Cleanup:
1262 FREEP(m_parserState.m_szSelectedSchemeName);
1263
1264 return bResult;
1265 }
1266
savePrefsFile(void)1267 bool XAP_Prefs::savePrefsFile(void)
1268 {
1269 bool bResult = false; // assume failure
1270 const char * szFilename;
1271 FILE * fp = NULL;
1272 #ifdef WIN32
1273 UT_Win32LocaleString str;
1274 #endif
1275
1276 szFilename = getPrefsPathname();
1277 if (!szFilename)
1278 {
1279 UT_DEBUGMSG(("could not get pathname for preferences file.\n"));
1280 goto Cleanup;
1281 }
1282
1283 #ifdef WIN32
1284 // TODO: something more elegant
1285 str.fromUTF8(szFilename);
1286 fp = _wfopen(str.c_str(), L"w");
1287 #else
1288 fp = fopen(szFilename, "w");
1289 #endif
1290 if (!fp)
1291 {
1292 UT_DEBUGMSG(("could not open preferences file [%s].\n",szFilename));
1293 goto Cleanup;
1294 }
1295
1296 // write a comment block as a prolog.
1297 // NOTE: this is human readable information only.
1298
1299 fprintf(fp,"<!-- ===================================================================== -->\n");
1300 fprintf(fp,"<!-- This file contains AbiSuite Preferences. AbiSuite is a suite of Open -->\n");
1301 fprintf(fp,"<!-- Source desktop applications developed by AbiSource, Inc. Information -->\n");
1302 fprintf(fp,"<!-- about this application can be found at http://www.abisource.com -->\n");
1303 fprintf(fp,"<!-- You should not edit this file by hand. -->\n");
1304 fprintf(fp,"<!-- ===================================================================== -->\n");
1305 fprintf(fp,"\n");
1306
1307 #if 1 //def DEBUG -- want this always in, it helps debugging
1308 if (XAP_App::s_szBuild_ID && XAP_App::s_szBuild_ID[0])
1309 {
1310 fprintf(fp,"<!-- Build_ID = ");
1311 fprintf(fp,"%s",XAP_App::s_szBuild_ID);
1312 fprintf(fp," -->\n");
1313 }
1314 if (XAP_App::s_szBuild_Version && XAP_App::s_szBuild_Version[0])
1315 {
1316 fprintf(fp,"<!-- Build_Version = ");
1317 fprintf(fp,"%s",XAP_App::s_szBuild_Version);
1318 fprintf(fp," -->\n");
1319 }
1320 if (XAP_App::s_szBuild_Options && XAP_App::s_szBuild_Options[0])
1321 {
1322 fprintf(fp,"<!-- Build_Options = ");
1323 fprintf(fp,"%s",XAP_App::s_szBuild_Options);
1324 fprintf(fp," -->\n");
1325 }
1326 if (XAP_App::s_szBuild_Target && XAP_App::s_szBuild_Target[0])
1327 {
1328 fprintf(fp,"<!-- Build_Target = ");
1329 fprintf(fp,"%s",XAP_App::s_szBuild_Target);
1330 fprintf(fp," -->\n");
1331 }
1332 if (XAP_App::s_szBuild_CompileTime && XAP_App::s_szBuild_CompileTime[0])
1333 {
1334 fprintf(fp,"<!-- Build_CompileTime = ");
1335 fprintf(fp,"%s",XAP_App::s_szBuild_CompileTime);
1336 fprintf(fp," -->\n");
1337 }
1338 if (XAP_App::s_szBuild_CompileDate && XAP_App::s_szBuild_CompileDate[0])
1339 {
1340 fprintf(fp,"<!-- Build_CompileDate = ");
1341 fprintf(fp,"%s",XAP_App::s_szBuild_CompileDate);
1342 fprintf(fp," -->\n");
1343 }
1344 #endif
1345
1346 // end of prolog.
1347 // now we begin the actual document.
1348
1349 UT_ASSERT(m_builtinScheme);
1350
1351 fprintf(fp,"\n<AbiPreferences app=\"%s\" ver=\"%s\">\n",
1352 XAP_App::getApp()->getApplicationName(),
1353 "1.0");
1354 {
1355 fprintf(fp,("\n"
1356 "\t<Select\n"
1357 "\t scheme=\"%s\"\n"
1358 "\t autosaveprefs=\"%d\"\n"
1359 "\t useenvlocale=\"%d\"\n"
1360 "\t/>\n"),
1361 m_currentScheme->getSchemeName(),
1362 static_cast<UT_uint32>(m_bAutoSavePrefs),
1363 static_cast<UT_uint32>(m_bUseEnvLocale));
1364
1365 UT_uint32 kLimit = m_vecSchemes.getItemCount();
1366 UT_uint32 k;
1367
1368 const gchar * szBuiltinSchemeName = getBuiltinSchemeName();
1369
1370 for (k=0; k<kLimit; k++)
1371 {
1372 XAP_PrefsScheme * p = getNthScheme(k);
1373 if(!p)
1374 {
1375 UT_ASSERT_HARMLESS(p);
1376 continue;
1377 }
1378
1379 const gchar * szThisSchemeName = p->getSchemeName();
1380 bool bIsBuiltin = (p == m_builtinScheme);
1381
1382 if (bIsBuiltin)
1383 {
1384 fprintf(fp,("\n"
1385 "\t<!-- The following scheme, %s, contains the built-in application\n"
1386 "\t**** defaults and adjusted by the installation system defaults. This scheme\n"
1387 "\t**** is only written here as a reference. Any schemes following this one\n"
1388 "\t**** only list values that deviate from the built-in values.\n"
1389 "\t**** Items values must observe XML encoding for double quote ("),\n"
1390 "\t**** ampersand (&), and angle brackets (< and >).\n"
1391 "\t-->\n"),
1392 szBuiltinSchemeName);
1393 }
1394
1395 fprintf(fp,"\n\t<Scheme\n\t\tname=\"%s\"\n",szThisSchemeName);
1396
1397 const gchar * szKey;
1398 const gchar * szValue;
1399 UT_uint32 j;
1400
1401 for (j=0;(p->getNthValue(j, &szKey, &szValue)) ; j++)
1402 {
1403 bool need_print;
1404 need_print = false;
1405 if (bIsBuiltin)
1406 {
1407 // for the builtin set, we print every value
1408 need_print = true;
1409 }
1410 else
1411 {
1412 // for non-builtin sets, we only print the values which are different
1413 // from the builtin set.
1414 const gchar * szBuiltinValue = NO_PREF_VALUE;
1415 m_builtinScheme->getValue(szKey,&szBuiltinValue);
1416 if (strcmp(static_cast<const char*>(szValue),static_cast<const char*>(szBuiltinValue)) != 0 ||
1417 // Always print debug values
1418 strncmp(static_cast<const char*>(szKey), static_cast<const char*>(DEBUG_PREFIX),
1419 sizeof(DEBUG_PREFIX) - 1) == 0)
1420 {
1421 need_print = true;
1422 }
1423 }
1424 if (need_print == true)
1425 {
1426 // szValue is UTF-8. Convert to Unicode and then
1427 // do XML-encoding of XML-special characters and
1428 // non-ASCII characters. The printed value
1429 // strings will get XML parsing and conversion to
1430 // UTF-8 the next time the application reads the
1431 // prefs file.
1432 UT_GrowBuf gb;
1433 UT_decodeUTF8string(szValue, strlen(szValue), &gb);
1434 UT_uint32 length = gb.getLength();
1435 fprintf(fp,"\t\t%s=\"",szKey);
1436 for (UT_uint32 udex=0; udex<length; ++udex)
1437 {
1438 UT_UCSChar ch = *(gb.getPointer(udex));
1439 switch (ch)
1440 {
1441 case '&': fputs("&", fp); break;
1442 case '<': fputs("<", fp); break;
1443 case '>': fputs(">", fp); break;
1444 case '"': fputs(""", fp); break;
1445 default:
1446 if (ch < ' ' || ch >= 128)
1447 {
1448 fprintf(fp, "&#x%x;", ch);
1449 }
1450 else
1451 {
1452 putc(ch, fp);
1453 }
1454 }
1455 }
1456 fputs("\"\n", fp);
1457 }
1458 }
1459
1460 fprintf(fp,"\t\t/>\n");
1461 }
1462
1463 // add Plugin Scheme (plugin specific preferences) if they exist
1464 kLimit = m_vecPluginSchemes.getItemCount();
1465 for (k=0; k<kLimit; k++)
1466 {
1467 XAP_PrefsScheme * p = getNthPluginScheme(k);
1468 if(!p)
1469 {
1470 UT_ASSERT_HARMLESS(p);
1471 continue;
1472 }
1473
1474 const gchar * szThisSchemeName = p->getSchemeName();
1475 fprintf(fp,"\n\t<Plugin\n\t\tname=\"%s\"\n",szThisSchemeName);
1476
1477 const gchar * szKey;
1478 const gchar * szValue;
1479 UT_uint32 j;
1480
1481 for (j=0;(p->getNthValue(j, &szKey, &szValue)) ; j++)
1482 {
1483 // szValue is UTF-8. Convert to Unicode and then
1484 // do XML-encoding of XML-special characters and
1485 // non-ASCII characters. The printed value
1486 // strings will get XML parsing and conversion to
1487 // UTF-8 the next time the application reads the
1488 // prefs file.
1489 UT_GrowBuf gb;
1490 UT_decodeUTF8string(szValue, strlen(szValue), &gb);
1491 UT_uint32 length = gb.getLength();
1492 fprintf(fp,"\t\t%s=\"",szKey);
1493 for (UT_uint32 udex=0; udex<length; ++udex)
1494 {
1495 UT_UCSChar ch = *(gb.getPointer(udex));
1496 switch (ch)
1497 {
1498 case '&': fputs("&", fp); break;
1499 case '<': fputs("<", fp); break;
1500 case '>': fputs(">", fp); break;
1501 case '"': fputs(""", fp); break;
1502 default:
1503 if (ch < ' ' || ch >= 128)
1504 {
1505 fprintf(fp, "&#x%x;", ch);
1506 }
1507 else
1508 {
1509 putc(ch, fp);
1510 }
1511 }
1512 }
1513 fputs("\"\n", fp);
1514 }
1515
1516 fprintf(fp,"\t\t/>\n");
1517 }
1518 // end Plugin preferences
1519
1520 fprintf(fp,"\n\t<Recent\n\t\tmax=\"%d\"\n",
1521 static_cast<UT_uint32>(m_iMaxRecent));
1522
1523 kLimit = m_vecRecent.getItemCount();
1524
1525 for (k=0; k<kLimit; k++)
1526 {
1527 const char * szRecent = getRecent(k+1);
1528 UT_UTF8String utf8string( szRecent );
1529 utf8string.escapeXML();
1530
1531 fprintf(fp,"\t\tname%d=\"%s\"\n",k+1,utf8string.utf8_str());
1532 }
1533
1534 fprintf(fp,"\t\t/>\n");
1535
1536 fprintf(fp,"\n\t<Geometry\n");
1537 fprintf(fp,"\t\twidth=\"%u\"\n", m_geom.m_width);
1538 fprintf(fp,"\t\theight=\"%u\"\n", m_geom.m_height);
1539 fprintf(fp,"\t\tposx=\"%d\"\n", m_geom.m_posx);
1540 fprintf(fp,"\t\tposy=\"%d\"\n", m_geom.m_posy);
1541 fprintf(fp,"\t\tflags=\"%d\"\n", m_geom.m_flags);
1542 fprintf(fp,"\t\t/>\n");
1543 }
1544
1545 {
1546 // now the log
1547 fprintf(fp, "\n\t<Log>\n");
1548
1549 UT_uint32 kLimit = m_vecLog.getItemCount();
1550 for (UT_uint32 k = 0; k < kLimit; ++k)
1551 {
1552 UT_UTF8String * s = (UT_UTF8String *) m_vecLog.getNthItem(k);
1553 fprintf(fp,"\t%s\n", s->utf8_str());
1554 }
1555
1556 fprintf(fp, "\t</Log>\n");
1557 }
1558
1559 {
1560 fprintf(fp, "\n\t<Fonts include=\"%d\">\n", m_fonts.getIncludeFlag());
1561 fprintf(fp,
1562 "\t<!--"
1563 "\n\t Here you can put a list of fonts to limit the fonts that appear "
1564 "\n\t in the font UI:\n"
1565 "\n\t\t<Face name=\"some face\"/>\n"
1566 "\n\t The include attribute of 'Fonts' controls the significance of "
1567 "\n\t the list:"
1568 "\n\t include=\"1\" - limit fonts to those listed"
1569 "\n\t include=\"0\" - exclude the listed fonts from the system font list"
1570 "\n\t-->");
1571
1572 const std::vector<UT_UTF8String> & v = m_fonts.getFonts();
1573
1574 for (std::vector<UT_UTF8String>::const_iterator k = v.begin();
1575 k != v.end() ; ++k)
1576 {
1577 fprintf(fp,"\n\t\t<Face name=\"%s\"/>",
1578 (*k).utf8_str());
1579 }
1580
1581 fprintf(fp, "\n\t</Fonts>\n");
1582 }
1583
1584 fprintf(fp,"\n</AbiPreferences>\n");
1585
1586 Cleanup:
1587 if (fp)
1588 fclose(fp);
1589 return bResult;
1590
1591 }
1592
1593 /*****************************************************************/
1594
_startElement_SystemDefaultFile(const gchar * name,const gchar ** atts)1595 void XAP_Prefs::_startElement_SystemDefaultFile(const gchar *name, const gchar **atts)
1596 {
1597 // routine to parse system default preferences file and
1598 // overlay values onto the builtin scheme.
1599
1600 if (!m_parserState.m_parserStatus) // eat if already had an error
1601 return;
1602
1603 if (strcmp(static_cast<const char*>(name), "SystemDefaults") == 0)
1604 {
1605 // we found the system default preferences scheme.
1606 //
1607 // we expect something of the form:
1608 // <SystemDefaults n0="v0" n1="v1" ... />
1609 // where the [nk,vk] are arbitrary name/value pairs
1610 // that mean something to the application.
1611 //
1612 // if there are duplicated name/value pairs our behavior is
1613 // undefined -- we remember the last one that the XML parser
1614 // give us.
1615
1616 const gchar ** a = atts;
1617 while (a && *a)
1618 {
1619 UT_ASSERT(a[1] && *a[1]); // require a value for each attribute keyword
1620
1621 // we ignore "name=<schemename>" just incase they copied and
1622 // pasted a user-profile into the system file.
1623
1624 if (strcmp(static_cast<const char*>(a[0]), "name") != 0)
1625 if (!m_builtinScheme->setValue(a[0],a[1]))
1626 goto MemoryError;
1627
1628 a += 2;
1629 }
1630 }
1631 else
1632 {
1633 UT_DEBUGMSG(("Ignoring tag [%s] in system default preferences file.\n",name));
1634 }
1635
1636 return; // success
1637
1638 MemoryError:
1639 UT_DEBUGMSG(("Memory error parsing preferences file.\n"));
1640 m_parserState.m_parserStatus = false; // cause parser driver to bail
1641 return;
1642 }
1643
1644 /*****************************************************************/
1645
loadSystemDefaultPrefsFile(const char * szSystemDefaultPrefsPathname)1646 bool XAP_Prefs::loadSystemDefaultPrefsFile(const char * szSystemDefaultPrefsPathname)
1647 {
1648 UT_ASSERT(szSystemDefaultPrefsPathname && *szSystemDefaultPrefsPathname);
1649
1650 bool bResult = false; // assume failure
1651 m_parserState.m_parserStatus = true;
1652
1653 m_bLoadSystemDefaultFile = true;
1654
1655 UT_XML parser;
1656 parser.setListener (this);
1657 if ((parser.parse (szSystemDefaultPrefsPathname) != UT_OK) || (!m_parserState.m_parserStatus))
1658 {
1659 UT_DEBUGMSG(("Problem reading (System Default Preferences) document\n"));
1660 goto Cleanup;
1661 }
1662
1663 // we succeeded in parsing the file,
1664
1665 bResult = true;
1666 Cleanup:
1667 return bResult;
1668 }
1669
1670 /*****************************************************************/
1671
addListener(PrefsListener pFunc,void * data)1672 void XAP_Prefs::addListener ( PrefsListener pFunc, void *data )
1673 {
1674 tPrefsListenersPair *pPair = new tPrefsListenersPair;
1675
1676 UT_return_if_fail(pPair);
1677 UT_ASSERT(pFunc);
1678
1679 pPair->m_pFunc = pFunc;
1680 pPair->m_pData = data;
1681
1682 m_vecPrefsListeners.addItem(pPair);
1683 }
1684
1685 // optional parameter, data. If given (i.e., != 0), will try to match data,
1686 // otherwise, will delete all calls to pFunc
removeListener(PrefsListener pFunc,void * data)1687 void XAP_Prefs::removeListener ( PrefsListener pFunc, void *data )
1688 {
1689 UT_sint32 index;
1690 tPrefsListenersPair *pPair;
1691
1692 for ( index = 0; index < m_vecPrefsListeners.getItemCount(); index++ )
1693 {
1694 pPair = m_vecPrefsListeners.getNthItem(index);
1695 UT_ASSERT_HARMLESS(pPair);
1696 if ( pPair ) {
1697 if ( pPair->m_pFunc == pFunc && (!data || pPair->m_pData == data) ) {
1698 m_vecPrefsListeners.deleteNthItem(index);
1699 delete pPair;
1700 }
1701 }
1702 }
1703 }
1704
_markPrefChange(const gchar * szKey)1705 void XAP_Prefs::_markPrefChange( const gchar *szKey )
1706 {
1707 if ( m_bInChangeBlock )
1708 {
1709 const void * uth_e = m_ahashChanges.pick(szKey );
1710
1711 if ( uth_e )
1712 uth_e = reinterpret_cast<const void *>(1);
1713 else
1714 m_ahashChanges.insert(szKey, reinterpret_cast<void *>(1));
1715
1716 // notify later
1717 }
1718 else
1719 {
1720 UT_StringPtrMap changes(3);
1721 changes.insert(szKey, reinterpret_cast<void *>(1));
1722
1723 _sendPrefsSignal(&changes);
1724 }
1725 }
1726
startBlockChange()1727 void XAP_Prefs::startBlockChange()
1728 {
1729 m_bInChangeBlock = true;
1730 }
1731
endBlockChange()1732 void XAP_Prefs::endBlockChange()
1733 {
1734 if ( m_bInChangeBlock )
1735 {
1736 m_bInChangeBlock = false;
1737 _sendPrefsSignal( &m_ahashChanges );
1738 }
1739 }
1740
_sendPrefsSignal(UT_StringPtrMap * hash)1741 void XAP_Prefs::_sendPrefsSignal( UT_StringPtrMap *hash )
1742 {
1743 UT_sint32 index;
1744 for ( index = 0; index < m_vecPrefsListeners.getItemCount(); index++ )
1745 {
1746 tPrefsListenersPair *p = m_vecPrefsListeners.getNthItem( index );
1747
1748 UT_ASSERT_HARMLESS(p && p->m_pFunc);
1749 if(!p || !p->m_pFunc)
1750 continue;
1751
1752 (p->m_pFunc)(this, hash, p->m_pData);
1753 }
1754 }
1755
isOnExcludeList(const char * name) const1756 bool XAP_FontSettings::isOnExcludeList (const char * name) const
1757 {
1758 if (m_bInclude)
1759 return false;
1760
1761 if (!m_vecFonts.size())
1762 return false;
1763
1764 std::vector<UT_UTF8String>::const_iterator i =
1765 std::find(m_vecFonts.begin(), m_vecFonts.end(), name);
1766
1767 return i != m_vecFonts.end();
1768 }
1769
1770