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 (&quot;),\n"
1390 							"\t**** ampersand (&amp;), and angle brackets (&lt; and &gt;).\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("&amp;", fp);  break;
1442 						case '<':   fputs("&lt;", fp);  break;
1443 						case '>':   fputs("&gt;", fp);  break;
1444 						case '"':   fputs("&quot;", 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("&amp;", fp);  break;
1499 						case '<':   fputs("&lt;", fp);  break;
1500 						case '>':   fputs("&gt;", fp);  break;
1501 						case '"':   fputs("&quot;", 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