/* AbiSource Application Framework * Copyright (C) 1998 AbiSource, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ // TODO add code to do an auto save anytime anything is changed. #include #include #include #include "ut_debugmsg.h" #include "ut_growbuf.h" #include "ut_string.h" #include "xap_Prefs.h" /*****************************************************************/ XAP_PrefsScheme::XAP_PrefsScheme( XAP_Prefs *pPrefs, const XML_Char * szSchemeName) : m_hash(13) { m_pPrefs = pPrefs; m_uTick = 0; if (szSchemeName && *szSchemeName) UT_XML_cloneString((XML_Char *&)m_szName,szSchemeName); else m_szName = NULL; } XAP_PrefsScheme::~XAP_PrefsScheme(void) { FREEP(m_szName); } const XML_Char * XAP_PrefsScheme::getSchemeName(void) const { return m_szName; } UT_Bool XAP_PrefsScheme::setSchemeName(const XML_Char * szNewSchemeName) { FREEP(m_szName); return UT_XML_cloneString(m_szName,szNewSchemeName); } UT_Bool XAP_PrefsScheme::setValue(const XML_Char * szKey, const XML_Char * szValue) { ++m_uTick; UT_HashEntry * pEntry = m_hash.findEntry((char*)szKey); if (pEntry) { if (UT_stricmp(szValue,pEntry->pszRight) == 0) return UT_TRUE; // equal values, no changes required m_hash.setEntry(pEntry, (char*)szValue, NULL); // update with new value } else { // otherwise, need to add a new entry m_hash.addEntry((char*)szKey,(char*)szValue,NULL); } m_pPrefs->_markPrefChange( szKey ); return UT_TRUE; } UT_Bool XAP_PrefsScheme::setValueBool(const XML_Char * szKey, UT_Bool bValue) { return setValue(szKey, (XML_Char*) ((bValue) ? "1" : "0")); } UT_Bool XAP_PrefsScheme::getValue(const XML_Char * szKey, const XML_Char ** pszValue) const { UT_HashEntry * pEntry = m_hash.findEntry((char*)szKey); if (!pEntry) return UT_FALSE; if (pszValue) *pszValue = pEntry->pszRight; return UT_TRUE; } UT_Bool XAP_PrefsScheme::getValueBool(const XML_Char * szKey, UT_Bool * pbValue) const { *pbValue = UT_FALSE; // assume something const XML_Char * szValue = NULL; if (!getValue(szKey,&szValue)) return UT_FALSE; // bogus keyword ?? if (!szValue || !*szValue) return UT_FALSE; // no value for known keyword ?? switch (szValue[0]) { case '1': case 't': case 'T': case 'y': case 'Y': *pbValue = UT_TRUE; return UT_TRUE; default: *pbValue = UT_FALSE; return UT_TRUE; } } UT_Bool XAP_PrefsScheme::getNthValue(UT_uint32 k, const XML_Char ** pszKey, const XML_Char ** pszValue) const { // TODO we should fix hash to use ut_uint32 rather than int if (k >= (UT_uint32)m_hash.getEntryCount()) return UT_FALSE; UT_HashEntry * pEntry = m_hash.getNthEntryAlpha(k); if (!pEntry) return UT_FALSE; if (pszKey) *pszKey = pEntry->pszLeft; if (pszValue) *pszValue = pEntry->pszRight; return UT_TRUE; } /*****************************************************************/ UT_Bool XAP_Prefs::getAutoSavePrefs(void) const { return m_bAutoSavePrefs; } void XAP_Prefs::setAutoSavePrefs(UT_Bool bAuto) { m_bAutoSavePrefs = bAuto; // TODO if turning autosave on, we should do a save now.... // TODO if was on and turning off, should we save it now ?? } /*****************************************************************/ UT_Bool XAP_Prefs::getUseEnvLocale(void) const { return m_bUseEnvLocale; } void XAP_Prefs::setUseEnvLocale(UT_Bool bUse) { m_bUseEnvLocale = bUse; } /*****************************************************************/ UT_uint32 XAP_Prefs::getMaxRecent(void) const { return m_iMaxRecent; } void XAP_Prefs::setMaxRecent(UT_uint32 k) { UT_ASSERT(k<=XAP_PREF_LIMIT_MaxRecent); if (k > XAP_PREF_LIMIT_MaxRecent) k = XAP_PREF_LIMIT_MaxRecent; m_iMaxRecent = k; } UT_uint32 XAP_Prefs::getRecentCount(void) const { return m_vecRecent.getItemCount(); } const char * XAP_Prefs::getRecent(UT_uint32 k) const { // NB: k is one-based UT_ASSERT(k <= m_iMaxRecent); const char * pRecent = NULL; if (k <= m_vecRecent.getItemCount()) { pRecent = (const char *) m_vecRecent.getNthItem(k - 1); } return pRecent; } void XAP_Prefs::addRecent(const char * szRecent) { const char * sz; UT_Bool bFound = UT_FALSE; if (m_iMaxRecent == 0) return; // NOOP // was it already here? for (UT_uint32 i=0; i0); UT_ASSERT(k<=getRecentCount()); char * sz = (char *) m_vecRecent.getNthItem(k-1); FREEP(sz); m_vecRecent.deleteNthItem(k-1); } void XAP_Prefs::_pruneRecent(void) { UT_sint32 i; UT_uint32 count = getRecentCount(); if (m_iMaxRecent == 0) { // nuke the whole thing for (i = (signed) count; i > 0 ; i--) { char * sz = (char *) m_vecRecent.getNthItem(i-1); FREEP(sz); } m_vecRecent.clear(); } else if (count > m_iMaxRecent) { // prune entries past m_iMaxRecent for (i = (signed) count; i > (signed) m_iMaxRecent; i--) removeRecent(i); } } /*****************************************************************/ XAP_Prefs::XAP_Prefs(XAP_App * pApp) : m_ahashChanges( 20 ) { m_pApp = pApp; m_bAutoSavePrefs = atoi(XAP_PREF_DEFAULT_AutoSavePrefs); m_bUseEnvLocale = atoi(XAP_PREF_DEFAULT_UseEnvLocale); m_currentScheme = NULL; m_builtinScheme = NULL; m_iMaxRecent = atoi(XAP_PREF_DEFAULT_MaxRecent); m_bInChangeBlock = UT_FALSE; // NOTE: since constructors cannot report malloc // NOTE: failures (and since it is virtual back // NOTE: to the application), our creator must call // NOTE: loadBuiltinPrefs(). // NOTE: we do not initialize the values in m_parserState // NOTE: since they are only used by the parser, it can // NOTE: initialize them. } XAP_Prefs::~XAP_Prefs(void) { UT_VECTOR_PURGEALL(XAP_PrefsScheme *, m_vecSchemes); UT_VECTOR_FREEALL(char *, m_vecRecent); UT_VECTOR_PURGEALL(tPrefsListenersPair *, m_vecPrefsListeners); } /*****************************************************************/ XAP_PrefsScheme * XAP_Prefs::getNthScheme(UT_uint32 k) const { UT_uint32 kLimit = m_vecSchemes.getItemCount(); if (k < kLimit) return (XAP_PrefsScheme *)m_vecSchemes.getNthItem(k); else return NULL; } XAP_PrefsScheme * XAP_Prefs::getScheme(const XML_Char * szSchemeName) const { UT_uint32 kLimit = m_vecSchemes.getItemCount(); UT_uint32 k; for (k=0; kgetSchemeName()) == 0) return p; } return NULL; } UT_Bool XAP_Prefs::addScheme(XAP_PrefsScheme * pNewScheme) { const XML_Char * szBuiltinSchemeName = getBuiltinSchemeName(); const XML_Char * szThisSchemeName = pNewScheme->getSchemeName(); if (UT_XML_stricmp(szThisSchemeName, szBuiltinSchemeName) == 0) { UT_ASSERT(m_builtinScheme == NULL); m_builtinScheme = pNewScheme; } return (m_vecSchemes.addItem(pNewScheme) == 0); } XAP_PrefsScheme * XAP_Prefs::getCurrentScheme(UT_Bool bCreate) { if (bCreate) { // the builtin scheme is not updatable, // so we may need to create one that is if ( !strcmp(m_currentScheme->getSchemeName(), "_builtin_") ) { const XML_Char new_name[] = "_custom_"; if (setCurrentScheme(new_name)) { // unused _custom_ scheme is lying around, so recycle it UT_ASSERT(UT_TODO); // HYP: reset the current scheme's hash table contents? // ALT: replace the existing scheme with new empty one } else { // we need to create it XAP_PrefsScheme * pNewScheme = new XAP_PrefsScheme(this, new_name); UT_ASSERT(pNewScheme); addScheme(pNewScheme); setCurrentScheme(new_name); } } } return m_currentScheme; } UT_Bool XAP_Prefs::setCurrentScheme(const XML_Char * szSchemeName) { // set the current scheme. // TODO notify the application that the scheme has changed XAP_PrefsScheme * p = getScheme(szSchemeName); if (!p) return UT_FALSE; UT_DEBUGMSG(("Preferences::setCurrentScheme [%s].\n",szSchemeName)); m_currentScheme = p; return UT_TRUE; } /*****************************************************************/ static const XML_Char DEBUG_PREFIX[] = "DeBuG"; // case insensitive static const XML_Char NO_PREF_VALUE[] = ""; UT_Bool XAP_Prefs::getPrefsValue(const XML_Char * szKey, const XML_Char ** pszValue) const { // a convenient routine to get a name/value pair from the current scheme UT_ASSERT(m_currentScheme); if (m_currentScheme->getValue(szKey,pszValue)) return UT_TRUE; if (m_builtinScheme->getValue(szKey,pszValue)) return UT_TRUE; // It is legal for there to be arbitrary preference tags that start with // "Debug", and Abi apps won't choke. The idea is that developers can use // these to selectively trigger development-time behaviors. if (UT_XML_strnicmp(szKey, DEBUG_PREFIX, sizeof(DEBUG_PREFIX) - 1) == 0) { *pszValue = NO_PREF_VALUE; return UT_TRUE; } UT_ASSERT(UT_SHOULD_NOT_HAPPEN); return UT_FALSE; } UT_Bool XAP_Prefs::getPrefsValueBool(const XML_Char * szKey, UT_Bool * pbValue) const { // a convenient routine to get a name/value pair from the current scheme UT_ASSERT(m_currentScheme); if (m_currentScheme->getValueBool(szKey,pbValue)) return UT_TRUE; if (m_builtinScheme->getValueBool(szKey,pbValue)) return UT_TRUE; // It is legal for there to be arbitrary preference tags that start with // "Debug", and Abi apps won't choke. The idea is that developers can use // these to selectively trigger development-time behaviors. if (UT_XML_strnicmp(szKey, DEBUG_PREFIX, sizeof(DEBUG_PREFIX) - 1) == 0) { *pbValue = UT_FALSE; return UT_TRUE; } UT_ASSERT(UT_SHOULD_NOT_HAPPEN); return UT_FALSE; } /***************************************************************** ****************************************************************** ** C-style callback functions that we register with the XML parser ****************************************************************** *****************************************************************/ #ifndef HAVE_LIBXML2 static void startElement(void *userData, const XML_Char *name, const XML_Char **atts) { XAP_Prefs * pPrefs = (XAP_Prefs *)userData; pPrefs->_startElement(name,atts); } static void endElement(void *userData, const XML_Char *name) { XAP_Prefs * pPrefs = (XAP_Prefs *)userData; pPrefs->_endElement(name); } static void charData(void* userData, const XML_Char *s, int len) { XAP_Prefs * pPrefs = (XAP_Prefs *)userData; pPrefs->_charData(s,len); } static void startElement_SystemDefaultFile(void *userData, const XML_Char *name, const XML_Char **atts) { XAP_Prefs * pPrefs = (XAP_Prefs *)userData; pPrefs->_startElement_SystemDefaultFile(name,atts); } #endif /* HAVE_LIBXML2 */ /*****************************************************************/ void XAP_Prefs::_startElement(const XML_Char *name, const XML_Char **atts) { XAP_PrefsScheme * pNewScheme = NULL; // must be freed if (!m_parserState.m_parserStatus) // eat if already had an error return; if (UT_XML_stricmp(name, "AbiPreferences") == 0) { m_parserState.m_bFoundAbiPreferences = UT_TRUE; // we expect something of the form: // ... const XML_Char ** a = atts; while (*a) { UT_ASSERT(a[1] && *a[1]); // require a value for each attribute keyword if (UT_XML_stricmp(a[0], "app") == 0) { // TODO the following test will fail if you are running // TODO both an AbiWord (release) build and an AbiWord // TODO Personal (development/personal) build. That is, // TODO you'll lose your MRU list if you alternate between // TODO the two types of executables. const char * szThisApp = m_pApp->getApplicationName(); UT_DEBUGMSG(("Found preferences for application [%s] (this is [%s]).\n", a[1],szThisApp)); if (UT_XML_stricmp(a[1],szThisApp) != 0) { UT_DEBUGMSG(("Preferences file does not match this application.\n")); goto InvalidFileError; } } else if (UT_XML_stricmp(a[0], "ver") == 0) { // TODO test version number } a += 2; } } else if (UT_XML_stricmp(name, "Select") == 0) { m_parserState.m_bFoundSelect = UT_TRUE; // we expect something of the form: //