diff --git a/stepmania/src/ThemeManager.cpp b/stepmania/src/ThemeManager.cpp index 1ba0829bc5..3b2fbf4725 100644 --- a/stepmania/src/ThemeManager.cpp +++ b/stepmania/src/ThemeManager.cpp @@ -22,7 +22,6 @@ ThemeManager* THEME = NULL; // global object accessable from anywhere in the pro const CString BASE_THEME_NAME = "default"; -const CString FALLBACK_THEME_NAME = "fallback"; const CString LANGUAGES_SUBDIR = "Languages/"; const CString BASE_LANGUAGE = "english"; const CString THEMES_DIR = "Themes/"; @@ -37,6 +36,16 @@ const CString ELEMENT_CATEGORY_STRING[NUM_ELEMENT_CATEGORIES] = "Other" }; + +struct Theme +{ + CString sThemeName; + IniFile iniMetrics; // make this a pointer so we don't have to include IniFile here +}; +// When looking for a metric or an element, search these from head to tail. +deque g_vThemes; + + /* We spend a lot of time doing redundant theme path lookups. Cache results. */ static map g_ThemePathCache[NUM_ELEMENT_CATEGORIES]; @@ -65,13 +74,8 @@ CString ClassAndElementToFileName( const CString &sClassName, const CString &sEl return sClassName + " " + sElement; } - ThemeManager::ThemeManager() { - m_pIniCurMetrics = new IniFile; - m_pIniBaseMetrics = new IniFile; - m_pIniFallbackMetrics = new IniFile; - m_sCurThemeName = BASE_THEME_NAME; // Use the base theme for now. It's up to PrefsManager to change this. CStringArray arrayThemeNames; @@ -80,8 +84,6 @@ ThemeManager::ThemeManager() ThemeManager::~ThemeManager() { - delete m_pIniCurMetrics; - delete m_pIniBaseMetrics; } void ThemeManager::GetThemeNames( CStringArray& AddTo ) @@ -136,6 +138,29 @@ bool ThemeManager::DoesLanguageExist( CString sLanguage ) return false; } +void ThemeManager::LoadThemeRecursive( deque &theme, CString sThemeName ) +{ + static int depth = 0; + depth++; + ASSERT_M( depth < 20, "Circular NoteSkin fallback references detected." ); + + Theme t; + t.sThemeName = sThemeName; + t.iniMetrics.ReadFile( GetMetricsIniPath(sThemeName) ); + t.iniMetrics.ReadFile( GetLanguageIniPath(sThemeName,BASE_LANGUAGE) ); + if( m_sCurLanguage.CompareNoCase(BASE_LANGUAGE) ) + t.iniMetrics.ReadFile( GetLanguageIniPath(sThemeName,m_sCurLanguage) ); + + // read global fallback the current NoteSkin (if any) + CString sFallback; + if( t.iniMetrics.GetValue("Global","FallbackTheme",sFallback) ) + LoadThemeRecursive( theme, sFallback ); + + g_vThemes.push_front( t ); + + depth--; +} + void ThemeManager::SwitchThemeAndLanguage( CString sThemeName, CString sLanguage ) { if( !DoesThemeExist(sThemeName) ) @@ -153,25 +178,16 @@ void ThemeManager::SwitchThemeAndLanguage( CString sThemeName, CString sLanguage for( i = 0; i < NUM_ELEMENT_CATEGORIES; ++i ) g_ThemePathCache[i].clear(); - // read new metrics. First read base metrics, then read cur theme's metrics, overriding base theme - m_pIniBaseMetrics->Reset(); - m_pIniBaseMetrics->ReadFile( GetMetricsIniPath(BASE_THEME_NAME) ); - m_pIniBaseMetrics->ReadFile( GetLanguageIniPath(BASE_THEME_NAME,BASE_LANGUAGE) ); - m_pIniBaseMetrics->ReadFile( GetLanguageIniPath(BASE_THEME_NAME,m_sCurLanguage) ); + g_vThemes.clear(); - m_pIniFallbackMetrics->Reset(); - m_pIniFallbackMetrics->ReadFile( GetMetricsIniPath(FALLBACK_THEME_NAME) ); - m_pIniFallbackMetrics->ReadFile( GetLanguageIniPath(FALLBACK_THEME_NAME,BASE_LANGUAGE) ); - m_pIniFallbackMetrics->ReadFile( GetLanguageIniPath(FALLBACK_THEME_NAME,m_sCurLanguage) ); + // load base theme + LoadThemeRecursive( g_vThemes, BASE_THEME_NAME ); + // load current theme /* Don't bother loading m_pIniCurMetrics if it'll be the same data as BASE_THEME_NAME. */ - m_pIniCurMetrics->Reset(); if( m_sCurThemeName.CompareNoCase(BASE_THEME_NAME) ) - { - m_pIniCurMetrics->ReadFile( GetMetricsIniPath(m_sCurThemeName) ); - m_pIniCurMetrics->ReadFile( GetLanguageIniPath(m_sCurThemeName,BASE_LANGUAGE) ); - m_pIniCurMetrics->ReadFile( GetLanguageIniPath(m_sCurThemeName,m_sCurLanguage) ); - } + LoadThemeRecursive( g_vThemes, m_sCurThemeName ); + CString sMetric; for( i = 0; GetCommandlineArgument( "metric", &sMetric, i ); ++i ) @@ -184,7 +200,7 @@ void ThemeManager::SwitchThemeAndLanguage( CString sThemeName, CString sLanguage if( !re.Compare( sMetric, sBits ) ) RageException::Throw( "Invalid argument \"--metric=%s\"", sMetric.c_str() ); - m_pIniCurMetrics->SetValue( sBits[0], sBits[1], sBits[2] ); + g_vThemes.front().iniMetrics.SetValue( sBits[0], sBits[1], sBits[2] ); } LOG->MapLog("theme", "Theme: %s", sThemeName.c_str()); @@ -379,24 +395,20 @@ CString ThemeManager::GetPath( ElementCategory category, CString sClassName, CSt } try_element_again: - - - // search the current theme - CString ret = GetPathToAndFallback( m_sCurThemeName, category, sClassName, sElement); - if( !ret.empty() ) // we found something - { - Cache[sFileName] = ret; - return ret; - } - - // search the base theme - ret = GetPathToAndFallback( BASE_THEME_NAME, category, sClassName, sElement); - if( !ret.empty() ) // we found something - { - Cache[sFileName] = ret; - return ret; - } + for( deque::const_iterator iter = g_vThemes.begin(); + iter != g_vThemes.end(); + iter++ ) + { + // search the current theme + CString ret = GetPathToAndFallback( iter->sThemeName, category, sClassName, sElement ); + if( !ret.empty() ) // we found something + { + Cache[sFileName] = ret; + return ret; + } + } + if( bOptional ) { Cache[sFileName] = ""; @@ -479,28 +491,17 @@ bool ThemeManager::GetMetricRaw( CString sClassName, CString sValueName, CString CString sFallback; - if( m_pIniCurMetrics->GetValue(sClassName,sValueName,ret) ) - return true; - if( m_pIniCurMetrics->GetValue(sClassName,"Fallback",sFallback) ) + for( deque::const_iterator iter = g_vThemes.begin(); + iter != g_vThemes.end(); + iter++ ) { - if( GetMetricRaw(sFallback,sValueName,ret,level+1) ) - return true; - } - - if( m_pIniBaseMetrics->GetValue(sClassName,sValueName,ret) ) - return true; - if( m_pIniBaseMetrics->GetValue(sClassName,"Fallback",sFallback) ) - { - if( GetMetricRaw(sFallback,sValueName,ret,level+1) ) - return true; - } - - if( m_pIniFallbackMetrics->GetValue(sClassName,sValueName,ret) ) - return true; - if( m_pIniFallbackMetrics->GetValue(sClassName,"Fallback",sFallback) ) - { - if( GetMetricRaw(sFallback,sValueName,ret,level+1) ) + if( iter->iniMetrics.GetValue(sClassName,sValueName,ret) ) return true; + if( iter->iniMetrics.GetValue(sClassName,"Fallback",sFallback) ) + { + if( GetMetricRaw(sFallback,sValueName,ret,level+1) ) + return true; + } } return false; @@ -630,21 +631,14 @@ CString ThemeManager::GetPathToO( CString sFileName, bool bOptional ) { CString void ThemeManager::GetModifierNames( set& AddTo ) { - const IniFile::key *cur = m_pIniCurMetrics->GetKey( "OptionNames" ); - const IniFile::key *base = m_pIniBaseMetrics->GetKey( "OptionNames" ); - const IniFile::key *fallback = m_pIniFallbackMetrics->GetKey( "OptionNames" ); - - if( cur ) + for( deque::const_iterator iter = g_vThemes.begin(); + iter != g_vThemes.end(); + iter++ ) + { + const IniFile::key *cur = iter->iniMetrics.GetKey( "OptionNames" ); for( IniFile::key::const_iterator iter = cur->begin(); iter != cur->end(); ++iter ) AddTo.insert( iter->first ); - - if( base ) - for( IniFile::key::const_iterator iter = base->begin(); iter != base->end(); ++iter ) - AddTo.insert( iter->first ); - - if( fallback ) - for( IniFile::key::const_iterator iter = fallback->begin(); iter != fallback->end(); ++iter ) - AddTo.insert( iter->first ); + } } /* diff --git a/stepmania/src/ThemeManager.h b/stepmania/src/ThemeManager.h index e1a1cb1976..31b07a9695 100644 --- a/stepmania/src/ThemeManager.h +++ b/stepmania/src/ThemeManager.h @@ -6,11 +6,14 @@ #include "RageTypes.h" #include "RageTimer.h" #include +#include class IniFile; enum ElementCategory { BGAnimations, Fonts, Graphics, Numbers, Sounds, Other, NUM_ELEMENT_CATEGORIES }; +struct Theme; + class ThemeManager { public: @@ -56,6 +59,7 @@ public: RageColor GetMetricC( CString sClassName, CString sValueName ); protected: + void LoadThemeRecursive( deque &theme, CString sThemeName ); bool GetMetricRaw( CString sClassName, CString sValueName, CString &ret, int level=0 ); CString GetPathToAndFallback( CString sThemeName, ElementCategory category, CString sClassName, CString sFile ); CString GetPathToRaw( CString sThemeName, ElementCategory category, CString sClassName, CString sFile ); @@ -67,10 +71,6 @@ protected: CString m_sCurThemeName; CString m_sCurLanguage; - - IniFile* m_pIniCurMetrics; // make this a pointer so we don't have to include IniFile here - IniFile* m_pIniBaseMetrics; // make this a pointer so we don't have to include IniFile here - IniFile* m_pIniFallbackMetrics; // make this a pointer so we don't have to include IniFile here }; extern ThemeManager* THEME; // global and accessable from anywhere in our program